🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

索引透视 Moowningstar/gitnexus-plus

DeepSeek Harnessv0.1.9
Moowningstar3 天前有提交已提供中文文档扫描:高危

DSH 插件:报告 GitNexus 索引遗漏了什么,包括跳过的大文件、索引覆盖率,以及把 git 活动与索引交叉比对。

36.4/100
综合分
2
GitHub 星标
559
npm 周下载
—/5
用户评分

数据截至 2026/10/29 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add dsh-gitnexus-plus
npm 源 · dsh-gitnexus-plus 的发布者账号已校验与本仓库一致

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · dev
装得上吗
本站已在 dsh 0.2.1-rc.5 下真实安装成功(非静态推断);本次验证针对插件 v0.1.10,当前版本为 v0.1.9
L5 · 运行时冒烟等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 3 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查✓ 自动检查通过
展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✓npm 包dsh-gitnexus-plus @ 0.1.9
✓Node 引擎要求 >=22 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/28 00:12:51

🔴 安全扫描发现高危能力自动 · 静态分析源码
v2.7.0
🔴 扫描:高危尚无人工评估

静态扫描发现安装期即执行代码,或命令参数由运行时变量拼接(参数来源无法静态判定,从严处理)。

安装时即执行脚本可执行外部命令读写/删除本地文件写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 79 处,展示前 66 条)
  • 发起网络请求
    lib/client.js:1467
    fetch((0, navigation_1.routeUrl)(`/api/gitnexus/file-diff?repo=${encodeURIComponent(projectPath)}` +
  • 发起网络请求
    lib/client.js:2157
    fetch((0, navigation_2.routeUrl)(`/api/gitnexus/file-diff?repo=${encodeURIComponent(projectPath)}` +
  • 发起网络请求
    lib/client.js:2429
    fetch((0, navigation_2.routeUrl)(query))
  • 发起网络请求
    lib/client.js:3436
    const res = await fetch((0, navigation_3.routeUrl)(`/api/gitnexus/webview?repo=${encodeURIComponent(projectPath)}`), signal ? { signal } : undefined);
  • 发起网络请求
    lib/client.js:3476
    const res = await fetch(url, { method: action === 'stop' ? 'DELETE' : 'POST' });
  • 写入剪贴板
    lib/client.js:3985
    : (text) => navigator.clipboard?.writeText(text);
  • 写入剪贴板
    lib/client.js:4487
    : (text) => navigator.clipboard?.writeText(text);
  • 发起网络请求
    lib/client.js:5610
    const res = await fetch((0, navigation_4.routeUrl)('/api/gitnexus/install'));
  • 写入剪贴板
    lib/client.js:5794
    : (value) => navigator.clipboard?.writeText(value);
  • 写入剪贴板
    lib/client.js:6661
    : (value) => navigator.clipboard?.writeText(value);
  • 读取本地文件
    scripts/link-runtime.mjs:58
    if (existsSync(path.join(root, '@deepseek-ai', 'dsh-tools'))) return root;
  • 读取本地文件
    scripts/link-runtime.mjs:90
    if (!existsSync(target)) {
  • 读取本地文件
    scripts/link-runtime.mjs:97
    if (existsSync(link)) {
  • 读取本地文件
    scripts/link-runtime.mjs:100
    isLink = statSync(link).isDirectory() && existsSync(path.join(link, 'package.json'));
  • 读取本地文件
    scripts/link-runtime.mjs:100
    isLink = statSync(link).isDirectory() && existsSync(path.join(link, 'package.json'));
  • 写入本地文件
    scripts/link-runtime.mjs:117
    mkdirSync(path.dirname(link), { recursive: true });
  • 读取本地文件
    scripts/link-runtime.mjs:118
    if (existsSync(link)) rmSync(link, { recursive: true, force: true });
  • 删除本地文件递归
    scripts/link-runtime.mjs:118
    if (existsSync(link)) rmSync(link, { recursive: true, force: true });
  • 读取本地文件
    lib/gitnexus/commands.js:59
    const raw = JSON.parse(readFileSync(path.join(repoPath, '.gitnexus', name), 'utf8'));
  • 读取本地文件
    lib/gitnexus/index-meta.js:105
    return existsSync(path.join(repoPath, '.git'));
  • 读取本地文件
    lib/gitnexus/delta.js:116
    const stream = createReadStream(absolutePath);
  • 写入本地文件
    lib/gitnexus/git-capture.js:120
    mkdirSync(inside, { recursive: true });
  • 执行外部命令固定命令
    lib/gitnexus/git-capture.js:215
    child = spawn('git', args, {
  • 读取本地文件
    lib/gitnexus/git-capture.js:240
    text = readFileSync(outFile, 'utf8');
  • 读取本地文件
    lib/gitnexus/git-capture.js:241
    stderr = readFileSync(errFile, 'utf8');
  • 删除本地文件
    lib/gitnexus/git-capture.js:286
    rmSync(file, { force: true });
  • 读取本地文件
    lib/gitnexus/watch/stamps.js:211
    if (!existsSync(file)) {
  • 读取本地文件
    lib/gitnexus/watch/stamps.js:216
    const stat = statSync(file);
  • 读取本地文件
    lib/gitnexus/watch/registry.js:160
    return existsSync(head) ? String(statSync(head).mtimeMs) : 'no-head';
  • 读取本地文件
    lib/gitnexus/watch/registry.js:160
    return existsSync(head) ? String(statSync(head).mtimeMs) : 'no-head';
  • 读取本地文件
    lib/gitnexus/graph.js:129
    if (!existsSync(lbugPath)) {
  • 读取本地文件
    lib/gitnexus/graph.js:444
    if (!existsSync(path.join(absRepo, '.gitnexus', 'lbug'))) {
  • 读取本地文件
    lib/gitnexus/graph.js:531
    const mtime = statSync(metaFile).mtimeMs;
  • 读取本地文件
    lib/gitnexus/graph-query.js:85
    if (!existsSync(path.join(absRepo, '.gitnexus', 'lbug'))) {
  • 写入本地文件
    lib/gitnexus/graph-query.js:103
    await writeFile(requestFile, JSON.stringify({ repo: absRepo, tool, params }), 'utf8');
  • 删除本地文件递归
    lib/gitnexus/graph-query.js:141
    await rm(dir, { recursive: true, force: true }).catch(() => undefined);
  • 读取本地文件
    lib/gitnexus/search.js:82
    if (existsSync(root))
  • 读取本地文件
    lib/gitnexus/search.js:197
    if (!existsSync(lbugPath)) {
  • 读取本地文件
    lib/gitnexus/search.js:374
    if (!existsSync(path.join(absRepo, '.gitnexus', 'lbug'))) {
  • 读取本地文件
    lib/gitnexus/search.js:578
    const mtime = statSync(path.join(absRepo, '.gitnexus', 'gitnexus.json')).mtimeMs;
  • 删除本地文件
    lib/gitnexus/analyze.js:180
    rmSync(path.join(repoPath, '.gitnexus', 'lbug.init.lock'), { force: true });
  • 读取本地文件
    lib/gitnexus/analyze.js:217
    return existsSync(path.join(repoPath, '.gitnexus', 'lbug'));
  • 删除本地文件递归
    lib/gitnexus/analyze.js:267
    rmSync(path.join(indexDir, RUN_SCOPED_STORE), { recursive: true, force: true });
  • 写入本地文件
    lib/gitnexus/analyze.js:275
    mkdirSync(target, { recursive: true });
  • 删除本地文件递归
    lib/gitnexus/analyze.js:341
    rmSync(path.join(repoPath, '.gitnexus', 'plugin-home'), { recursive: true, force: true });
  • 写入本地文件
    lib/gitnexus/install.js:163
    mkdirSync(prefix, { recursive: true });
  • 发起网络请求
    lib/gitnexus/webview.js:123
    const doFetch = options.fetchLike ?? ((url, init) => fetch(url, init));
  • 读取本地文件
    lib/gitnexus/storage-state.js:65
    raw = readFileSync(file, 'utf8');
  • 读取本地文件
    lib/gitnexus/storage-state.js:136
    entries = readdirSync(storagePath);
  • 读取本地文件
    lib/gitnexus/toolchain.js:130
    if (typeof explicit === 'string' && explicit !== '' && existsSync(explicit)) {
  • 读取本地文件
    lib/gitnexus/toolchain.js:146
    if (existsSync(script)) {
  • 读取本地文件
    lib/gitnexus/toolchain.js:162
    if (existsSync(candidate)) {
  • 读取本地文件
    lib/gitnexus/toolchain.js:194
    if (existsSync(manifest) && existsSync(script)) {
  • 读取本地文件
    lib/gitnexus/discover.js:67
    return existsSync(path.join(dir, '.gitnexus', 'lbug'));
  • 读取本地文件
    lib/gitnexus/discover.js:131
    const hasGit = existsSync(path.join(dir, '.git'));
  • 写入本地文件
    lib/gitnexus/lbug-heal.js:48
    renameSync(shadow, parked);
  • 读取本地文件
    lib/gitnexus/root.js:80
    const hasShim = existsSync(path.join(dir, 'gitnexus.cmd')) ||
  • 读取本地文件
    lib/gitnexus/root.js:81
    existsSync(path.join(dir, 'gitnexus.exe')) ||
  • 读取本地文件
    lib/gitnexus/root.js:82
    existsSync(path.join(dir, 'gitnexus.ps1')) ||
  • 读取本地文件
    lib/gitnexus/root.js:83
    existsSync(path.join(dir, 'gitnexus'));
  • 读取本地文件
    lib/gitnexus/root.js:140
    if (existsSync(hit.root))
  • 读取本地文件
    lib/gitnexus/root.js:146
    if (existsSync(path.join(root, 'package.json')) && existsSync(path.join(root, DIST.walker))) {
  • 读取本地文件
    lib/gitnexus/webdist.js:145
    if (existsSync(path.join(candidate, 'index.html')))
  • 读取本地文件
    lib/gitnexus/webdist.js:185
    else if (existsSync(target)) {
  • 读取本地文件
    lib/bin/graph-query-helper.js:64
    const parsed = JSON.parse(readFileSync(file, 'utf8'));
  • 读取本地文件
    lib/gitnexus/risk-query.js:101
    if (!existsSync(lbugPath))
扫描于 2026/11/1(0 天前) · 范围 入口闭包 + 全包扫描 · 来源 npm 发布包

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-tools@deepseek-ai/schemastery
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
v0.1.102026-10-28
· 0.1.10 0.1.9 让索引说清楚「我知道什么、不知道什么」。0.1.10 做的是另一半:在漂移影响到你之前先说出口——提交时间线终于是一张图、每个提交带着它碰过的文件、被查询的工具会在结果前面加一句实话,CI 也能用一个退出码挡住「索引漏了文件」。 · 新增 · 提交时间线是图,不是一列点 0.1.9 的泳道有三个毛病被人一眼看出来:线宽 10px 而点直径只有 6.4px,相邻两列差不到一个点的宽度;颜色编码的是「索引裁决」,于是每条分支都是同一个绿;git log 只走 HEAD,除 merge 外根本没有并行可画。三处一起改: · 列宽 14、每列一个固定色(八色调色板),点用自己列的颜色,索引裁决改成点外面的细环——两套语义不再抢同一个通道。 · --all:时间线读所有 ref,才真的出现并行分支;代价是聚合统计(提交者、改动数)也计入未合并分支,这一点写进了命令注释。 · 分支名进载荷:解析 %D(HEAD -> main, origin/main, tag: v0.1.9)得到 refs,行内渲染成与列同色的胶囊,HEAD -> 剥成裸分支名,tag: 前缀保留(tag 不是分支)。 · 超过 6 列折叠:右缘写 +N,数字按本行穿过的被折叠列统计,而不是 laneCount - 6(否则只穿越两条线的行会谎报)。 里仍报真实总数。 · 悬停一行:该行的线加粗到 2.8px、点放大 0.7px,它穿越的其它列淡到 0.3——在并排三条线时能一眼分清「这条是哪一行」。 · 曲线用竖直中位控制点的三次贝塞尔,线与点在 6.5px 处留空(DOT_RADIUS + DOT_GAP),不再有线从点中间穿过去。 没有引入图库,理由记在计划文档 §2.1:@gitgraph/core 按「分支从哪个提交创建」分列,用 --all 反推会把主干塞进匿名列;而本插件的客户端构建链没有 bundler,引包只能 vendored 源码。 · 每个提交带着它碰过的文件,点文件就地看 diff · git log 现在多解析 %P(父提交)与 --numstat 的逐文件增删,每个提交带 changed: [{ path, insertions, deletions }],在解析处按 40 个封顶(COMMIT_FILE_LIMIT),超出的提交标 filesTruncated,面板写「仅列出前 40 个文件(共 N 个)」。 · 点提交展开文件列表,再点某个文件,就地复用已有的 DiffView 渲染补丁——走的是 0.1.8 就有的 /api/gitnexus/file-diff 路由,没有新路由、没有新的写路径。宿主不认识这条路由时,面板明说「当前宿主没有文件改动路由;重启 DSH 后再试」,不伪装成空 diff。 · 「更多」不是「最多看到前几个」 四个文件列表各自记住翻到哪(基线 8、频繁 8、最近 5、缺失 8,每次 +25),提交时间线一次 15 条、每次 +50,尾部按钮写清还剩多少。翻页状态按列表分开存,切列表不会互相重置。 · 索引落后时会先说话(面板与三个工具) 一句话由 describeStaleness() 生成,面板与工具共用同一套规则: · 干净就一个字都不说:needsRefresh === false 且 certain === true 才返回 null。截断过的「干净」不算干净,会走第三类。 · 落后就说清楚:「索引落后:自 99c1e78 起 16 个文件已改(14 改 / 2 增 / 0 删),以下结果可能过期;建议增量刷新。」 · 无法确认单独措辞:「索引新鲜度无法确认…;以下结果可能过期。」unknown 不借用「落后」这句话——否则用户会去刷新一个本来没问题的索引。 gitnexus_impact / gitnexus_context / gitnexus_query 的返回顶部会带上这句(5 秒 TTL,失败静默;工具本身从不为测新鲜度而变慢);面板把它画成图卡片顶部的一条窄提示。老宿主根本不发这个字段,所以客户端把「缺席」当作 null:宁可安静,也不误报。 · CI 能挡住「索引漏了文件」 gitnexus-coverage 新增两道门,与原有的 --fail-on-large 分工: | 开关 | 命中 | 退出码 | | --- | --- | --- | | --fail-on-uncovered | 扫描选中但索引里没有的文件 | 3 | | --fail-on-missing | 没有索引、索引指向已删文件、目标分析不了 | 3 | |
v0.1.92026-10-27
· 0.1.9 0.1.8 把索引交给 agent,并让它记得自己描述的是哪个提交。0.1.9 做的是另一件事:让索引和引擎说清楚自己知道什么、不知道什么——流程数旁边写清预算,版本不一致时写清是跨线还是出界;顺带把 Git 活动面板修到能用(文件时间线点哪开哪、顶栏真切分支)。 · 新增 · 流程预算(图质量说真话) 索引清单里的 processDetection(maxProcesses / maxProcessBranching / maxProcessTraceDepth / maxEntryPointCandidates)现在被读出来,和 stats.processes 一起解释: · 被预算截断:流程数达到 maxProcesses,或分析器记录了分支/深度/入口候选上限 —— 卡片写「可能漏报,不代表流程只有这些」。 · 全程未见:0 条流程但入口点候选 > 0 —— 写「本次没有流程进入排名;不代表仓库里没有流程」,不再把 0 读成「没有流程」。 · 确实没有 / 没有预算:两者分开。旧版分析器的清单没有 processDetection,写「该索引未记录流程检测预算;N 条流程的完整性无法判断」,不是「没有上限」。 · 文本输出(bin/gitnexus-coverage.js)在图例行后追加 process budgets: …,只在该结论不是「完整」时出现。 实测本仓库:{"maxProcesses":null,"maxProcessBranching":4,"maxProcessTraceDepth":10,"maxEntryPointCandidates":200},315 条流程 → budget-limited,文案「流程 315 条——在分支 ≤4、深度 ≤10、入口候选 ≤200 的预算内得到;可能漏报,不代表流程只有这些。」 · 版本线(跨线不是错误) runnerIdentity.cliVersion 与当前 CLI 的比较不再只给「一致 / 不一致」: · 新增 src/gitnexus/version-lines.ts:解析版本、比较(含 prerelease 数字段,rc.159 > rc.9)、判定 stable / candidate / other / unknown,并把一对版本归为 same / cross-line / index-out-of-range / cli-out-of-range / out-of-range / unknown。 · readCompatibility 的 status 保持字符串相等(match/mismatch/unknown),新增 supportedRange、relation、indexedLine、installedLine;message 三分支:一致、跨线(「两者都在受支持范围内(^1.6.12 || >=1.6.13-rc.0 --(唯一会写工作树的路由,走写守卫),成功后重读报告。 · 不做任何自动处置:不 stash、不 rebase、不切换前检查脏工作区;git 拒绝(冲突、未提交改动)就把 git 的原话显示在顶栏,按钮恢复可用。 · dubious ownership(Windows 上常见的“仓库属主不同”)只对这一条路径做一次 -c safe.directory= 重试,不写全局配置。 · 卡片里的只读分支视图和「回到当前分支」一并删除——切换就是切换。 · 文件时间线点哪开哪 · 点文件行的「时间线」,提交时间线展开在被点的那一行下面,滚动仅滚到最近边缘,不把卡片顶上去。 · 同一文件同时出现在「自基线以来」「最近改动」「索引缺失」多个列表时,先点的那个列表负责承载;再点一次收起,换列表点则移动。 · 表头选择器选中的文件,时间线渲染在卡片顶部(没有行可挂)。 · 顶栏置顶且不透明 · 项目头部(项目名、分支下拉、刷新)改为 sticky 顶栏,背景取宿主 --dsw-alias-bg-base,卡片滚过去不会从背后透出来;根容器的顶部内边距挪进顶栏,消除 12px 缝隙。 · 活动卡片降密度 · 四个文件列表(基线以来 / 改动最频繁 / 最近改动 / 应被索引缺失)合并成一个分段控件,同一时刻只渲染一个列表,计数放在控件标签里;交叉表与提交者收进「更多统计」折叠区。卡片高度约减半,不再三次重复同一路径。 · 提交改动块 · 展开提交后的 patch 从纯彩色 改为带边框的代码块:头部显示 改动 · 语言 与 +A −R 计数、可复制整段补丁;正文按 unified diff 解析出新旧
v0.1.82026-10-25
· 0.1.8 这一版把 GitNexus 索引交给 agent,并让索引记得自己描述的是哪个提交。 · 三个图工具 · gitnexus_impact —— 改这个符号会波及谁(按深度分组,附受影响的流程与模块) · gitnexus_context —— 一个符号的定义、调用者、被调用者与它参与的流程 · gitnexus_query —— 用自然语言问图「这块功能在哪」 risk / epistemic / boundaries / causes 永不截断,列表才有上限且计数永远是真实值;risk: UNKNOWN 会明说「这不等于安全」;上游给了 riskNote 就整句引用。回答不了就直说:查不到符号时提示「先刷新索引」,而不是印成「零调用者」。gitnexus_query 的定义行会打印 uid ,可以原样交给 gitnexus_context。 · 索引锚定 刷新成功后把当前提交写回 manifest 的 lastCommit 与本次 analyze 用的那份 registry —— 字节级替换、只填空绝不覆盖、脏工作区不写(否则门禁会误报「已是最新」)。面板在锚定期间显示「正在记录提交基线…」。于是 gitnexus_delta 终于能给出「无需刷新」,而不是永远「无法确认」。 · 子进程走宿主的执行 seam 新增一条 runner:宿主提供 ctx.subprocess 时用 argv 直接 spawn(不经 shell),有沙箱策略就必须 ctx.sandbox.confine,否则降级而不静默不隔离;没有 seam 时才回退 ctx.shell。取消与超时两条路径都成立。三个图工具、图投影、FTS 搜索与全部 git 读取都走这条链,没有 shell 执行器的部署也能读图。 · 修复 · 「面板显示 git 不可用」其实没修好 —— 0.1.7 改用文件描述符捕获,但宿主自己的 spawner 也会拒绝。现在先试宿主 spawner、够不到再回退文件描述符,临时文件放进工作区(/.gitnexus/plugin-home/tmp),全都失败时结论带上原因(git rev-parse HEAD → EPERM / … → exit 128)。 · 面板「缺少组件」那一行是乱码 —— 自 0.1.7 起就编进了 lib/client.js,现在修好,并加了一套 GBK 反向的编解码护栏。 · gitnexus_delta 不再把 GitNexus 自己写的 AGENTS.md / CLAUDE.md / .claude 当成待索引的改动。 · 上游错误信封不再被吞;空回答不再印成 (未知符号) + 上游 0 · 下游 0。 · 刷新与锚定的竞态:第二次刷新开始时,前一次的锚定不会覆盖新状态。 · manifest 有多处空白 lastCommit 时拒绝写入,而不是猜。 · gitnexus_context 三路参数全空时明确提示需要 name / uid / file_path。 · 发布卫生 .npmrc 与 package-lock.json 锁定 registry.npmjs.org(0.1.7 的 lockfile 31 处全指镜像),并有 4 项测试盯着。 · 验证 npm run check 0、npm test 1026 passed / 0 failed(引擎 708、插件 80、客户端 238)、npm ci --dry-run 通过、npm pack --dry-run 199 文件 / 388 KB(解包 1.44 MB)。真机实测:impact('guardHandler') 297 ms;三个工具 458–728 ms;六路并发全部成功。 · 已知限制 detect_changes 仍未暴露(它在受限宿主里会静默失败);cypher / explain / trace 等留待以后;仓库里的 AGENTS.md 用上游裸名,README 给了对照表;锚定只发生在插件自己发起的那次刷新之后。
数据来自仓库的 GitHub Release,可能与插件实际版本不一致。

README

由 DeepSeek 最新模型翻译生成
dsh-gitnexus-plus Listed on dsh-plugin.org dsh.so install · dsh 0.2.1-rc.2 English · 简体中文 当前版本:0.1.10 · npm · Issues 功能说明 · 安装 · 故障排查 ·上游 GitNexus 文档 一个 DeepSeek Harness 插件:看清 GitNexus 索引覆盖了什么、漏掉了什么,判断值不值得刷新,浏览图谱,读仓库自己的提交图——每个提交的文件和补丁都在里面——打开 GitNexus 自带的 3D 视图看全貌,从侧边栏刷新索引,按关键词搜索索引里的符号,以及索引记录下来的源码文本。 它不 fork GitNexus,不改写它的解析器,也不打包或再分发它。gitnexus 是对等依赖,由你提供。 功能说明 - 刷新闸门(Tier 0)——在提供刷新之前,面板会把工作树与索引记录在 .gitnexus/gitnexus.json 里的 sha256 哈希做比对,报告实际差异:修改 / 新增 / 删除,以及索引自身策略排除的文件。工作树干净时几毫秒就能给出答案,不必启动分析器;按钮会直接说明这一点,而不是跑一次耗时数分钟的重建。
展开完整文档(README 剩余 1 节)
dsh-gitnexus-plus

Listed on dsh-plugin.org dsh.so install · dsh 0.2.1-rc.2

English · 简体中文

当前版本:0.1.10 ·
npm ·
Issues

功能说明 · 安装 · 故障排查 ·上游 GitNexus 文档

一个 DeepSeek Harness 插件:看清 GitNexus 索引覆盖了什么、漏掉了什么,判断值不值得刷新,浏览图谱,读仓库自己的提交图——每个提交的文件和补丁都在里面——打开 GitNexus 自带的 3D 视图看全貌,从侧边栏刷新索引,按关键词搜索索引里的符号,以及索引记录下来的源码文本。

它不 fork GitNexus,不改写它的解析器,也不打包或再分发它。gitnexus 是对等依赖,由你提供。

功能说明

- 刷新闸门(Tier 0)——在提供刷新之前,面板会把工作树与索引记录在 .gitnexus/gitnexus.json 里的 sha256 哈希做比对,报告实际差异:修改 / 新增 / 删除,以及索引自身策略排除的文件。工作树干净时几毫秒就能给出答案,不必启动分析器;按钮会直接说明这一点,而不是跑一次耗时数分钟的重建。只有结论锚定在索引记录的提交上,才会判定为「已是最新」;索引没有记录提交时,退化为把工作树与 HEAD 比对,并说明无法确认,而不是谎称是最新的。
- 图谱视图——在画布上画出索引里的社区,并聚合跨社区边;点击社区可查看其成员符号及彼此关系,点击符号可在对应行打开文件。节点和边的预算由宿主执行,视图会说明省略了什么。当当前社区之外的符号变得重要时,Symbol view 按钮可切换到有界的全符号投影;它按文件顺序显式截断,社区视图仍是结构总览。单击选中社区;Expand 打开居中对话框,投影相同,另带社区侧边栏,不是全屏模式。卡片会推迟拉取,直到滚动到视口附近。
- 3D 视图——完整、可旋转的图谱就是 GitNexus 自带的 Web UI,直接嵌入,而不是重新实现。面板会启动 gitnexus serve(每个插件宿主一个进程,停止插件卸载时)并在覆盖层 iframe 中显示 SPA,旁边有 在新标签页打开 和 停止服务器。启动服务器用的命令行与分析器相同,指向真正存放这个仓库的 GITNEXUS_HOME 注册表,所以 URL 打开的是这个项目,而不是另一个已建索引的项目。启动失败时会显示进程自身的输出——版本不匹配的索引会显示 Trying to read a database file with a different version,而不是一片空白。端口上已有服务器、且它能看见这个仓库时,直接复用它(绝不杀掉)。对话框按可用视口的 86% 打开,有 紧凑 / 标准 / 铺满 三种预设,可从右下角拖拽成任意大小;尺寸记在 localStorage 里,窗口缩小时会自动收窄。端口和嵌入方式可配置,见 webViewPort。
- AI 设置,预填好 — SPA 把服务商设置存在 sessionStorage 里,跨源 iframe 写不进去。所以插件给它提供一个生成的 index.html(经由 GITNEXUS_WEB_DIST),在应用启动前写入 gitnexus-llm-settings,服务商、密钥和模型取自 GITNEXUS_PLUS_AI_PROVIDER / GITNEXUS_PLUS_AI_API_KEY / GITNEXUS_PLUS_AI_MODEL(另有可选的 GITNEXUS_PLUS_AI_BASE_URL,用于 OpenAI 兼容端点、Azure 端点和 Ollama 主机),这样聊天不用每个标签页都粘贴一次密钥。变量没设,对话框保持原样;设了一半或拼错,卡片上会说明(缺少 GITNEXUS_PLUS_AI_MODEL…),服务器照常启动。生成的页面随服务器一起删除,复用的服务器绝不改动,密钥也绝不会进入面板——只有服务商、模型和端点。
- 刷新建议 — 过期判定还会给出 none / incremental / reanalyze / rebuild / init / unknown 之一,这样只比较工作区时不会读成「可以增量刷新」,--skip-git 建的索引会被告知重新分析,而不是给出一次根本跑不了的增量运行。
- 索引覆盖 — 列出 gitnexus analyze 跳过的超大文件,附大小、语言占比和生成/压缩信号。
- 索引新鲜度 — 建索引的提交与 HEAD 对比,分类为 current / behind / diverged / unknown,外加未提交改动数。同一份判定同时供给面板那句话和上面说的工具前缀,所以只有一个判断,而不是三个可能互相打架的判断。
- 版本一致性 — 对比写索引的 GitNexus 命令行(runnerIdentity.cliVersion)和现在安装的命令行。支持范围(^1.6.12 || >=1.6.13-rc.0 <2.0.0)内的不匹配报为 跨线 提示——索引建在稳定线上而命令行是候选版,或反过来——不算错误;只有一方超出范围的一对才是警告。
卡片上写明全部三项事实:范围、写入索引的那一行,以及当前安装的行。
- 进程预算 —— 每张图卡都说明进程检测器被允许做什么。索引的 manifest 记录 processDetection(最大进程数、分支、追踪深度、入口点候选);面板区分三种情况:流数量被预算截断;扫描没有产生流,尽管入口点存在(计数不为零,因为仓库本身没有流);完全没有记录预算(来自旧版分析器的索引——完整性未知,而不是「没有」)。

- 图构成 —— 节点、边、社区、进程、嵌入、每个节点的边数。
- Git 活动 × 索引 —— 最近的提交、作者、分支和改动最多的文件,每个文件标记为 已索引、按设计排除 或 索引中缺失。包括活动 × 索引交叉表、针对已索引提交的基线审查(如果索引没有记录,则用活动窗口起点)、7/14/30 天提交时间线,以及点击行下方展开的逐文件提交时间线(无论来自三个列表中的哪一个)。一次只渲染一个列表——分段控件在基线、改动最多、最近和缺失之间切换,交叉表和作者放在一个展开项后面。标题栏的分支选择运行真正的 git checkout,并针对新分支重新加载报告;它从不 stash,也从不猜测,git 自己的拒绝会原样显示。

- 提交图 —— 时间线画成泳道,而不是一排点:每条并发历史线一列,每列固定颜色,索引判定是点 周围 的细环,而不是点的填充色,指向某个提交的引用(main、origin/main、tag: v0.1.9)作为芯片显示在其列的颜色中。历史用 --all 读取,所以尚未合并的分支有自己的列,而不是被压平成一条直线;悬停一行会加粗其泳道并淡化它交叉的泳道;超过六列时,边栏在每行把折叠的列计为 +N,而不是假装该行很窄。不捆绑绘图库:曲线是三次贝塞尔曲线,在每个点前停止,这就是线条最终不再穿过它们的原因。

- 提交自己的文件 —— 点击提交会展开它触及的文件,带 +n / −n,按 git 的路径顺序,每个提交最多 40 个(上限显示在屏幕上)。点击其中一个文件会内联打开其补丁:同样的 file-diff 路由和逐文件时间线已经使用的同一个渲染器,带旧/新行号、着色添加/删除行和复制按钮。补丁只读取,从不存储——没有任何新内容写入索引。

- 分页,而不是「前几个」 —— 四个活动列表各自保留自己的页(每次点击再加载 25 个),提交时间线一次显示 15 个,其余计数在按钮。切换列表不会重置其他列表,打开某个提交的文件也不会收起你当前所在的页面。

- 索引过期,明说 —— 当校验结论是 behind 时,图谱卡片会在图上方多一句话(落后于哪个提交、改了多少文件、该跑什么命令),gitnexus_impact / gitnexus_context / gitnexus_query 也会在回答顶部重复同一句话。匹配的树什么都不说;无法确认的结论用自己的措辞,不借用「过期」——对一个只是未锚定的索引弹刷新提示,会让人把两者都忽略掉。

- CI 校验 —— gitnexus-coverage 可以让流水线失败,不必等人去看面板:--fail-on-uncovered(分析器会索引索引里没有的文件)和 --fail-on-missing(没有索引,或索引描述的文件已不存在)以 3 退出;旧的 --fail-on-large 因体积跳过仍以 2 退出。两个退出码故意分开——一个是索引自己知道的缺口,另一个意味着索引和检出已经分叉——原因写到 stderr,所以 --json 仍能解析,退出码仍是契约。

- 语言分布 —— 字节分成 GitNexus 能解析的语言、它只能识别的格式,以及其他所有内容。没有 GitNexus 时,内置扩展名映射让分布仍然有用,并标记为 builtin,绝不标成「已解析」。
- 关键词搜索 —— 图谱查询匹配符号的 name / filePath,返回 kind、path 和 line;同一查询还会向索引自己的全文索引(file_fts)要源码文本,所以只出现在文件正文里的词也能找到那个文件。正文命中是文件级的,不带行范围,因为只有图谱知道符号边界,页脚会说明实际跑了哪两种范围。点击符号命中会在该行打开文件,单独的 Copy 按钮复制 path:line。
- 源码回退 —— 图谱助手起不来时(没有可用的 shell 沙箱、DLL/ABI、索引忙),对已索引文件做有界行搜索,返回 mode: "text",而不是假装成图谱。
- 一个工作区里的多个项目 —— 工作区可以是容器(D:\Project 装着 gitnexus-plus/、viewer/,或 geo/ 装着 frontend/、backend/)。面板自己发现工作区、它下面每个已索引项目,以及还没有索引的 git 仓库。默认选中一个,多于一个时在页头提供切换器;选择按工作区记住,未索引的 git 项目可以在那里选中并初始化。
- 无索引可见 —— 没有索引的 git 项目或普通文件夹会显示扫描概览、语言分布和初始化命令,而不是空面板。没有 .git 的文件夹用 --skip-git 索引。
- 维护 — 初始化、增量刷新和全量重建(--force)都通过 DSH 的 shell 执行器运行,不会自行提权。索引报告 FTS 不可用时,修复搜索索引(FTS) 操作会运行 analyze --repair-fts,只修搜索索引,不重新分析仓库。

安装

要求:

- DSH >=0.2.0-rc.2 <0.3.0(已逐项对照桌面宿主 0.2.1-rc.3 检查:shell、webServer、sandboxPolicy 和 tools 服务,sidebar.right.pane.tab 插槽,以及本插件用到的客户端基础组件,都还在)
- Node >=22
- GitNexus >=1.6.0 <2.0.0(对等依赖;本插件不安装它)

dsh plugin add dsh-gitnexus-plus

添加后重启 DSH。面板是右侧边栏的一个标签页:打开侧边栏的 + → 引导页 → GitNexus 索引。

desktop 配置档由 Electron 管理,命令行改不了。在那里请用 Web 侧边栏的插件页。从本地检出安装:

dsh plugin --profile web add <absolute path to this package>

GitNexus 可以由你安装,也可以由面板安装。自定义位置:

GITNEXUS_CLI=/path/to/gitnexus/dist/cli/index.js

获取 GitNexus

推荐自己安装:

npm install -g gitnexus

面板也能通过 DSH 的 shell 安装:

npm install --prefix <DSH_HOME>/gitnexus-plus/gitnexus gitnexus@^1.6.12

这会下载大约 500 MB–1 GB,并且需要 C++ 工具链;没有硬编码任何 registry。首次安装请当作未经验证,失败就退回手动命令。

用法

面板报告当前工作区包含的项目,没有全局仓库列表。它从工作区向下查找:工作区本身、每个已建索引的后代目录,以及没有索引的 git 仓库,都是候选;已建索引的目录或 .git 目录是项目边界。候选多于一个时,标题栏路径会变成切换器,并有一行说明工作区、项目/索引数量和当前路径;未建索引的项目会标注出来。之后的刷新、增量、搜索、图谱和维护都作用于选中的项目。默认值依次是:记住的选择、工作区自己的索引、第一个已建索引的项目、工作区扫描;未建索引的容器停留在工作区扫描,并列出它的 git 项目供初始化。目录必须存在,且不能是文件系统根目录;普通文件夹仍会被扫描,可以用 --skip-git 建索引(没有提交基线,也没有增量刷新)。该路由不提供浏览或列出其他任何路径的方式。

在 DSH 内:

- 工具 gitnexus_coverage:{ repo, includeLanguages? }
- 工具 gitnexus_delta:{ repo } — 刷新会不会改变什么?
- 工具 gitnexus_impact:{ repo, target, direction?, maxDepth?, summaryOnly?, limit?, minConfidence? } — 这个符号变了谁会坏;编辑之前先问它
- 工具 gitnexus_context:{ repo, name?, uid?, file_path?, kind?, include_content? } —— 查看单个符号的定义、调用方、被调用方,以及它参与的流程;名字有歧义时用 uid 指定
- 工具 gitnexus_query:{ repo, search_query, goal?, task_context?, limit?, max_symbols? } —— 向图询问某个概念位于何处;返回流程、流程内的符号,以及名称匹配的定义
- 路由 GET /api/gitnexus/projects?root=<workspace> —— 工作空间里的项目(已索引和未索引的 git 仓库)
- 路由 GET /api/gitnexus/coverage?repo=<project>
- 路由 GET /api/gitnexus/search?repo=<project>&q=<keyword>&limit=20
- 路由 GET /api/gitnexus/file-history?repo=<project>&path=<file>&days=7 —— 单个文件有界的提交时间线
- 路由 GET /api/gitnexus/file-diff?repo=<project>&path=<file>&sha=<commit> —— 单个文件在某次提交中的补丁(7–40 位十六进制 sha,只读,有界);提交的文件列表点开的就是它
- 路由 POST /api/gitnexus/checkout?repo=<project>,请求体 {"branch": "<ref>"} —— 顶栏的分支切换:在项目里执行 git checkout(唯一会写入工作区的路由),随后面板重新加载报告
- 路由 GET /api/gitnexus/delta?repo=<project> —— 与索引做 Tier 0 比对
- 路由 GET /api/gitnexus/graph?repo=<project>&view=communities|community&id=<community> —— 有界的图投影
- 路由 POST/GET/DELETE /api/gitnexus/analyze(唯一会改动索引的入口;force=1 全量重建,mode=repair-fts 只修搜索索引)
- 右侧栏标签页 gitnexus

名称映射。 GitNexus 会往它分析的每个仓库里写一份 AGENTS.md(每次运行都重写),其中按上游 MCP 的写法命名工具。本插件注册时加了 gitnexus_ 前缀,照那份文件字面去找会一无所获。对照如下:

| AGENTS.md 里的写法 | 本插件提供的工具 | 说明 |
| impact({target, direction}) | gitnexus_impact | 参数名相同,另有 maxDepth/summaryOnly/limit/minConfidence |
| context({name}) | gitnexus_context | 光给名字有歧义时,用 uid/file_path/kind |
| query({search_query}) | gitnexus_query | 参数名相同,另有 goal/task_context/limit/max_symbols |
| detect_changes({scope}) | gitnexus_delta | 回答「索引要不要刷新」;暂存区/比对范围的作用域还没接上 |

命令行:

npm run build
node lib/bin/gitnexus-coverage.js <repo-path>
node lib/bin/gitnexus-coverage.js -r material-ui # by registry alias
node lib/bin/gitnexus-coverage.js --all --json # every indexed repository
node lib/bin/gitnexus-coverage.js <repo> --fail-on-large # CI gate, exit 2: a file was skipped for size
node lib/bin/gitnexus-coverage.js <repo> --fail-on-uncovered # CI gate, exit 3: the index is missing files it should hold
node lib/bin/gitnexus-coverage.js <repo> --fail-on-missing # CI gate, exit 3: no index, or one describing deleted files
npm run check
npm test

npm test 会构建并运行引擎、插件和客户端三套测试——在 0.1.10 上是 1254 项检查(引擎 852、插件 81、客户端 321;其中三项引擎检查只在本地索引记录了提交时才运行,所以没有提交的检出会报 849)。其中七项是编码守卫:它把每个可 GBK 编码的字符映射回字节,当一串这样的字符再次被解码成 UTF-8 时就失败——这正是双重解码后的字符串的样子,也是字符黑名单一直漏掉的东西。另外六项是发布卫生检查:.npmrc 和 package-lock.json 必须只指向 registry.npmjs.org(0.1.7 的 lockfile 完全指向一个镜像,而在配置了镜像的机器上跑一次 npm install 就会把它写回去——只有断言能发现),并且 LICENSE 必须只包含 MIT 文本,后面不能附加任何内容(附加内容会让 GitHub 报告 NOASSERTION,每个复制它的目录都会显示「Other」;因此范围说明放在 NOTICE.md 里)。这套测试不再依赖开发者的 D: 盘:格式测试会构建一个临时仓库,真实仓库冒烟测试默认使用当前检出(或 GITNEXUS_SMOKE_REPO),没有索引时跳过。.github/workflows/ci.yml 在 Windows 和 Ubuntu 上用 Node 22 和 24 运行 npm ci、npm run check 和 npm test。

三个图工具如何工作

gitnexus_impact、gitnexus_context 和 gitnexus_query 不重新实现任何遍历:请求会发给 lib/bin/graph-query-helper.js,由它在系统 Node 下加载 GitNexus 自己的 MCP 后端。原生 lbug 插件无法加载进这个宿主(Electron 的 Node,ABI 不同)——图投影已经用了同一条边界。

每次调用启动一个短命子进程,冷启动约 400–600 ms(node、注册表、原生存储、遍历)——和 grep 同一量级。请求体通过临时文件传递,而不是放在 argv 里,因为符号名是调用方提供的文本,可能包含空格、引号或换行。

子进程走宿主的执行接缝,而不是裸 spawn(src/subprocess.ts):当配置档挂载了 ctx.subprocess 时,argv 原样交给系统(没有 shell 解析它),任何沙箱策略都通过 ctx.sandbox.confine 应用——没有 confine 时,插件拒绝在无隔离的情况下启动进程,而不是悄悄绕过隔离。只有 ctx.subprocess 缺失时,才回退到 ctx.shell 和命令字符串。两条路径都会转发工具的 exec.signal,所以取消调用会停掉子进程并回答「this graph query was cancelled」,而不是耗尽整个超时。同一条接缝也让面板能在完全没有 shell 的部署里读取图。

有两样东西绝不截断,因为它们说明答案的可信度有多少:
- risk:LOW/MEDIUM/HIGH/CRITICAL/UNKNOWN。UNKNOWN 不等于「安全」——它表示上游遍历没有解析出任何调用方,也可能是因为调用点无法解析(动态分派、其他语言、未建索引的包)。
- epistemic:exact 或 lower-bound。取 lower-bound 时,列表只是下界。

上游在某些路径上还会输出 boundaries(它自己用文字说明为什么是下界)、causes(按类型统计的缺口计数)和 riskNote(遍历自身的依据)。这些字段有就打印,没有就不编造——在 1.6.12 上实测,impact/context 通常三者都没有,所以插件不会凭空生成,也不会在缺失时丢掉上面两行。

不在索引里的符号,现在会明说。 上游对找不到的目标返回 {"error":"Target 'x' not found", …},外加 risk: UNKNOWN;只打印 UNKNOWN 会被读成「没有东西调用它」。渲染器会原样打印错误,并补一句「该符号不在索引中——请刷新索引后重试」。

列表本身有上限(12 个调用方、6 个进程、12 个定义),但计数始终是真实值,被截断的列表会说明漏掉了多少条。

索引过期时,答案会先说明这一点。 渲染之前,工具会询问面板所用的同一个校验;如果索引落后,就在前面加一句话: text 索引落后:自 99c1e78 起 16 个文件已改(14 改 / 2 增 / 0 删),以下结果可能过期;建议增量刷新。

GitNexus impact · foo …

它是一句话,不是一个标志位,因为文本工具里的 <prefix?> 会被忽略。三条规则保证它有用:工作区干净时不加任何内容;无法确认的判定用自己的措辞(索引新鲜度无法确认…),而不是借用「过期」;gitnexus_delta 不给自己加前缀——它本身就是那个答案。

graphTools: false 会完全撤掉这三个工具,保留 gitnexus_coverage 和 gitnexus_delta,这两个不启动任何进程。

搜索有两种模式:

- graph——常规路径。宿主自己不加载原生 lbug;它在系统 Node 下启动 lib/bin/search-helper.js,把符号名和文件路径与图进行匹配,返回确切的符号和行范围。另外,helper 会向索引自带的 FTS(file_fts)查询源文本,所以一次查询也能找到正文提到该词的文件。正文命中以文件级结果返回,不带行范围,因为只有图知道符号边界。如果建索引时用了 --skip-fts,或者 FTS 扩展加载不了,helper 仍会用名称和路径作答,面板会说明实际跑的是两种范围中的哪一种;图查询会在整个运行失败前不带 FTS 重试一次。
- text——图查询无法启动时的回退。它读取索引清单里列出的文件,逐行匹配,先搜代码再搜文档,并限制文件数/字节数/匹配数。面板把它标为 源码关键词(回退)。

点击一条命中会调用
ctx.sidebarRight.openResourceIn(sessionId, address, { params: { line } }) 可在平台文件查看器中打开该行;没有这个服务时,回退为复制路径。

故障排查

先看面板报告的状态:GitNexus absent、no index、index stale、FTS unavailable。下表覆盖本插件的边界;其余问题归上游 GitNexus 路径负责。

| 现象 | 这里怎么做 | 上游 GitNexus 路径 |
| 面板提示 GitNexus 不存在 | 自行安装,或用面板的安装功能。非标准位置请设置 GITNEXUS_CLI=/path/to/gitnexus/dist/cli/index.js。 | gitnexus/README.md — Requirements、Quick Start |
| 项目没有索引 | 用面板里的 初始化索引,或运行 gitnexus analyze --index-only <repo>。对于不是 git 仓库的文件夹,面板的命令会加上 --skip-git(上游要求如此);这样生成的索引没有提交基线,也不会增量刷新。 | README.md — CLI Reference、RUNBOOK.md — Index out of date |
| 子目录有索引,但切换器里不列出 | 搜索只从当前工作区向下找,不会向上,也不会找同级目录。它会跳过隐藏目录、node_modules 和构建产物,并且有深度、数量、时间限制。确认 <workspace>/<child>/.gitnexus/gitnexus.json;如果索引在跳过的目录或很深的目录里,就把那个目录作为工作区打开。多个项目会出现在顶部切换器中,包括没有索引的 git 项目;搜索被截断时会标注出来。 | 本插件的发现逻辑;没有对应的上游 GitNexus 路径。 |
| 刷新失败、spawn EPERM,或索引一直停在 incremental-in-progress | 面板的命令已经设置 GITNEXUS_MEMORY=off、NODE_OPTIONS=--max-old-space-size=8192、GITNEXUS_HOME=<repo>/.gitnexus/plugin-home 和 --index-only。如果沙箱仍然拒绝,请在你自己的 shell 里运行复制出的命令;插件不会提权。 | RUNBOOK.md — LadybugDB / lock errors、Memory / analyze crashes |
| 刷新失败,报 EPERM … mkdir 或 rmdir '<repo>\.gitnexus\parsedfile-store' | GitNexus 在每次解析开始时删除这个按运行划分的存储,解析过程中解析 worker 又会把它建回来——两个方向都可能被拒绝。原因有两个,0.1.4 都已处理:插件不再预先创建该目录(0.1.3 的 rmdir 就是它),分析器的 shell 调用现在把 repository 作为沙箱工作区根。面板路由没有会话,沙箱于是回退到 DSH 应用目录,而该目录的写权限属于另一个身份——仓库的写授权从未生效。如果仍然失败,说明部署模式是只读:在普通终端里运行复制出来的命令。 | RUNBOOK.md — LadybugDB / lock errors、RUNBOOK.md — Clean slate |
| Windows 报 windows-acl-run: SetNamedSecurityInfoW failed (Win32 5): grantWrite(D:\Program Files\DSH Desktop) | ACL 沙箱未能给 workspace root 授予写权限;D:\Program Files\DSH Desktop 是 DSH 应用目录,不是项目。从 0.1.5 起,搜索/图辅助工具改用 <repo>/.gitnexus 作为策略根,安装器则使用插件前缀;安装失败会返回可复制的命令。如果普通 shell 命令仍以这种方式失败,说明会话工作区本身就是安装目录:请打开一个可写的项目。不要给 Program Files 添加 ACL 授权。 | 本插件的策略根修复;DSH 沙箱本身没有上游 GitNexus 路径。 |
| 增量分析卡住,或数据库报 Maximum database size … has been reached | 使用 全量重建(--force),或按 clean-slate 步骤处理。 | RUNBOOK.md — Clean slate、gitnexus/README.md — Troubleshooting |
| 刷新失败,报 EPERM … open '<repo>\.gitnexus\lbug.init.lock' 或 unable to acquire init lock | 这是临时文件锁,不是权限问题:在报告该问题的机器上检查过 .gitnexus 的 ACL,结果正常。GitNexus 的第一次写入就是这把锁,扫描器、索引器或崩溃的运行都可能持有它。插件会清除过期锁并自动重试一次(运行记录显示 attempts: 2)。如果仍然失败,稍等片刻再重试,或在普通终端里运行复制出来的命令。 | 本插件的重试加上分析器自带的锁文件;没有上游 GitNexus 路径。 |
| 搜索失败,报 A dynamic link library (DLL) initialization routine failed / lbugjs.node | 图搜索在 lib/bin/search-helper.js 里运行,从不在 Electron 宿主里跑。升级后重启 DSH,并让 node 留在 shell 的 PATH 上。如果 helper 完全起不来,搜索会回退到源文本。 | gitnexus/README.md — Troubleshooting |

| Windows search returns very few hits, or the panel warns FTS is unavailable | Read capabilities.fts in .gitnexus/meta.json first: status: "unavailable" is a record written when the index was built, and it can be stale — measured here, the record said unavailable while gitnexus doctor reported Full-text search: available and the extension files were present. 修复搜索索引(FTS) (analyze --repair-fts) rewrites that record and the search indexes without touching the graph; the panel then reports what it actually searched instead of repeating the manifest. Other causes, in order: the index was built with --skip-fts; the repair could not run out of process because the sandbox refuses the spawn (spawn EPERM — run the copied command in a terminal); or the extension genuinely cannot load, which is a missing Windows runtime (VC++ 2015-2022 x64 and OpenSSL 3) — the rarest of the four, and worth confirming rather than assuming. | gitnexus/README.md — Windows: full-text search unavailable, Analyze warns about unavailable FTS |
| The refresh button says 无需刷新 and is disabled | That is the Tier 0 verdict, not a failure: every file the index recorded still hashes the same, so re-running the analyzer would change nothing. 仍然刷新 forces it anyway (useful after a schema or GitNexus upgrade). If the index recorded no commit baseline, the panel does not claim 无需刷新 — it says it cannot confirm and leaves the button enabled. | This plugin's delta engine; no upstream GitNexus path. |
| The gate says 无法确认 instead of 无需刷新 | The index recorded no commit. Since 0.1.8 the plugin writes the current commit back into the manifest's lastCommit (and the registry entry that run used) after a refresh it started itself succeeds, so a refresh under the sandbox ends up saying 已是最新 too. It does not write when the working tree is dirty — recording a commit that excludes those edits would make the gate claim 已是最新 falsely; the panel then says there are N uncommitted changes and to refresh after committing. Three cases still need a terminal: the index was built elsewhere, the directory is not a git repository, or the repository has no commits yet. The write-back only fills an empty field and never overwrites an existing baseline. | This plugin's delta engine plus the analyzer's git access; no upstream GitNexus path. |
| The graph card, or a tool answer, opens with 索引落后 | That sentence is generated from the same gate the refresh button uses: the commit it names is the index's recorded baseline, the count is content hashes that no longer match, and the suggestion is the gate's own none / incremental / reanalyze / rebuild / init. Clear it by refreshing the index (panel or CLI) — committing alone does not make the index fresher, it only stops new files from being counted. Since 0.1.10 it is cached for 5 seconds, so a refresh is reflected almost immediately; an index that recorded no commit says 无法确认 and never borrows this sentence. | This plugin's delta engine and freshness.ts; no upstream GitNexus path. |
| A coverage gate fails the pipeline with exit 3 | Read stderr: 未入索引 means the analyzer would index files the index does not hold (refresh it); 索引缺失 means the target has no .gitnexus/gitnexus.json (initialize it, or drop --fail-on-missing for a repository you only scan); 索引过陈旧 means the index describes paths that no longer exist (refresh or rebuild). Exit 2 is the older --fail-on-large (a file was skipped for size) and is a different problem. --json is unaffected because the reasons go to stderr. | This plugin's coverage-gate.ts; upstream owns which files get skipped. |
| The commit timeline is one column although the repository has branches | The timeline reads git log --all but only within the window you picked (7/14/30 days, at most the 300 most recent commits), so a branch whose commits are all older contributes no column, and a branch that was merged may show only its merge point. One column is also the correct answer for linear history — the width is evidence about the window, not about the repository. Past six columns the folded ones are counted as +N on each row. | This plugin's git log reader; no upstream GitNexus path. |
| The panel says git is unavailable, and the activity/file-history cards are empty | Up to 0.1.7 every git call captured output through a pipe, which a confined host (the DSH sandbox, a Windows service whose children run under a restricted token) refuses with spawn EPERM — read as "git is not installed". Since 0.1.8 every git call writes the child's output to a file and reads it back (src/gitnexus/git-capture.ts); stdout and stderr are captured separately so git's LF will be replaced by CRLF warning cannot leak into a list of file names. The capture tries the host's own spawner first and falls back to the file descriptor when that cannot reach git — and the scratch files live inside the workspace (<repo>/.gitnexus/plugin-home/tmp), because a host process is often the one component that may not write to the system temp directory. When all of it fails, the verdict now names the reason (git rev-parse HEAD → EPERM, … → exit 128): no git, no repository and a refused spawn are three different problems. | This plugin's subprocess capture; no upstream GitNexus path. |
| The graph card is empty, or fails with 404 | A 404 means the host predates /api/gitnexus/graph — restart DSH, not just the page. An empty canvas with a message means the index has no communities yet (an old index), or the store is busy: refresh the index. Nodes are capped by design; the card states how much it left out. | This plugin's host route; graph data itself comes from the GitNexus index. |
| The graph loads for some projects but spins or returns 504 for others | On Windows the ACL sandbox's first workspace grant propagates an inheritable ACE across the whole repository, which can exceed the 25 s helper timeout on a large tree; a project whose grant is already cached is fast. Since 0.1.5 the search/graph helpers root the policy at <repo>/.gitnexus, so the grant covers only the index; restart DSH. If it still fails, confirm .gitnexus/lbug exists and that no analyze is holding the index. | This plugin's sandbox policy root; graph data itself comes from the GitNexus index. |
| The 3D view will not open, or its overlay stays blank | Read the card first: unavailable means the host has no shell executor or the machine has no gitnexus CLI (the card prints the install command); failed carries the child process's own output, where a busy port (EADDRINUSE) and an index written by another CLI version (database file version) are the two common cases. 服务在跑,但索引对不上 means the server on that port does not list this repository in its registry: refresh this repository's index, or stop the server you started yourself. A missing card or a 404 means the host never mounted the route — restart DSH. | This plugin's web-view lifecycle; the SPA is the web/ directory inside the GitNexus package. |
| The AI settings inside the 3D view are still empty | The AI 设置 line on the card names the reason: no GITNEXUS_PLUS_AI_ variables set (export them and restart DSH or press 重新启动), a partial set (缺少 GITNEXUS_PLUS_AI_MODEL / 缺少 GITNEXUS_PLUS_AI_API_KEY), or 外部启动的服务 — that server was not started by the plugin, so its page cannot be seeded and the dialog has to be filled once by hand. The variables are read from the DSH process environment, so a restart is required after changing them. | This plugin's generated page (GITNEXUS_WEB_DIST); the SPA keeps its own settings in sessionStorage. |
| Clicking a search hit does not open a file | The click uses ctx.sidebarRight.openResourceIn; a profile without that service falls back to copying path:line. Restart DSH; if it persists, use the Copy button. | This panel/DSH boundary; no upstream GitNexus path. |
| The file timeline fails with 404/401 | The client hot-reloaded before the host did, so /api/gitnexus/file-history is not mounted yet. Restart DSH (not just refresh the page). | This plugin's host route; no upstream GitNexus path. |
| 切换分支失败 / 分支下拉不出现 | 下拉只在面板拿到报告、且项目有多个本地分支时出现。失败信息是 git 的原话:工作树有未提交改动(Your local changes … would be overwritten)、rebase 进行中、或分支名写错都会拒绝切换;面板不会 stash、不会丢弃改动。切换成功后索引仍是旧分支构建的那份,报告会以「索引不是当前分支构建的」标出,重建是另一个显式动作。dubious ownership 时面板会对该路径重试一次(等价于给这一个目录加 safe.directory,不写全局配置)。 | This plugin's host route; no upstream GitNexus path. |
| Analyze runs out of memory | Keep the panel's NODE_OPTIONS / GITNEXUS_MEMORY settings, or raise the heap manually; the index may need a full rebuild. | gitnexus/README.md — Analysis runs out of memory, RUNBOOK.md — Memory / analyze crashes |
| A large file is missing from the graph | This is what the panel reports: size/share, generated/minified heuristics, binary detection. Raising the cap is an upstream analyze option; this plugin only reports the skip. | gitnexus/README.md — Large files are being skipped |
| Native install fails (@ladybugdb/core, tree-sitter-) | The plugin runs npm install --prefix …; it needs a C++ toolchain and a reachable registry. Run the manual command from Getting GitNexus to see the compiler error. | gitnexus/README.md — Installation fails with native module errors |
| The panel does not appear at all | Check that dsh plugin add succeeded and DSH was restarted; open it from the sidebar's + guide page. For the desktop profile, use the Web Plugins page. | This plugin's install path; no upstream GitNexus path. |

Upstream GitNexus documentation

This README describes the plugin, not GitNexus. GitNexus behavior follows its source docs.
Repository: abhigyanpatwari/GitNexus
(branch main).

| Topic | Path |
| Install, CLI, environment variables, MCP | README.md |
| CLI package, requirements, full Troubleshooting | gitnexus/README.md |
| Index stale, clean slate, locks, memory | RUNBOOK.md |
| Architecture and data flow | ARCHITECTURE.md |
| Tests and guardrails | TESTING.md, GUARDRAILS.md |
| CLI command reference | gitnexus/skills/gitnexus-cli.md |
| Language-specific notes | docs/languages/ |

Local .docs/ notes are gitignored; the links above are the published source of truth.

Maintenance commands

The panel shows and runs more than a bare gitnexus analyze:
powershell
$env:GITNEXUS_MEMORY='off'; $env:NODE_OPTIONS='--max-old-space-size=8192';
$env:GITNEXUS_HOME='<repo>/.gitnexus/plugin-home';
gitnexus analyze --index-only '<repo>'

Add --force for a full rebuild, or --repair-fts alone to rebuild the search indexes
without touching the graph (the panel offers that as 修复搜索索引(FTS) when the index reports FTS as unavailable). A folder without .git also gets --skip-git.

- --skip-git — GitNexus refuses a directory without .git even when its path is explicit; this flag indexes the folder directly. The index then has no commit baseline, no incremental refresh and no Git activity, and the panel says so.

- GITNEXUS_MEMORY=off — stops the CLI from re-spawning itself over piped stdio, which a confined sandbox denies (spawn EPERM).
- NODE_OPTIONS=--max-old-space-size=8192 — supplies the heap that respawn wanted.
- GITNEXUS_HOME — redirects the global registry write into the project, so a failed write to ~/.gitnexus cannot leave the index stuck at incremental-in-progress.
- --index-only — otherwise analyze rewrites AGENTS.md, CLAUDE.md and skill files inside the repository.

--skip-fts is adaptive per index: no flag when the manifest says FTS is available,
and the flag when it is not. skipFts: true can force the flag; there is no config that
forces FTS on, because enabling it is a repair (gitnexus analyze --repair-fts), not an
incremental refresh — so the panel exposes it as its own action, and a repair run never carries --skip-fts (the CLI accepts that pair and exits 0 having done nothing).

AI settings for the embedded web UI

The embedded GitNexus SPA has its own chat, and its AI settings are stored per tab. Export
these in the environment DSH starts from, and the panel seeds them whenever it starts the
server:

| Variable | Meaning |
| GITNEXUS_PLUS_AI_PROVIDER | One of openai, gemini, anthropic, azure-openai, ollama, openrouter, minimax, glm, deepseek. Common aliases are accepted (google, claude, azure, z.ai, open-ai). |
| GITNEXUS_PLUS_AI_API_KEY | The provider key. Not needed for ollama. |
| GITNEXUS_PLUS_AI_MODEL | Model id as the provider spells it, e.g. gpt-4o-mini, gemini-2.0-flash, llama3.2. |
| GITNEXUS_PLUS_AI_BASE_URL | Optional. OpenAI-compatible endpoint, Azure endpoint (required for azure-openai), or Ollama host. |

Nothing is sent over the network to configure this: the page the server hands out seeds
sessionStorage inline, before the SPA's module script runs. With none of the variables
set the dialog is left exactly as it was, and the card says so. A partial set (say,
provider and model but no key) is reported on the card and the server still starts, so a
typo never costs you the 3D view.

License (read before commercial use)

This plugin is MIT (see LICENSE; the scope of that grant, and the part
it does not cover, is in NOTICE.md — LICENSE deliberately holds nothing but the standard text, because a licence file with an appendix is detected as "Other").

GitNexus is PolyForm-Noncommercial-1.0.0: noncommercial use is granted, commercial
use is restricted. gitnexus is a peer; this package does not install, bundle or
redistribute it. But using this plugin's GitNexus-backed features (reading an index,
refreshing it, installing it) puts your use of GitNexus under GitNexus's license;
commercial use needs a separate grant from GitNexus's author:

<https://github.com/abhigyanpatwari/GitNexus/blob/main/LICENSE>

In one line: MIT covers this plugin's code; GitNexus's license covers GitNexus, and using
the related features puts that use under it.

Boundaries

- Coverage and search are read-only; maintenance is a separately confirmed write and
never starts by itself.
- The refresh gate compares content hashes of the files the index recorded. It cannot
see a change the analyzer would make for its own reasons (a schema upgrade, a new
feature mode); 仍然刷新 exists for exactly that case.
- The commit graph is bounded on purpose: a 7/14/30-day window, at most the 300 most
recent commits across all refs, six drawn columns and 40 files per commit. +N and
「仅列出前 40 个文件」 name what was folded; a missing column or file is evidence about the window, not proof that it never existed.
- The staleness sentence is advisory. It never blocks a query, a refresh or a commit, and
it is cached for 5 seconds — so a refresh can be visible slightly before the sentence disappears, and a just-committed change can still be reported for a moment.
- A coverage gate judges only what the analyzer would include. A file it would never index
(no extension, generated, over the size cap) is reported as excluded and does not
trip --fail-on-uncovered; a gate that fired on those would be permanently red.
- The graph view draws communities by default, not every symbol: the overview is the
community graph, and a drill-down shows at most the configured node budget of one
community's members. The Symbol view is a bounded all-symbol projection ordered by
file and explicitly truncated, not a claim of completeness. Both say when they are
truncated.
- The 3D view is a second process, started on demand: it needs the gitnexus CLI on the
machine and, on a host with no shell executor, only the manual command is offered.
Embedding is display-only — the iframe is another origin, so the panel cannot script into it; select things in the SPA itself, or open it in its own tab.
- A server started by hand on the configured port is adopted only when its registry lists
this repository; otherwise the plugin tries the next port rather than killing it. The
card says which case it is in. An adopted server keeps its own page, so the AI settings
cannot be seeded into it — fill the dialog once by hand there.
- The seeded AI settings live in sessionStorage, exactly like hand-entered ones: the
SPA clears them when the tab closes, and the plugin re-seeds them on the next start.
Nothing is written to the repository or to ~/.dsh beyond the generated page, which is
deleted when the server stops.
- The plugin reports facts about a GitNexus index; it does not rewrite the parser or
reimplement the graph database.
- Graph search answers with two scopes: symbol names and file paths from the graph, and
source text from the index's own FTS. Body hits are file-level and carry no line range,
and the footer states which scope ran. The text fallback matches lines and is capped.
- The source fallback only covers files in the index manifest; refresh the index for new
files.
- An index built with --skip-git has no commit baseline: the gate can only say it cannot
confirm, a refresh re-analyses the whole folder, and the Git activity cards do not
appear.
- The workspace search is bounded: 12 levels, 4000 directories and 2 seconds by
default; it skips node_modules, build output and every hidden directory, and does not
follow symlinks. Indexed projects are preferred by default; unindexed git projects are
listed and can be initialized. An indexed or .git directory stops the descent. When a
bound stops the search the panel says it may have missed a project.
- Deeper design/build notes are kept out of the public README; this file stays at the
"what it does" and "how to fix it" level.
预览分享卡片长什么样 ↓Moowningstar/gitnexus-plus
分享 · DeepSeek Harness Hub
Moowningstar/gitnexus-plus

DSH 插件:报告 GitNexus 索引遗漏了什么,包括跳过的大文件、索引覆盖率,以及把 git 活动与索引交叉比对。

回到顶部