dsh-code-index 0.11.0 → 0.11.2
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 +10 -0
- package/README.md +24 -24
- package/README.zh.md +26 -24
- package/dist/index.d.ts +20 -706
- package/package.json +3 -2
- package/scripts/release-smoke.mjs +14 -7
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to dsh-code-index are documented here.
|
|
4
4
|
|
|
5
|
+
## 0.11.2 — DSH compatibility range
|
|
6
|
+
|
|
7
|
+
- Allow DSH versions from `0.2.0-rc.2` onward, including prereleases, without claiming future versions have been verified.
|
|
8
|
+
|
|
9
|
+
## 0.11.1 — DSH rc.3 compatibility
|
|
10
|
+
|
|
11
|
+
- Add the verified exact `@deepseek-ai/dsh-tools@0.2.1-rc.3` host group; retain `0.2.0-rc.2` and `0.2.1-rc.2` support without exemptions or an open-ended range.
|
|
12
|
+
- Verify all eight tools and Impact X-Ray in a real DSH rc.3 Web/Host environment. Keep server/client behavior, runtime dependencies and the development API baseline unchanged.
|
|
13
|
+
- Make rc.3 the default installed-package smoke target and include `code_health` in native runtime dispatch coverage. See `RELEASE_EVIDENCE_v0.11.1.md` for methods and limits.
|
|
14
|
+
|
|
5
15
|
## 0.11.0 — Impact X-Ray
|
|
6
16
|
|
|
7
17
|
- Present the existing `code_change_context` analysis in an interactive bilingual Desktop view with source-backed nodes and relationships; keep default text output and `code_context` Context Packs compatible.
|
package/README.md
CHANGED
|
@@ -5,36 +5,35 @@
|
|
|
5
5
|
|
|
6
6
|
English | [中文](README.zh.md)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
## Impact X-Ray
|
|
9
9
|
|
|
10
|
-
**
|
|
10
|
+
**See the blast radius before your AI edits the code.**
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
A local [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness) plugin that turns a code diff into an inspectable map of potentially affected callers, imports and test leads. Click a relationship to check its source file, line and evidence.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
**For DSH users working with existing code:** inspect likely ripple effects before asking an AI agent to edit. Install the stable **v0.11.1** in a compatible DSH profile:
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
```sh
|
|
17
|
+
dsh plugin --profile desktop add dsh-code-index@0.11.1
|
|
18
|
+
```
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
- **See what may break:** inspect changed declarations, direct callers, indirect paths, module imports and candidate tests.
|
|
20
|
-
- **Inspect the evidence:** select a node or arrow to see its reason, provenance, file, line and actual source snapshot. Expand the view for a graph and evidence side by side; open current files in Desktop's native preview.
|
|
21
|
-
- **Know what remains uncertain:** dotted amber `name-only` candidates, import scope, unmapped ranges, missing source and truncation stay visible. A test lead does not prove coverage or failure.
|
|
20
|
+
Restart Desktop after installation. v0.11.1 declares DSH `0.2.0-rc.2`, `0.2.1-rc.2` and `0.2.1-rc.3` host groups; for Desktop rc.3, the compatibility warning was reported gone, but full Electron Desktop feature and Agent acceptance is not claimed. [Install details](#install) · [v0.11.1 verification and limits](RELEASE_EVIDENCE_v0.11.1.md).
|
|
22
21
|
|
|
23
|
-
|
|
22
|
+
**Watch the 30-second real Desktop demo:** a change to `normalize()` → inspect callers and a test lead → click a relationship to see the actual source location.
|
|
24
23
|
|
|
25
|
-

|
|
24
|
+

|
|
26
25
|
|
|
27
|
-
[
|
|
26
|
+
[Watch MP4](assets/impact-xray-mvp.mp4) · [Full-size GIF](assets/impact-xray-mvp.gif) · [Demo notes and reproduction](assets/impact-xray-mvp-demo.md) · [Install v0.11.1 from npm](https://www.npmjs.com/package/dsh-code-index/v/0.11.1) · [Project](https://github.com/lemonxiny55/dsh-code-index)
|
|
28
27
|
|
|
29
|
-
The recording
|
|
28
|
+
The recording is an actual DSH Desktop interaction with the `0.11.0-dev.0` MVP in Desktop 0.2.1-rc.2. It shows existing analysis and source evidence; it is not a model-driven Agent latency or success claim. See [recording provenance](assets/impact-xray-mvp-demo.md).
|
|
30
29
|
|
|
31
|
-
|
|
30
|
+
- Start from a Git diff or an explicitly selected file/symbol; inspect callers, import dependents, paths and candidate tests.
|
|
31
|
+
- Click a relationship to inspect its provenance and available source snapshot at a file and line.
|
|
32
|
+
- Treat candidates as leads: `name-only` matches can be uncertain, and a test lead does not prove coverage or failure.
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
dsh plugin --profile desktop add dsh-code-index@0.11.0
|
|
35
|
-
```
|
|
34
|
+
Impact X-Ray is an interactive view of the existing `code_change_context` analysis and source reader; it does not add an impact-analysis engine. Relationships are static evidence, not guaranteed runtime impact.
|
|
36
35
|
|
|
37
|
-
|
|
36
|
+
**Verified:** full X-Ray UI in Desktop 0.2.1-rc.2; Web/Host rc.3 verified for all eight tools and X-Ray (no model-driven Agent run). Desktop 0.2.0-rc.2 passed installation and ToolRuntime checks, not full X-Ray UI. Keep each host's first-party packages on the same declared version. [v0.11.1 evidence and limits](RELEASE_EVIDENCE_v0.11.1.md).
|
|
38
37
|
|
|
39
38
|
Ask the Agent to call `code_change_context` with:
|
|
40
39
|
|
|
@@ -88,16 +87,16 @@ This supports syntax and static code context, without full HarmonyOS SDK semanti
|
|
|
88
87
|
|
|
89
88
|
## Quick start
|
|
90
89
|
|
|
91
|
-
Requires Node 22/24
|
|
90
|
+
Requires Node 22/24. The v0.11.2 candidate allows DSH `>=0.2.0-rc.2`, including prereleases; matching first-party packages should be used. Later versions may attempt to load but have not all been verified. Install into an existing Desktop profile using its bundled `dsh` CLI:
|
|
92
91
|
|
|
93
92
|
```sh
|
|
94
|
-
dsh plugin --profile desktop add dsh-code-index@0.11.
|
|
93
|
+
dsh plugin --profile desktop add dsh-code-index@0.11.1
|
|
95
94
|
```
|
|
96
95
|
|
|
97
96
|
Restart Desktop after installation. For a matching Web profile:
|
|
98
97
|
|
|
99
98
|
```sh
|
|
100
|
-
npx @deepseek-ai/dsh@0.2.1-rc.2 plugin --profile web add dsh-code-index@0.11.
|
|
99
|
+
npx @deepseek-ai/dsh@0.2.1-rc.2 plugin --profile web add dsh-code-index@0.11.1
|
|
101
100
|
npx @deepseek-ai/dsh@0.2.1-rc.2 web
|
|
102
101
|
```
|
|
103
102
|
|
|
@@ -149,13 +148,13 @@ Plus an optional **auto-injected system prompt section** (`code-index:repo-map`,
|
|
|
149
148
|
|
|
150
149
|
Requires `dsh` (any install path — npx, npm, or source) and Node ≥ 22.
|
|
151
150
|
|
|
152
|
-
The
|
|
151
|
+
The v0.11.2 candidate declares `@deepseek-ai/dsh-tools: >=0.2.0-rc.2`, with no upper bound. DSH includes prereleases when checking this range, so future DSH versions may attempt to load the plugin; this does not mean every version has been verified. Use matching DSH/dsh-tools groups. The plugin surface remains a preview API, and future releases may introduce incompatibilities.
|
|
153
152
|
|
|
154
|
-
Desktop checks DSH peer requirements before activation. v0.10.0's exact `0.2.0-rc.2` pin is rejected by DSH `0.2.1-rc.2`; v0.10.1
|
|
153
|
+
Desktop checks DSH peer requirements before activation. v0.10.0's exact `0.2.0-rc.2` pin is rejected by DSH `0.2.1-rc.2`; v0.10.1 enumerated the then-supported groups; v0.11.2 removes that enumeration. Its development dependency stays on `0.2.0-rc.2` for backward API checks.
|
|
155
154
|
|
|
156
155
|
```sh
|
|
157
156
|
# from npm (prebuilt)
|
|
158
|
-
npx @deepseek-ai/dsh@0.2.0-rc.2 plugin --profile web add dsh-code-index@0.11.
|
|
157
|
+
npx @deepseek-ai/dsh@0.2.0-rc.2 plugin --profile web add dsh-code-index@0.11.1
|
|
159
158
|
|
|
160
159
|
# or from a directory containing this checkout
|
|
161
160
|
npx @deepseek-ai/dsh@0.2.0-rc.2 plugin --profile web add ./dsh-code-index
|
|
@@ -323,3 +322,4 @@ To make a report actionable, include the approximate repository size and languag
|
|
|
323
322
|
|
|
324
323
|
MIT. Not affiliated with DeepSeek; built on the public `dsh` plugin surface.
|
|
325
324
|
|
|
325
|
+
|
package/README.zh.md
CHANGED
|
@@ -2,36 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
[English](README.md) | 中文
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Impact X-Ray
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**See the blast radius before your AI edits the code.**
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
**在 AI 修改代码前,看清潜在影响范围。**
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
这是 [DeepSeek Harness(DSH)](https://github.com/deepseek-ai/deepseek-harness) 的本地插件:把代码 diff 呈现为一张可检查的潜在影响图,列出可能受影响的调用者、导入和测试线索。点击关系即可核对源码文件、行号与证据。
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
**适合正在维护现有代码的 DSH 用户:** 在让 AI Agent 修改代码前,先查看可能的连带影响。使用正式稳定版 **v0.11.1**,安装到兼容的 DSH profile:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
dsh plugin --profile desktop add dsh-code-index@0.11.1
|
|
17
|
+
```
|
|
14
18
|
|
|
15
|
-
-
|
|
16
|
-
- **看清可能影响什么:**检查变更声明、直接调用者、间接路径、模块导入和测试候选。
|
|
17
|
-
- **检查真实证据:**点击节点或箭头,查看纳入原因、关系来源、文件行号及实际源码快照。放大视图中图与证据并排展示,可打开 Desktop 原生文件预览。
|
|
18
|
-
- **了解不确定性:**黄色点线的 `name-only` 候选、导入范围、未映射行、缺失源码和裁剪均明确呈现;测试线索不证明覆盖率或必然失败。
|
|
19
|
+
安装后重启 Desktop。v0.11.1 声明支持 DSH `0.2.0-rc.2`、`0.2.1-rc.2` 和 `0.2.1-rc.3` 宿主版本组;Desktop rc.3 的兼容性提示据反馈已消失,但这不代表 Electron Desktop 全部功能或 Agent 验收通过。[安装说明](#安装) · [v0.11.1 验证与限制](RELEASE_EVIDENCE_v0.11.1.md)。
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
**观看 30 秒真实 Desktop 演示:**`normalize()` 被修改 → 查看调用者与测试线索 → 点击关系查看真实源码位置。
|
|
21
22
|
|
|
22
|
-

|
|
23
|
+

|
|
23
24
|
|
|
24
|
-
[
|
|
25
|
+
[观看 MP4](assets/impact-xray-mvp.mp4) · [全尺寸 GIF](assets/impact-xray-mvp.gif) · [演示说明与复现](assets/impact-xray-mvp-demo.md) · [从 npm 安装 v0.11.1](https://www.npmjs.com/package/dsh-code-index/v/0.11.1) · [项目主页](https://github.com/lemonxiny55/dsh-code-index)
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
录屏来自真实 DSH Desktop 0.2.1-rc.2 中的 `0.11.0-dev.0` MVP 操作,展示的是既有分析与源码证据;它不代表模型驱动 Agent 的耗时或成功率。详见[录制来源](assets/impact-xray-mvp-demo.md)。
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
- 从 Git diff 或明确选定的文件/符号出发,查看调用者、导入依赖、影响路径和测试候选。
|
|
30
|
+
- 点击关系,查看其来源,以及可用的文件行号和源码快照。
|
|
31
|
+
- 把候选视为线索:`name-only` 匹配可能不确定;测试线索不证明覆盖率或测试失败。
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
dsh plugin --profile desktop add dsh-code-index@0.11.0
|
|
32
|
-
```
|
|
33
|
+
Impact X-Ray 是既有 `code_change_context` 分析及源码读取能力的交互展示,并未新增影响分析引擎。关系属于静态证据,不保证运行时必然受影响。
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
**验证范围:** Desktop 0.2.1-rc.2 完成完整 X-Ray UI 验证;Web/Host rc.3 完成全部八个工具及 X-Ray 验证(未运行模型驱动 Agent);Desktop 0.2.0-rc.2 通过安装和 ToolRuntime 检查,未验证完整 X-Ray UI。宿主中的第一方包须使用同一已声明版本。[v0.11.1 验证与限制](RELEASE_EVIDENCE_v0.11.1.md)。
|
|
35
36
|
|
|
36
37
|
让 Agent 调用 `code_change_context`:
|
|
37
38
|
|
|
@@ -85,16 +86,16 @@ node scripts/impact-xray-demo.mjs D:/absolute/path/to/new-demo-repo
|
|
|
85
86
|
|
|
86
87
|
## 快速开始
|
|
87
88
|
|
|
88
|
-
需要 Node 22/24
|
|
89
|
+
需要 Node 22/24。v0.11.2 候选版本允许 DSH `>=0.2.0-rc.2`(包括预发布版本),第一方包应使用配套版本。后续版本可以尝试加载,但并不代表全部经过验证。用 Desktop 自带的 `dsh` CLI 安装到现有 profile:
|
|
89
90
|
|
|
90
91
|
```sh
|
|
91
|
-
dsh plugin --profile desktop add dsh-code-index@0.11.
|
|
92
|
+
dsh plugin --profile desktop add dsh-code-index@0.11.1
|
|
92
93
|
```
|
|
93
94
|
|
|
94
95
|
安装后重启 Desktop。匹配的 Web profile 可使用:
|
|
95
96
|
|
|
96
97
|
```sh
|
|
97
|
-
npx @deepseek-ai/dsh@0.2.1-rc.2 plugin --profile web add dsh-code-index@0.11.
|
|
98
|
+
npx @deepseek-ai/dsh@0.2.1-rc.2 plugin --profile web add dsh-code-index@0.11.1
|
|
98
99
|
npx @deepseek-ai/dsh@0.2.1-rc.2 web
|
|
99
100
|
```
|
|
100
101
|
|
|
@@ -146,13 +147,13 @@ npx @deepseek-ai/dsh@0.2.1-rc.2 web
|
|
|
146
147
|
|
|
147
148
|
需要 `dsh`(任意安装方式——npx、npm 或源码)与 Node ≥ 22。
|
|
148
149
|
|
|
149
|
-
v0.
|
|
150
|
+
v0.11.2 候选版本声明 `@deepseek-ai/dsh-tools: >=0.2.0-rc.2`,不设置上限。DSH 检查范围时包含预发布版本,因此未来版本可以尝试加载插件,但这不代表每个版本都经过验证。请使用配套的 DSH/dsh-tools 版本组;插件接口仍是预览 API,未来版本可能产生兼容问题。
|
|
150
151
|
|
|
151
|
-
Desktop 在激活前检查 DSH peer 约束。v0.10.0 的精确 `0.2.0-rc.2` pin 会被 DSH `0.2.1-rc.2` 拒绝;v0.10.1
|
|
152
|
+
Desktop 在激活前检查 DSH peer 约束。v0.10.0 的精确 `0.2.0-rc.2` pin 会被 DSH `0.2.1-rc.2` 拒绝;v0.10.1 列举了当时支持的版本组;v0.11.2 移除这一枚举。开发依赖保留 `0.2.0-rc.2`,继续检查旧版 API。
|
|
152
153
|
|
|
153
154
|
```sh
|
|
154
155
|
# 从 npm(预编译)
|
|
155
|
-
npx @deepseek-ai/dsh@0.2.0-rc.2 plugin --profile web add dsh-code-index@0.11.
|
|
156
|
+
npx @deepseek-ai/dsh@0.2.0-rc.2 plugin --profile web add dsh-code-index@0.11.1
|
|
156
157
|
|
|
157
158
|
# 或从包含本仓库检查副本的目录
|
|
158
159
|
npx @deepseek-ai/dsh@0.2.0-rc.2 plugin --profile web add ./dsh-code-index
|
|
@@ -312,3 +313,4 @@ node.exe scripts\fix-wsl-links.mjs C:\Users\you\.dsh\profiles\web # dsh profil
|
|
|
312
313
|
|
|
313
314
|
MIT。与 DeepSeek 无关;基于公开的 `dsh` 插件接口构建。
|
|
314
315
|
|
|
316
|
+
|
package/dist/index.d.ts
CHANGED
|
@@ -1,708 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
decorators?: string[];
|
|
21
|
-
/** Stable qualified id; unique within a RepoIndex. */
|
|
22
|
-
id: SymbolId;
|
|
23
|
-
/** Symbol name (identifier / type id / member name). */
|
|
24
|
-
name: string;
|
|
25
|
-
kind: SymbolKind;
|
|
26
|
-
/** Repo-relative path of the containing file, always forward-slash. */
|
|
27
|
-
file: string;
|
|
28
|
-
/** 1-based start line. */
|
|
29
|
-
line: number;
|
|
30
|
-
/** 1-based end line (inclusive). */
|
|
31
|
-
endLine: number;
|
|
32
|
-
/** Normalized public/export status by language; Rust: unrestricted `pub` only. */
|
|
33
|
-
exported: boolean;
|
|
34
|
-
/** Short human-readable signature, e.g. `greet(name: string)` — empty when n/a. */
|
|
35
|
-
signature: string;
|
|
36
|
-
/** Owners only; excludes the symbol itself. Empty at module level. */
|
|
37
|
-
scope: SymbolScopePart[];
|
|
38
|
-
/** 1-based among symbols with the same scope, kind, and name. */
|
|
39
|
-
ordinal: number;
|
|
40
|
-
}
|
|
41
|
-
/** One name bound by an import statement, with its local alias. */
|
|
42
|
-
interface ImportBinding {
|
|
43
|
-
/** Imported name: "foo" | "default" | "*" | null (namespace/package). */
|
|
44
|
-
imported: string | null;
|
|
45
|
-
/** Name visible in this file (alias when renamed). */
|
|
46
|
-
local: string;
|
|
47
|
-
kind: 'named' | 'default' | 'namespace' | 'package' | 'static' | 'glob';
|
|
48
|
-
}
|
|
49
|
-
/** Structured form of one import/include/use site. */
|
|
50
|
-
interface ImportInfo {
|
|
51
|
-
specifier: string;
|
|
52
|
-
line: number;
|
|
53
|
-
kind: 'import' | 'reexport' | 'include' | 'use';
|
|
54
|
-
bindings: ImportBinding[];
|
|
55
|
-
}
|
|
56
|
-
/** One call site: callee name, line, and enclosing function ('' = module level). */
|
|
57
|
-
interface CallInfo {
|
|
58
|
-
/** ArkUI syntax requires an explicit/local binding; no global name guessing. */
|
|
59
|
-
context?: 'arkui';
|
|
60
|
-
/** Callee name as written (identifier, member/property, or qualified tail). */
|
|
61
|
-
name: string;
|
|
62
|
-
/** 1-based line of the call expression. */
|
|
63
|
-
line: number;
|
|
64
|
-
/** Enclosing function/method name, or '' for a module-level call. */
|
|
65
|
-
from: string;
|
|
66
|
-
/** Structural owner from AST containment; null at module level. */
|
|
67
|
-
fromId: SymbolId | null;
|
|
68
|
-
/** Receiver/namespace text when available: `ns.foo()` -> "ns"; bare -> null. */
|
|
69
|
-
qualifier: string | null;
|
|
70
|
-
}
|
|
71
|
-
interface IndexedFile {
|
|
72
|
-
/** Syntax/read completeness, not a typecheck or HarmonyOS runtime verdict. */
|
|
73
|
-
diagnostic?: ParseDiagnostic;
|
|
74
|
-
/** Repo-relative path (forward-slash). */
|
|
75
|
-
path: string;
|
|
76
|
-
lang: string;
|
|
77
|
-
/** fs mtime of the source file at index time (milliseconds). */
|
|
78
|
-
mtimeMs: number;
|
|
79
|
-
symbols: SymbolInfo[];
|
|
80
|
-
/** Derived compatibility view of {@link importDetails} specifiers. */
|
|
81
|
-
imports?: string[];
|
|
82
|
-
/** Structured import/include/use sites with their name bindings. */
|
|
83
|
-
importDetails?: ImportInfo[];
|
|
84
|
-
/** Call sites found in this file (callee + line + enclosing function).
|
|
85
|
-
* Optional because caches written before call-graph support lack it. */
|
|
86
|
-
calls?: CallInfo[];
|
|
87
|
-
}
|
|
88
|
-
interface ParseDiagnostic {
|
|
89
|
-
status: 'parsed' | 'partial' | 'failed';
|
|
90
|
-
errorCount: number;
|
|
91
|
-
missingCount: number;
|
|
92
|
-
issues: Array<{
|
|
93
|
-
kind: 'ERROR' | 'MISSING';
|
|
94
|
-
line: number;
|
|
95
|
-
}>;
|
|
96
|
-
reason?: 'source-unavailable' | 'parser-failed';
|
|
97
|
-
}
|
|
98
|
-
interface RepoIndex {
|
|
99
|
-
/** Persisted layout version; see {@link REPO_INDEX_SCHEMA_VERSION}. */
|
|
100
|
-
schemaVersion: typeof REPO_INDEX_SCHEMA_VERSION;
|
|
101
|
-
/** Absolute source root this index describes. */
|
|
102
|
-
root: string;
|
|
103
|
-
/** Unix ms when the index was generated. */
|
|
104
|
-
generatedAt: number;
|
|
105
|
-
files: IndexedFile[];
|
|
106
|
-
excludedDirs: string[];
|
|
107
|
-
}
|
|
108
|
-
declare function symbolCount(index: RepoIndex): number;
|
|
109
|
-
/** Optional config consumed by the index build pipeline. */
|
|
110
|
-
interface IndexOptions {
|
|
111
|
-
/** Extra directories to exclude, appended to the defaults. */
|
|
112
|
-
excludeDirs?: string[];
|
|
113
|
-
/** Minimum mtime delta (ms) that forces a re-extract in refresh mode. */
|
|
114
|
-
staleToleranceMs?: number;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/** Build a RepoIndex for a workspace: scan -> extract -> persist. */
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Full build: scan + extract every supported file. Existing per-file
|
|
121
|
-
* extraction is reused when `previous` holds an identical mtime for the
|
|
122
|
-
* same rel path (incremental refresh), so touched files alone re-parse.
|
|
123
|
-
*/
|
|
124
|
-
declare function buildIndex(root: string, options?: IndexOptions, previous?: RepoIndex | null, forcePaths?: ReadonlySet<string>): Promise<RepoIndex>;
|
|
125
|
-
/** Convenience wrapper: evolve an on-disk cache if `root` exists. */
|
|
126
|
-
declare function buildIndexWithCache(root: string, options?: IndexOptions, cacheDir?: string, forcePaths?: ReadonlySet<string>): Promise<RepoIndex>;
|
|
127
|
-
/**
|
|
128
|
-
* Locate the git repo root for a path by walking up to the nearest `.git`
|
|
129
|
-
* directory. Bounded walk (max `maxLevels` levels); returns `null` when no
|
|
130
|
-
* repo marker is found — callers must NOT index an untagged directory
|
|
131
|
-
* (the fs root is the classic footgun: indexing `C:\` by accident).
|
|
132
|
-
*/
|
|
133
|
-
declare function findRepoRoot(startDir: string, maxLevels?: number): Promise<string | null>;
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* tree-sitter symbol extraction.
|
|
137
|
-
*
|
|
138
|
-
* Parses a source text with a per-language grammar (web-tree-sitter WASM,
|
|
139
|
-
* static grammars from tree-sitter-wasms) and returns flat SymbolInfo rows.
|
|
140
|
-
*
|
|
141
|
-
* web-tree-sitter 0.25 loads the existing tree-sitter-wasms grammars and the
|
|
142
|
-
* pinned ArkTS ABI-14 asset. See grammars/README.md for provenance.
|
|
143
|
-
*/
|
|
144
|
-
|
|
145
|
-
type LanguageId = 'arkts' | 'typescript' | 'javascript' | 'python' | 'go' | 'rust' | 'java' | 'cpp' | 'c';
|
|
146
|
-
declare function languageForFile(filePath: string): LanguageId | null;
|
|
147
|
-
interface ExtractedFile {
|
|
148
|
-
diagnostic: ParseDiagnostic;
|
|
149
|
-
symbols: SymbolInfo[];
|
|
150
|
-
/** Derived compatibility view of {@link importDetails} specifiers. */
|
|
151
|
-
imports: string[];
|
|
152
|
-
/** Structured import/include/use sites with name bindings. */
|
|
153
|
-
importDetails: ImportInfo[];
|
|
154
|
-
/** Call sites, ordered by line: callee name + enclosing function. */
|
|
155
|
-
calls: CallInfo[];
|
|
156
|
-
}
|
|
157
|
-
declare function extractAll(code: string, id: LanguageId, file?: string): Promise<ExtractedFile>;
|
|
158
|
-
/** Symbols only — see extractAll for the combined parse. */
|
|
159
|
-
declare function extractSymbols(code: string, id: LanguageId): Promise<SymbolInfo[]>;
|
|
160
|
-
/** Compile a symbol query for a code sample (used by tests). */
|
|
161
|
-
declare function parseFileToSymbols(filePath: string, repoRoot: string, code?: string): Promise<SymbolInfo[]>;
|
|
162
|
-
|
|
163
|
-
/** Workspace file discovery with dir exclusions and mtime tracking. */
|
|
164
|
-
|
|
165
|
-
declare const DEFAULT_EXCLUDED_DIRS: string[];
|
|
166
|
-
/** Language-agnostic source extensions we index. */
|
|
167
|
-
declare const SUPPORTED_EXTS: Set<string>;
|
|
168
|
-
interface ScannedFile {
|
|
169
|
-
/** Absolute path on disk. */
|
|
170
|
-
abs: string;
|
|
171
|
-
/** Repo-relative path (forward-slash). */
|
|
172
|
-
rel: string;
|
|
173
|
-
mtimeMs: number;
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Recursively walk `root` and return indexed files, skipping excluded dir
|
|
177
|
-
* components at any depth. Uses readdir with `withFileTypes` so we never
|
|
178
|
-
* stat every entry; mtime comes from a targeted stat per candidate file.
|
|
179
|
-
*/
|
|
180
|
-
declare function scanRepo(root: string, options?: IndexOptions): Promise<ScannedFile[]>;
|
|
181
|
-
|
|
182
|
-
/** On-disk JSON cache for RepoIndex, keyed by repo root. */
|
|
183
|
-
|
|
184
|
-
declare const CACHE_DIR_NAME = ".dsh-code-index";
|
|
185
|
-
/** Default cache location: `<repoRoot>/.dsh-code-index/index.json`. */
|
|
186
|
-
declare function defaultCachePath(root: string, cacheDir?: string): string;
|
|
187
|
-
declare function loadIndex(cachePath: string): Promise<RepoIndex | null>;
|
|
188
|
-
declare function saveIndex(cachePath: string, index: RepoIndex): Promise<void>;
|
|
189
|
-
|
|
190
|
-
/** One summary and at most three file locations. No source or exception dump. */
|
|
191
|
-
declare function indexIntegrityWarnings(index: RepoIndex): string[];
|
|
192
|
-
|
|
193
|
-
/** Pure search / filter logic over a RepoIndex — unit-testable, no IO. */
|
|
194
|
-
|
|
195
|
-
interface SymbolFilter {
|
|
196
|
-
query?: string;
|
|
197
|
-
file?: string;
|
|
198
|
-
kind?: SymbolKind;
|
|
199
|
-
exportedOnly?: boolean;
|
|
200
|
-
}
|
|
201
|
-
interface RankedHit extends SymbolInfo {
|
|
202
|
-
/** 0..1 relevance. 1 = exact name match; lower = fuzzier. */
|
|
203
|
-
score: number;
|
|
204
|
-
}
|
|
205
|
-
/** Filter symbols by file/kind/export, then score by name against query (if any). */
|
|
206
|
-
declare function searchSymbols(index: RepoIndex, filter: SymbolFilter, limit?: number, refs?: Map<string, number>): RankedHit[];
|
|
207
|
-
/** Neat one-line rendering of a hit for terminal/chat output. */
|
|
208
|
-
declare function renderHit(hit: RankedHit): string;
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Bounded, ranked repo map generation — the "aider-style" overview the model
|
|
212
|
-
* can turn to before browsing files. Pure logic, no IO.
|
|
213
|
-
*/
|
|
214
|
-
|
|
215
|
-
interface RepoMapOptions {
|
|
216
|
-
/** Max files to include (default 24). */
|
|
217
|
-
topFiles?: number;
|
|
218
|
-
/** Max symbols per file (default 18). */
|
|
219
|
-
symbolsPerFile?: number;
|
|
220
|
-
/** Hard cap on rendered characters (default 3200). */
|
|
221
|
-
maxChars?: number;
|
|
222
|
-
}
|
|
223
|
-
interface RepoMapEntry {
|
|
224
|
-
path: string;
|
|
225
|
-
score: number;
|
|
226
|
-
symbols: Array<{
|
|
227
|
-
name: string;
|
|
228
|
-
kind: SymbolKind;
|
|
229
|
-
line: number;
|
|
230
|
-
signature: string;
|
|
231
|
-
}>;
|
|
232
|
-
}
|
|
233
|
-
/** Density-aware file score: weighted symbols minus bloat; test paths damped. */
|
|
234
|
-
declare function scoreFile(file: IndexedFile): number;
|
|
235
|
-
/** Rank files, take the top slice, cap per-file symbols. */
|
|
236
|
-
declare function rankRepoMap(index: RepoIndex, options?: RepoMapOptions): RepoMapEntry[];
|
|
237
|
-
/** Render the ranked map as markdown, hard-truncated to maxChars. */
|
|
238
|
-
declare function renderRepoMap(entries: RepoMapEntry[], options?: RepoMapOptions): string;
|
|
239
|
-
|
|
240
|
-
/** Function-level call graph built from the cached index — pure, no IO. */
|
|
241
|
-
|
|
242
|
-
/** name -> definitions; several when same-named symbols live in different files. */
|
|
243
|
-
declare function buildSymbolTable(index: RepoIndex): Map<string, SymbolInfo[]>;
|
|
244
|
-
/** One call site that invokes the queried symbol. */
|
|
245
|
-
interface CallerHit {
|
|
246
|
-
file: string;
|
|
247
|
-
line: number;
|
|
248
|
-
from: string;
|
|
249
|
-
}
|
|
250
|
-
/** One distinct symbol the queried function calls, with its first call line. */
|
|
251
|
-
interface CalleeHit {
|
|
252
|
-
name: string;
|
|
253
|
-
line: number;
|
|
254
|
-
/** In-repo definitions matched by the callee name. */
|
|
255
|
-
targets: SymbolInfo[];
|
|
256
|
-
}
|
|
257
|
-
interface SymbolRefs {
|
|
258
|
-
/** Definitions of the queried symbol (empty when only call sites exist). */
|
|
259
|
-
defs: SymbolInfo[];
|
|
260
|
-
callers: CallerHit[];
|
|
261
|
-
callees: CalleeHit[];
|
|
262
|
-
}
|
|
263
|
-
/** How many call sites reference each name — used to rank search hits. */
|
|
264
|
-
declare function callerCounts(index: RepoIndex): Map<string, number>;
|
|
265
|
-
/**
|
|
266
|
-
* Resolve one symbol to its callers (call sites invoking it anywhere) and its
|
|
267
|
-
* callees (calls made inside the definition, attributed by the extracted
|
|
268
|
-
* enclosing-function name). Name-based, so it is a best-effort view: like-named
|
|
269
|
-
* symbols are reported as candidates rather than guessed apart.
|
|
270
|
-
*/
|
|
271
|
-
declare function symbolRefs(index: RepoIndex, symbol: string, limit?: number): SymbolRefs;
|
|
272
|
-
/**
|
|
273
|
-
* How sure the graph is about one resolved edge. Deliberately coarse: the
|
|
274
|
-
* index has no type checker, so `exact` only ever means "one structurally
|
|
275
|
-
* unambiguous target under this index's model".
|
|
276
|
-
*/
|
|
277
|
-
type ResolutionLabel = 'exact' | 'import-scoped' | 'name-only';
|
|
278
|
-
/** Which resolution tier produced an edge — the *why* behind its label. */
|
|
279
|
-
type ResolutionProvenance = 'structural-owner' | 'local-unique' | 'explicit-import-binding' | 'namespace-import' | 'resolved-import-module' | 'global-name';
|
|
280
|
-
|
|
281
|
-
/** Import-graph health checks: circular dependencies and orphan modules. */
|
|
282
|
-
|
|
283
|
-
interface ModuleGraph {
|
|
284
|
-
nodes: string[];
|
|
285
|
-
edges: Map<string, Set<string>>;
|
|
286
|
-
}
|
|
287
|
-
/** Nodes are indexed files; an edge A -> B means A imports B (self-edges dropped). */
|
|
288
|
-
declare function buildModuleGraph(index: RepoIndex): ModuleGraph;
|
|
289
|
-
/** Tarjan strongly-connected components of size > 1 — i.e. import cycles. */
|
|
290
|
-
declare function findCycles(index: RepoIndex): string[][];
|
|
291
|
-
/**
|
|
292
|
-
* Symbol-bearing files that neither import nor are imported by any other file,
|
|
293
|
-
* excluding conventional entry points and tests (which are legitimately
|
|
294
|
-
* unreferenced). A cheap, low-false-positive stand-in for dead-code detection.
|
|
295
|
-
*/
|
|
296
|
-
declare function findOrphanModules(index: RepoIndex): string[];
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Unified-diff parsing and a thin `git` adapter.
|
|
300
|
-
*
|
|
301
|
-
* `parseUnifiedDiff` is pure (no IO) so it is unit-testable in isolation; the
|
|
302
|
-
* two readers shell out to `git` exclusively through `execFile` with an argv
|
|
303
|
-
* array — a ref or path is never interpolated into a shell string.
|
|
304
|
-
*/
|
|
305
|
-
/** One `@@` hunk header; a missing count means 1 (git omits `,1`). */
|
|
306
|
-
interface DiffHunk {
|
|
307
|
-
oldStart: number;
|
|
308
|
-
oldLines: number;
|
|
309
|
-
newStart: number;
|
|
310
|
-
newLines: number;
|
|
311
|
-
}
|
|
312
|
-
/** One file's entry in a unified diff. */
|
|
313
|
-
interface FileChange {
|
|
314
|
-
/** Repo-relative path on the base side, or null for an added file. */
|
|
315
|
-
oldPath: string | null;
|
|
316
|
-
/** Repo-relative path on the current side, or null for a deleted file. */
|
|
317
|
-
newPath: string | null;
|
|
318
|
-
status: 'added' | 'modified' | 'deleted' | 'renamed';
|
|
319
|
-
hunks: DiffHunk[];
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* Parse `git diff` / `diff -u` output into per-file changes. Handles
|
|
323
|
-
* `diff --git`, `new file mode`, `deleted file mode`, `rename from/to`,
|
|
324
|
-
* `---`/`+++` (including `/dev/null`), and `@@` headers with omitted counts.
|
|
325
|
-
*/
|
|
326
|
-
declare function parseUnifiedDiff(text: string): FileChange[];
|
|
327
|
-
/**
|
|
328
|
-
* Working-tree diff against `baseRef`, rename-aware and hunk-minimal.
|
|
329
|
-
* Includes non-ignored untracked files and reliable exact-content renames.
|
|
330
|
-
*/
|
|
331
|
-
declare function readWorkingTreeDiff(root: string, baseRef: string): Promise<string>;
|
|
332
|
-
/** Read one repo-relative file at a ref; null when the path/ref is absent. */
|
|
333
|
-
declare function readGitFileAtRef(root: string, ref: string, repoRelativePath: string): Promise<string | null>;
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* Change-aware context (design §3): map a git/working-tree diff (or an explicit
|
|
337
|
-
* file/symbol selection) onto indexed symbols, then walk the provenance-labeled
|
|
338
|
-
* reference graph to surface callers, import dependents, transitive impact,
|
|
339
|
-
* entry-point paths, and likely affected tests.
|
|
340
|
-
*
|
|
341
|
-
* Every relation keeps the reference graph's honest label (`exact` /
|
|
342
|
-
* `import-scoped` / `name-only`); a diff hunk intersecting a symbol's declared
|
|
343
|
-
* line range is the only thing treated as `exact`.
|
|
344
|
-
*/
|
|
345
|
-
|
|
346
|
-
type ChangeKind = 'added' | 'modified' | 'deleted' | 'renamed' | 'unclassified';
|
|
347
|
-
interface ChangedSymbol {
|
|
348
|
-
symbol: SymbolInfo;
|
|
349
|
-
change: ChangeKind;
|
|
350
|
-
side: 'current' | 'base';
|
|
351
|
-
/** A hunk range intersecting the declaration is structural, not inferred. */
|
|
352
|
-
resolution: 'exact';
|
|
353
|
-
/** First intersecting changed line, used for large-declaration windows. */
|
|
354
|
-
focusLine?: number;
|
|
355
|
-
}
|
|
356
|
-
interface UnmappedChange {
|
|
357
|
-
file: string;
|
|
358
|
-
side: 'current' | 'base';
|
|
359
|
-
ranges: Array<{
|
|
360
|
-
startLine: number;
|
|
361
|
-
endLine: number;
|
|
362
|
-
}>;
|
|
363
|
-
reason: 'outside-symbol' | 'base-content-unavailable';
|
|
364
|
-
}
|
|
365
|
-
interface ImpactHop {
|
|
366
|
-
fromId: SymbolId | null;
|
|
367
|
-
toId: SymbolId;
|
|
368
|
-
callSite: {
|
|
369
|
-
file: string;
|
|
370
|
-
line: number;
|
|
371
|
-
};
|
|
372
|
-
resolution: ResolutionLabel;
|
|
373
|
-
reason: ResolutionProvenance;
|
|
374
|
-
}
|
|
375
|
-
interface ImpactRow {
|
|
376
|
-
symbol: SymbolInfo;
|
|
377
|
-
depth: number;
|
|
378
|
-
via: ImpactHop;
|
|
379
|
-
resolution: ResolutionLabel;
|
|
380
|
-
}
|
|
381
|
-
interface SymbolPath {
|
|
382
|
-
entry: SymbolInfo;
|
|
383
|
-
changed: SymbolInfo;
|
|
384
|
-
hops: ImpactHop[];
|
|
385
|
-
resolution: ResolutionLabel;
|
|
386
|
-
}
|
|
387
|
-
interface ImportDependent {
|
|
388
|
-
file: string;
|
|
389
|
-
changedFile: string;
|
|
390
|
-
specifier: string;
|
|
391
|
-
resolution: 'import-scoped';
|
|
392
|
-
}
|
|
393
|
-
interface AffectedTest {
|
|
394
|
-
file: string;
|
|
395
|
-
resolution: ResolutionLabel;
|
|
396
|
-
reason: 'changed-test' | 'imports-changed-file' | 'imports-impact-file' | 'references-symbol-name' | 'path-convention';
|
|
397
|
-
distance?: number;
|
|
398
|
-
}
|
|
399
|
-
interface ChangeContextResult {
|
|
400
|
-
root: string;
|
|
401
|
-
source: 'git' | 'diff' | 'files' | 'symbols';
|
|
402
|
-
baseRef?: string;
|
|
403
|
-
changed: ChangedSymbol[];
|
|
404
|
-
unmapped: UnmappedChange[];
|
|
405
|
-
directCallers: ImpactRow[];
|
|
406
|
-
importDependents: ImportDependent[];
|
|
407
|
-
paths: SymbolPath[];
|
|
408
|
-
impact: ImpactRow[];
|
|
409
|
-
tests: AffectedTest[];
|
|
410
|
-
warnings: string[];
|
|
411
|
-
truncated: {
|
|
412
|
-
callers: boolean;
|
|
413
|
-
dependents: boolean;
|
|
414
|
-
paths: boolean;
|
|
415
|
-
impact: boolean;
|
|
416
|
-
tests: boolean;
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
interface CodeChangeContextArgs {
|
|
420
|
-
outputFormat?: 'text' | 'xray';
|
|
421
|
-
repoRoot?: string;
|
|
422
|
-
diff?: string;
|
|
423
|
-
baseRef?: string;
|
|
424
|
-
files?: string[];
|
|
425
|
-
symbols?: SymbolId[];
|
|
426
|
-
maxDepth?: number;
|
|
427
|
-
maxImpact?: number;
|
|
428
|
-
maxPaths?: number;
|
|
429
|
-
maxTests?: number;
|
|
430
|
-
}
|
|
431
|
-
interface ChangeContextOptions {
|
|
432
|
-
maxDepth?: number;
|
|
433
|
-
maxImpact?: number;
|
|
434
|
-
maxPaths?: number;
|
|
435
|
-
maxTests?: number;
|
|
436
|
-
}
|
|
437
|
-
/** Normalized input: exactly one selection mode. */
|
|
438
|
-
type ChangeContextInput = {
|
|
439
|
-
kind: 'git';
|
|
440
|
-
root: string;
|
|
441
|
-
baseRef: string;
|
|
442
|
-
} | {
|
|
443
|
-
kind: 'diff';
|
|
444
|
-
root: string;
|
|
445
|
-
text: string;
|
|
446
|
-
baseRef?: string;
|
|
447
|
-
} | {
|
|
448
|
-
kind: 'files';
|
|
449
|
-
files: string[];
|
|
450
|
-
} | {
|
|
451
|
-
kind: 'symbols';
|
|
452
|
-
symbolIds: SymbolId[];
|
|
453
|
-
};
|
|
454
|
-
/** Validate flat tool args and normalize them into one input mode. */
|
|
455
|
-
declare function changeContextInputFromArgs(args: Pick<CodeChangeContextArgs, 'diff' | 'baseRef' | 'files' | 'symbols'>, root: string): ChangeContextInput;
|
|
456
|
-
declare function buildChangeContext(index: RepoIndex, input: ChangeContextInput, options?: ChangeContextOptions): Promise<ChangeContextResult>;
|
|
457
|
-
/**
|
|
458
|
-
* Render the result as fixed skeleton sections. Section headers and warnings
|
|
459
|
-
* are always kept; rows are trimmed to the final character cap, which then
|
|
460
|
-
* appends an explicit truncation line.
|
|
461
|
-
*/
|
|
462
|
-
declare function renderChangeContext(result: ChangeContextResult, maxChars?: number): string;
|
|
463
|
-
|
|
464
|
-
interface SourceExcerpt {
|
|
465
|
-
file: string;
|
|
466
|
-
startLine: number;
|
|
467
|
-
endLine: number;
|
|
468
|
-
side: 'current' | 'base';
|
|
469
|
-
ref?: string;
|
|
470
|
-
mode: 'complete' | 'window' | 'signature';
|
|
471
|
-
code?: string;
|
|
472
|
-
signature?: string;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
interface ContextPackItem extends Partial<SourceExcerpt> {
|
|
476
|
-
kind: 'primary' | 'caller' | 'callee' | 'import' | 'test' | 'change' | 'file' | 'relationship';
|
|
477
|
-
name?: string;
|
|
478
|
-
reason: string;
|
|
479
|
-
resolution?: ResolutionLabel;
|
|
480
|
-
provenance?: string;
|
|
481
|
-
change?: string;
|
|
482
|
-
text?: string;
|
|
483
|
-
}
|
|
484
|
-
/** The only final presentation DTO. Items here have already passed selection. */
|
|
485
|
-
interface ContextPack {
|
|
486
|
-
version: 1;
|
|
487
|
-
root: string;
|
|
488
|
-
task: string;
|
|
489
|
-
route: TaskContextResult['route']['kind'];
|
|
490
|
-
items: ContextPackItem[];
|
|
491
|
-
gaps: string[];
|
|
492
|
-
budget: {
|
|
493
|
-
budgetChars: number;
|
|
494
|
-
usedChars: number;
|
|
495
|
-
packChars: number;
|
|
496
|
-
truncated: boolean;
|
|
497
|
-
};
|
|
498
|
-
}
|
|
499
|
-
declare function renderContextPack(pack: ContextPack): string;
|
|
500
|
-
|
|
501
|
-
/**
|
|
502
|
-
* Task-aware structural context orchestration.
|
|
503
|
-
*
|
|
504
|
-
* This module deliberately contains no parser, search index, graph, or git
|
|
505
|
-
* implementation of its own. It ranks the existing primitives into a compact
|
|
506
|
-
* package that answers the question: which code matters for this task?
|
|
507
|
-
*/
|
|
508
|
-
|
|
509
|
-
type ContextTaskKind = 'change' | 'symbol' | 'architecture' | 'test' | 'exploration' | 'ambiguous';
|
|
510
|
-
interface ContextRoute {
|
|
511
|
-
kind: ContextTaskKind;
|
|
512
|
-
queries: string[];
|
|
513
|
-
mentionedFiles: string[];
|
|
514
|
-
exactSymbolNames: string[];
|
|
515
|
-
sources: string[];
|
|
516
|
-
includeChangeContext: boolean;
|
|
517
|
-
includeRelationships: boolean;
|
|
518
|
-
}
|
|
519
|
-
interface ContextOptions {
|
|
520
|
-
budgetChars?: number;
|
|
521
|
-
maxFiles?: number;
|
|
522
|
-
maxSymbols?: number;
|
|
523
|
-
}
|
|
524
|
-
interface ContextSymbol {
|
|
525
|
-
symbol: SymbolInfo;
|
|
526
|
-
score: number;
|
|
527
|
-
provenance: 'exact' | 'lexical';
|
|
528
|
-
reason: string;
|
|
529
|
-
}
|
|
530
|
-
interface ContextFile {
|
|
531
|
-
path: string;
|
|
532
|
-
score: number;
|
|
533
|
-
reason: string;
|
|
534
|
-
}
|
|
535
|
-
interface ContextRelationship {
|
|
536
|
-
text: string;
|
|
537
|
-
resolution: ResolutionLabel;
|
|
538
|
-
priority: number;
|
|
539
|
-
key: string;
|
|
540
|
-
provenance: string;
|
|
541
|
-
}
|
|
542
|
-
interface ContextConfidence {
|
|
543
|
-
exact: number;
|
|
544
|
-
'import-scoped': number;
|
|
545
|
-
'name-only': number;
|
|
546
|
-
}
|
|
547
|
-
interface TaskContextResult {
|
|
548
|
-
pack: ContextPack;
|
|
549
|
-
root: string;
|
|
550
|
-
task: string;
|
|
551
|
-
route: ContextRoute;
|
|
552
|
-
primarySymbols: ContextSymbol[];
|
|
553
|
-
relevantFiles: ContextFile[];
|
|
554
|
-
relationships: ContextRelationship[];
|
|
555
|
-
changeContext: ChangeContextResult | null;
|
|
556
|
-
tests: string[];
|
|
557
|
-
testEvidence: AffectedTest[];
|
|
558
|
-
confidence: ContextConfidence;
|
|
559
|
-
warnings: string[];
|
|
560
|
-
budget: {
|
|
561
|
-
usedChars: number;
|
|
562
|
-
budgetChars: number;
|
|
563
|
-
truncated: boolean;
|
|
564
|
-
};
|
|
565
|
-
}
|
|
566
|
-
/** Classify a task and decide which existing primitives should be composed. */
|
|
567
|
-
declare function routeTask(task: string, index?: RepoIndex): ContextRoute;
|
|
568
|
-
/** Build a deterministic, bounded context package from the current task. */
|
|
569
|
-
declare function buildTaskContext(index: RepoIndex, task: string, options?: ContextOptions): Promise<TaskContextResult>;
|
|
570
|
-
/** Text always renders the final selected pack. */
|
|
571
|
-
declare function renderTaskContext(result: TaskContextResult): string;
|
|
572
|
-
|
|
573
|
-
declare const tools: _deepseek_ai_dsh_tools.ToolDefinition[];
|
|
574
|
-
|
|
575
|
-
/** Browser-safe presentation contract. Persisted v1 PoCs remain readable. */
|
|
576
|
-
declare const XRAY_LIMITS: {
|
|
577
|
-
readonly nodes: 32;
|
|
578
|
-
readonly edges: 48;
|
|
579
|
-
readonly paths: 12;
|
|
580
|
-
readonly evidenceChars: 900;
|
|
581
|
-
readonly metadataBytes: 96000;
|
|
582
|
-
};
|
|
583
|
-
interface XrayEvidence {
|
|
584
|
-
file: string;
|
|
585
|
-
line: number;
|
|
586
|
-
startLine: number;
|
|
587
|
-
side: 'current' | 'base';
|
|
588
|
-
ref?: string;
|
|
589
|
-
type?: 'declaration' | 'call-site' | 'import-site' | 'test-lead' | 'unmapped-change';
|
|
590
|
-
code?: string;
|
|
591
|
-
gap?: string;
|
|
592
|
-
}
|
|
593
|
-
interface XrayNode {
|
|
594
|
-
id: string;
|
|
595
|
-
name: string;
|
|
596
|
-
kind: string;
|
|
597
|
-
file: string;
|
|
598
|
-
line: number;
|
|
599
|
-
changed: boolean;
|
|
600
|
-
category?: 'change' | 'caller' | 'impact' | 'module' | 'test';
|
|
601
|
-
depth?: number;
|
|
602
|
-
resolution?: ImpactHop['resolution'];
|
|
603
|
-
change?: string;
|
|
604
|
-
reason: string;
|
|
605
|
-
evidence: XrayEvidence;
|
|
606
|
-
}
|
|
607
|
-
interface XrayEdge {
|
|
608
|
-
id: string;
|
|
609
|
-
from: string;
|
|
610
|
-
to: string;
|
|
611
|
-
kind?: 'call' | 'import';
|
|
612
|
-
resolution: ImpactHop['resolution'];
|
|
613
|
-
reason: string;
|
|
614
|
-
evidence: XrayEvidence;
|
|
615
|
-
}
|
|
616
|
-
interface XrayPath {
|
|
617
|
-
id: string;
|
|
618
|
-
from: string;
|
|
619
|
-
to: string;
|
|
620
|
-
edgeIds: string[];
|
|
621
|
-
resolution: ImpactHop['resolution'];
|
|
622
|
-
omitted: boolean;
|
|
623
|
-
}
|
|
624
|
-
interface ImpactXray {
|
|
625
|
-
card: 'impact-xray';
|
|
626
|
-
version: 1 | 2;
|
|
627
|
-
root: string;
|
|
628
|
-
source: ChangeContextResult['source'];
|
|
629
|
-
nodes: XrayNode[];
|
|
630
|
-
edges: XrayEdge[];
|
|
631
|
-
tests: Array<ChangeContextResult['tests'][number] & {
|
|
632
|
-
nodeId?: string;
|
|
633
|
-
}>;
|
|
634
|
-
imports: Array<ChangeContextResult['importDependents'][number] & {
|
|
635
|
-
edgeId?: string;
|
|
636
|
-
}>;
|
|
637
|
-
paths?: XrayPath[];
|
|
638
|
-
unmapped?: Array<ChangeContextResult['unmapped'][number] & {
|
|
639
|
-
evidence: XrayEvidence;
|
|
640
|
-
}>;
|
|
641
|
-
bounds?: {
|
|
642
|
-
analysis: ChangeContextResult['truncated'];
|
|
643
|
-
projection: boolean;
|
|
644
|
-
text?: boolean;
|
|
645
|
-
totals: {
|
|
646
|
-
changed: number;
|
|
647
|
-
callers: number;
|
|
648
|
-
impact: number;
|
|
649
|
-
imports: number;
|
|
650
|
-
tests: number;
|
|
651
|
-
paths: number;
|
|
652
|
-
unmapped: number;
|
|
653
|
-
};
|
|
654
|
-
};
|
|
655
|
-
gaps: string[];
|
|
656
|
-
truncated: boolean;
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
/** Presentation of existing analysis. Never resolves new function-call edges. */
|
|
660
|
-
declare function buildImpactXray(index: RepoIndex, result: ChangeContextResult): Promise<ImpactXray>;
|
|
661
|
-
|
|
662
|
-
/** Plugin configuration: merged once at apply time, read wherever needed. */
|
|
663
|
-
|
|
664
|
-
/** DSH 0.1.7+ renders fields marked volatile in its per-plugin config forms. */
|
|
665
|
-
declare const Config: Schema<Schemastery.ObjectS<NoInfer<{
|
|
666
|
-
excludeDirs: Schema<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
667
|
-
mapTopFiles: Schema<number, number, "volatile-defined">;
|
|
668
|
-
mapMaxChars: Schema<number, number, "volatile-defined">;
|
|
669
|
-
mapTtlMs: Schema<number, number, "volatile-defined">;
|
|
670
|
-
autoInject: Schema<boolean, boolean, "volatile-defined">;
|
|
671
|
-
codeHealth: Schema<boolean, boolean, "volatile-defined">;
|
|
672
|
-
toolSurface: Schema<string, string, "volatile-defined">;
|
|
673
|
-
externalWatch: Schema<boolean, boolean, "volatile-defined">;
|
|
674
|
-
watchDebounceMs: Schema<number, number, "volatile-defined">;
|
|
675
|
-
}>>, Schemastery.ObjectT<NoInfer<{
|
|
676
|
-
excludeDirs: Schema<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
677
|
-
mapTopFiles: Schema<number, number, "volatile-defined">;
|
|
678
|
-
mapMaxChars: Schema<number, number, "volatile-defined">;
|
|
679
|
-
mapTtlMs: Schema<number, number, "volatile-defined">;
|
|
680
|
-
autoInject: Schema<boolean, boolean, "volatile-defined">;
|
|
681
|
-
codeHealth: Schema<boolean, boolean, "volatile-defined">;
|
|
682
|
-
toolSurface: Schema<string, string, "volatile-defined">;
|
|
683
|
-
externalWatch: Schema<boolean, boolean, "volatile-defined">;
|
|
684
|
-
watchDebounceMs: Schema<number, number, "volatile-defined">;
|
|
685
|
-
}>>, "plain">;
|
|
686
|
-
interface PluginConfig {
|
|
687
|
-
/** Extra directories to exclude from indexing (appended to defaults). */
|
|
688
|
-
excludeDirs?: string[];
|
|
689
|
-
/** Max files in the ranked repo map (code_map / auto section). */
|
|
690
|
-
mapTopFiles?: number;
|
|
691
|
-
/** Hard char cap for rendered maps. */
|
|
692
|
-
mapMaxChars?: number;
|
|
693
|
-
/** Refresh interval for the auto-injected map (ms, min 1000). */
|
|
694
|
-
mapTtlMs?: number;
|
|
695
|
-
/** Set false to disable the auto-injected system section. */
|
|
696
|
-
autoInject?: boolean;
|
|
697
|
-
/** Register the code_health tool (circles / orphans). Off by default. */
|
|
698
|
-
codeHealth?: boolean;
|
|
699
|
-
/** Experimental tool surface; full preserves the complete v0.6 API. */
|
|
700
|
-
toolSurface?: 'full' | 'compact';
|
|
701
|
-
/** Observe source changes made by editors and external tools. */
|
|
702
|
-
externalWatch?: boolean;
|
|
703
|
-
/** Coalesce filesystem events before refreshing affected files. */
|
|
704
|
-
watchDebounceMs?: number;
|
|
705
|
-
}
|
|
1
|
+
export { buildIndex, buildIndexWithCache, findRepoRoot } from './buildIndex.js';
|
|
2
|
+
export { extractAll, extractSymbols, languageForFile, parseFileToSymbols } from './extract.js';
|
|
3
|
+
export { DEFAULT_EXCLUDED_DIRS, SUPPORTED_EXTS, scanRepo } from './scan.js';
|
|
4
|
+
export { CACHE_DIR_NAME, defaultCachePath, loadIndex, saveIndex } from './store.js';
|
|
5
|
+
export { IndexOptions, IndexedFile, ParseDiagnostic, RepoIndex, SymbolInfo, SymbolKind, symbolCount } from './types.js';
|
|
6
|
+
export { indexIntegrityWarnings } from './integrity.js';
|
|
7
|
+
export { renderHit, searchSymbols } from './search.js';
|
|
8
|
+
export { rankRepoMap, renderRepoMap, scoreFile } from './repomap.js';
|
|
9
|
+
export { buildSymbolTable, callerCounts, symbolRefs } from './refgraph.js';
|
|
10
|
+
export { buildModuleGraph, findCycles, findOrphanModules } from './health.js';
|
|
11
|
+
export { DiffHunk, FileChange, parseUnifiedDiff, readGitFileAtRef, readWorkingTreeDiff } from './git-diff.js';
|
|
12
|
+
export { ContextFile, ContextOptions, ContextRelationship, ContextRoute, ContextSymbol, ContextTaskKind, TaskContextResult, buildTaskContext, renderTaskContext, routeTask } from './context.js';
|
|
13
|
+
export { ContextPack, ContextPackItem, renderContextPack } from './context-pack.js';
|
|
14
|
+
export { AffectedTest, ChangeContextInput, ChangeContextOptions, ChangeContextResult, ChangeKind, ChangedSymbol, CodeChangeContextArgs, ImpactHop, ImpactRow, ImportDependent, SymbolPath, UnmappedChange, buildChangeContext, changeContextInputFromArgs, renderChangeContext } from './change-context.js';
|
|
15
|
+
export { tools } from './tools.js';
|
|
16
|
+
export { XRAY_LIMITS, buildImpactXray } from './impact-xray.js';
|
|
17
|
+
export { ImpactXray, XrayEdge, XrayEvidence, XrayNode, XrayPath } from './impact-xray-model.js';
|
|
18
|
+
import { PluginConfig } from './config.js';
|
|
19
|
+
export { Config } from './config.js';
|
|
706
20
|
|
|
707
21
|
/**
|
|
708
22
|
* dsh-code-index — DeepSeek Harness bundle entry.
|
|
@@ -738,4 +52,4 @@ declare const name = "dsh-code-index";
|
|
|
738
52
|
declare const inject: readonly ["tools", "systemPrompt"];
|
|
739
53
|
declare function apply(ctx: MinimalContext, pluginConfig?: PluginConfig): void;
|
|
740
54
|
|
|
741
|
-
export {
|
|
55
|
+
export { apply, inject, name };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-code-index",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.2",
|
|
4
4
|
"description": "Impact X-Ray for DSH: inspect potential code-change impact, provenance and real source evidence before AI edits.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
54
|
-
"@deepseek-ai/dsh-tools": "0.2.0-rc.2
|
|
54
|
+
"@deepseek-ai/dsh-tools": ">=0.2.0-rc.2"
|
|
55
55
|
},
|
|
56
56
|
"peerDependenciesMeta": {
|
|
57
57
|
"@deepseek-ai/cordis": {
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
"@types/node": "^26.2.0",
|
|
65
65
|
"@types/react": "^18.3.31",
|
|
66
66
|
"react": "^18.3.1",
|
|
67
|
+
"semver": "^7.8.5",
|
|
67
68
|
"tsup": "^8.5.0",
|
|
68
69
|
"typescript": "^6.0.3",
|
|
69
70
|
"vitest": "^4.1.8"
|
|
@@ -4,6 +4,7 @@ import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'nod
|
|
|
4
4
|
import os from 'node:os'
|
|
5
5
|
import { createRequire } from 'node:module'
|
|
6
6
|
import path from 'node:path'
|
|
7
|
+
import semver from 'semver'
|
|
7
8
|
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
8
9
|
|
|
9
10
|
const root = path.resolve(fileURLToPath(new URL('..', import.meta.url)))
|
|
@@ -66,10 +67,10 @@ function parseJsonArrayOutput(output, command) {
|
|
|
66
67
|
|
|
67
68
|
// A release gate can reuse one immutable candidate across Node versions.
|
|
68
69
|
const suppliedTarball = process.argv[2] ? path.resolve(process.argv[2]) : null
|
|
69
|
-
// Exercise
|
|
70
|
-
const toolsVersion = process.argv[3] ?? '0.2.1-rc.
|
|
71
|
-
const
|
|
72
|
-
assert.ok(
|
|
70
|
+
// Exercise a selected host version against the same immutable candidate.
|
|
71
|
+
const toolsVersion = process.argv[3] ?? '0.2.1-rc.3'
|
|
72
|
+
const toolsRange = packageJson.peerDependencies['@deepseek-ai/dsh-tools']
|
|
73
|
+
assert.ok(semver.satisfies(toolsVersion, toolsRange, { includePrerelease: true }), 'smoke requires a DSH tools version matching the peer range')
|
|
73
74
|
const packed = suppliedTarball ? null : parseJsonArrayOutput(run(npm, ['pack', '--ignore-scripts', '--json'], root), 'npm pack')
|
|
74
75
|
if (packed) assert.equal(packed.length, 1, 'npm pack must produce exactly one artifact')
|
|
75
76
|
const tarball = suppliedTarball ?? path.resolve(root, packed[0].filename)
|
|
@@ -97,7 +98,11 @@ for (const file of expectedFiles) assert.ok(listed.has(file), `tarball is missin
|
|
|
97
98
|
const smokeDir = mkdtempSync(path.join(os.tmpdir(), 'dsh-code-index-release-'))
|
|
98
99
|
try {
|
|
99
100
|
run(npm, ['init', '--yes'], smokeDir)
|
|
100
|
-
|
|
101
|
+
// npm's default peer checks exclude prereleases that DSH explicitly accepts.
|
|
102
|
+
// --force only prepares this disposable runtime fixture; it does not prove
|
|
103
|
+
// normal npm peer resolution. The range assertion above uses DSH's rules.
|
|
104
|
+
console.log('smoke fixture: npm --force bypasses peer resolution; validating DSH range and runtime only')
|
|
105
|
+
run(npm, ['install', '--ignore-scripts', '--no-save', '--force', tarball, `@deepseek-ai/dsh-tools@${toolsVersion}`], smokeDir)
|
|
101
106
|
|
|
102
107
|
const installedPackage = JSON.parse(
|
|
103
108
|
readFileSync(path.join(smokeDir, 'node_modules', packageJson.name, 'package.json'), 'utf8'),
|
|
@@ -108,7 +113,7 @@ try {
|
|
|
108
113
|
assert.match(installedPackage.engines.node, />=22/)
|
|
109
114
|
assert.equal(installedPackage.license, 'MIT')
|
|
110
115
|
const installedTools = JSON.parse(readFileSync(path.join(smokeDir, 'node_modules', '@deepseek-ai', 'dsh-tools', 'package.json'), 'utf8'))
|
|
111
|
-
assert.equal(installedTools.version, toolsVersion, 'consumer must use the selected
|
|
116
|
+
assert.equal(installedTools.version, toolsVersion, 'consumer must use the selected DSH tools version')
|
|
112
117
|
|
|
113
118
|
const plugin = await import(
|
|
114
119
|
pathToFileURL(path.join(smokeDir, 'node_modules', packageJson.name, 'dist', 'index.js')).href,
|
|
@@ -294,6 +299,8 @@ try {
|
|
|
294
299
|
assert.ok(rustSymbols.value.some(symbol => symbol.name === 'CrateOnly' && symbol.signature === 'CrateOnly' && !symbol.exported))
|
|
295
300
|
assert.ok(rustSymbols.value.some(symbol => symbol.name === 'Restricted' && symbol.signature === 'Restricted' && !symbol.exported))
|
|
296
301
|
const rustMap = await nativeInvoke('code_map', { repoRoot: rustRoot })
|
|
302
|
+
const health = await nativeInvoke('code_health', { repoRoot: rustRoot })
|
|
303
|
+
assert.match(health.value, /repo health/)
|
|
297
304
|
for (const name of ['PublicOne', 'PublicEnum', 'PublicTrait']) {
|
|
298
305
|
const search = await nativeInvoke('code_search', { query: name, repoRoot: rustRoot })
|
|
299
306
|
assert.equal(search.value[0]?.name, name)
|
|
@@ -337,7 +344,7 @@ try {
|
|
|
337
344
|
for (const dispose of nativeDisposers.reverse()) dispose()
|
|
338
345
|
await nativeContext.fiber.dispose()
|
|
339
346
|
}
|
|
340
|
-
console.log(`release smoke passed for ${packageJson.name}@${packageJson.version} with dsh-tools@${toolsVersion}`)
|
|
347
|
+
console.log(`release smoke passed (DSH range/runtime; npm peer resolution bypassed) for ${packageJson.name}@${packageJson.version} with dsh-tools@${toolsVersion}`)
|
|
341
348
|
} finally {
|
|
342
349
|
rmSync(smokeDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 })
|
|
343
350
|
if (!suppliedTarball) rmSync(tarball, { force: true })
|