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

记忆库路由锚定 j62268781-alt/dsh-hindsight-bank-router

DeepSeek Harnessv0.1.0
j62268781-alt22 天前有提交已提供中文文档扫描:含敏感能力

按远程仓库名统一记忆库,让同一仓库的克隆共享记忆

30.7/100
综合分
1
GitHub 星标
39
npm 周下载
—/5
用户评分

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

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add @j62268781-alt/dsh-hindsight-bank-router
npm 源 · @j62268781-alt/dsh-hindsight-bank-router 的发布者账号已校验与本仓库一致

状态速览

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

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

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

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

✓npm 包@j62268781-alt/dsh-hindsight-bank-router @ 0.1.0
✓Node 引擎要求 >=20 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/13 09:39:25

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

读写/删除本地文件
查看扫描证据(共 7 处,展示前 7 条)
  • 读取本地文件
    index.js:150
    return readFileSync(path, "utf8");
  • 读取本地文件
    index.js:171
    const st = lstatSync(path);
  • 读取本地文件
    index.js:364
    entries = readdirSync(dir, { withFileTypes: true });
  • 读取本地文件
    index.js:428
    entries = readdirSync(root, { withFileTypes: true });
  • 写入本地文件
    index.js:521
    mkdirSync(dirname(path), { recursive: true });
  • 写入本地文件
    index.js:523
    writeFileSync(tmp, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
  • 写入本地文件
    index.js:524
    renameSync(tmp, path);
扫描于 2026/11/2(1 天前) · 范围 入口闭包 + 全包扫描 · 来源 npm 发布包

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

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 按远程仓库名锚定记忆库
  • 同一仓库克隆共享记忆

把 Hindsight 编码插件的 bank 身份锚定到远程仓库名,而不是本地目录名。

README

由 DeepSeek 最新模型翻译生成
dsh-hindsight-bank-router 把 Hindsight 编码插件的 bank 身份锚定到「远程仓库名」而不是「本地目录名」——让同一仓库的每个克隆都共享同一个记忆库 Listed on dsh-plugin.org @vectorize-io/hindsight-coding-agents 给每个仓库一份长期记忆,但 bank 名取自本地目录名:同一个仓库因各人 git clone 得到不同目录名而分叉成互不相通的记忆库,且完全静默。本插件喂给它一份动态生成的 mapPathToBank,把身份锚定到远程仓库名。 - 🎯 锚定远程仓库名:payments-app(本地名)与 mobile-client(远程名)都解析到 git::mobile-client,团队记忆真正汇合 - 🪜 声明式优先级链:用户级工作区文件 → 仓库内 .hindsight.json → git 远程名 → 目录名;显式配置永远最高优先 - 🔐 两把锁才生效:用户级 enableBankRouter 必须显式写 true,加上部署级开关。
展开完整文档(README 剩余 1 节)
dsh-hindsight-bank-router

把 Hindsight 编码插件的 bank 身份锚定到「远程仓库名」而不是「本地目录名」——让同一仓库的每个克隆都共享同一个记忆库

Listed on dsh-plugin.org

@vectorize-io/hindsight-coding-agents 给每个仓库一份长期记忆,但 bank 名取自本地目录名:同一个仓库因各人 git clone 得到不同目录名而分叉成互不相通的记忆库,且完全静默。本插件喂给它一份动态生成的 mapPathToBank,把身份锚定到远程仓库名。

- 🎯 锚定远程仓库名:payments-app(本地名)与 mobile-client(远程名)都解析到 git::mobile-client,团队记忆真正汇合
- 🪜 声明式优先级链:用户级工作区文件 → 仓库内 .hindsight.json → git 远程名 → 目录名;显式配置永远最高优先
- 🔐 两把锁才生效:用户级 enableBankRouter 必须显式写 true,加上部署级开关。任一为假就彻底不动你的配置
- 🛡️ 绝不覆盖你手写的映射:sidecar 状态文件记录"我管理过哪些路径",删除时只动自己写的那些
- 🚫 不 spawn git:直接读 .git/config —— 无子进程、无超时、不会因机器负载失败(上游为此删过一版 spawn 实现,见 #3950)
- 🈶 中文仓库名可用:先 decodeURIComponent 再清理,中文仓库.git → git::中文仓库(顺序反了会变 -E4-B8-AD 乱码)
- 🧯 失败开放:任何异常只记日志,绝不阻断 DSH 启动或会话

目录:它解决什么问题 · 安装 · 配置 · 路由规则 · 安全承诺 · 已知限制 · 更多文档 · License

它解决什么问题

@vectorize-io/hindsight-coding-agents 的 bank 名来自模板 coding-agent::{gitProject},而 {gitProject} 的解析逻辑(源码 core/bank.ts 的 resolveProjectRoot)是:

if (basename(commonDir) === ".git") return { root: dirname(commonDir) };
// {gitProject} = basename(root)

它只读磁盘上的目录名,从不读 remote.origin.url。 后果是同一个仓库因各人 git clone 得到的目录名不同而分叉:

你: git clone <url> → payments-app → coding-agent::payments-app
同事: git clone <url> → mobile-client → coding-agent::mobile-client

两者都"正常工作",记忆却永不交汇,且完全静默——没有任何日志提示你出了问题。

上游把这种静默分叉视为最严重的失败模式(见 git-layout.ts 注释与 issue #3950),但它的防护只覆盖了 linked worktree,没覆盖"同一仓库、不同本地目录名"。

它怎么做

不动 hindsight 插件本身,只喂给它一份动态生成的 mapPathToBank:

DSH 启动 └─ 本插件 apply() ├─ 扫描配置的根目录,找 .git ├─ 直接解析 .git/config 的 remote.origin.url ← 绝不 spawn git
├─ 由远程 URL 推导 bank 名:git::<repo>
└─ 安全合并进 ~/.hindsight/coding-agent.json 的 mapPathToBank ↓ hindsight 插件首次会话时惰性解析 bank → 读到的就是正确的映射

可行性依据(hindsight 源码):readRaw() 每次调用都 readFileSync 重新读文件、没有缓存,而 workspaceFor(root) 是首次会话才构建的。所以只要在同一个进程的加载期写入,就一定先于它的读取。

为什么不 spawn git

上游 git-layout.ts 明确记录了删除 spawn 实现的原因:git rev-parse 带 1000ms 预算,机器繁忙时会超时、进程压力下会 EAGAIN,两种失败都被映射为 null —— 与"不是仓库"无法区分,于是把 worktree 分叉进自己的 bank(#3950)。

直接读 .git/config 是纯文件读取:无子进程、无超时、不会因负载失败。

路由规则:优先级链

插件为每个工作区决定 bank 名,严格按以下顺序,第一个命中者胜出:

① <用户目录>/workspaces/<目录名>.json 的 bankId ← 用户自己控制,最高优先 ② <工作区>/.hindsight.json 的 bankId ← 仓库自带(可关闭)
③ git 仓库 → git::<远程仓库名> ← 锚定客观身份,团队共享
④ git 仓库但无 origin ← 不生成映射,交回默认
⑤ 非 git 目录 → workspace::<目录名> ← 个人记忆(可关闭)

注意 ①② 与 ③⑤ 的关系:显式配置(①②)无论是否 git 仓库都优先——它是「我知道得比你多」的逃生舱。

总览图

扫描根目录 ├─ 有 .hindsight.json? → 用它的 bankId(①②) ├─ 是 git 仓库? → 读 remote.origin.url
│ ├─ 有 origin → git::<远程名> ✅ 锚定远端
│ └─ 无 origin → 不写映射 ⚠️ 交回默认
└─ 非 git 目录? → workspace::<目录名> (routeNonGit 控制)

① 开关:两把锁

路由默认关闭,需要两个开关同时为真:

| 位置 | 字段 | 作用 |
| ~/.hindsight/coding-agent.json | "enableBankRouter": true | 用户级总闸 |
| cordis.patch.yml 的 config | enabled: true | 部署级开关(DSH profile 控制) |

任一为假就彻底不动你的配置。 用户级开关必须显式写 true(不是「缺省即开」),因为它是会改写你配置文件的动作,应当由你主动授权。

② 显式指定:.hindsight.json

在两处查找,用户级优先:

| 位置 | 谁控制 | 优先级 |
| ~/.hindsight/workspaces/<目录名>.json | 你 | 高 |
| <工作区>/.hindsight.json | 仓库 | 低 |

两种字段写法都接受:

{ "bankId": "custom::my-bank" } // 推荐
{ "bank": "custom::my-bank" } // 兼容 hindsight 的 banks.<id>.bank 用词

为什么用户级优先:上游明确反对仓库携带配置——

There is no per-repo opt-in file, for the same reason there is no repo-carried config at all: a cloned repository must not be able to turn memory on.

理由是一个克隆下来的仓库不应能决定你的记忆行为。本插件保留仓库级配置,是为团队随仓库统一 bank 命名的便利,但用户级永远压过它:仓库想改你的 bank,你在用户级写一条就能推翻。

完全不接受该风险时,在 cordis.patch.yml 设 allowRepoConfig: false 关闭仓库级查找。

③ Git 仓库 → 锚定远程仓库名

判定:目录下存在 .git(目录或文件都算——后者是 linked worktree / submodule)。

推导链:

.git 存在 ↓ resolveCommonGitDir() ← worktree 感知,只读文件、不 spawn git ↓ 读 <commonDir>/config ↓ remoteUrlFromConfig() ← 只认 [remote "origin"] 段的 url
↓ bankNameFromRemote(url, "git::")
↓
写入 mapPathToBank[仓库路径] = "git::<仓库名>"

URL → bank 名的完整规则(取 URL 的最后一段路径,而非本地目录名):

| 输入 URL | 处理 | bank 名 |
| https://host/a/b/mobile-client.git | 末段去 .git | git::mobile-client |
| git@github.com:org/repo.git | scp 风格先拆出 path | git::repo |
| ssh://git@host/a/repo | 取 URL pathname | git::repo |
| https://host/a/my%20repo.git | 先解码再把空格转 - | git::my-repo |
| https://host/a/中文仓库.git | 解码后 CJK 原样保留 | git::中文仓库 |

⚠️ 解码顺序很重要:new URL() 会把空格转成 %20、把中文转成 %E4%B8%AD…,因此实现必须先用 decodeURIComponent 解码,再做字符清理。若顺序反了,my repo 会变成 my-20repo、中文仓库名会变成 -E4-B8-AD… 乱码。

字符白名单用 Unicode 属性 [\p{L}\p{N}_.-](而非 \w),中文/日文/韩文仓库名因此能原样保留。空仓库名或清理后为空的情况返回 null(不生成映射)。

实例(本机实测):

~/work/payments-app → git::mobile-client ← 锚定成功
~/work/web-console → git::web-console
~/work/flutter → git::flutter

注意第一条:本地目录叫 payments-app,但远程仓库名是 mobile-client,插件取的是后者。

特殊处理:

- linked worktree —— .git 是文件(内容 gitdir: …/worktrees/<name>)时,读其 commondir 回溯到主仓库。结果:worktree 与主仓库映射到同一个 bank,在哪个分支工作都共享记忆。
- 无 origin 远程地址 —— 不生成映射,只记一条 debug 日志(无 origin 远程地址 —— 保持默认 bank 命名)。没有远程地址就无法推导稳定身份,此时交回 hindsight 默认逻辑(用目录名),而不是猜一个。这符合上游「宁可跳过也不猜」的原则。

④ 非 Git 目录 → workspace::<目录名>

默认接管(routeNonGit: true):为根目录的直接子目录写 workspace::<目录名>。

~/Documents/Qoder → workspace::Qoder

为什么接管而不是放任:hindsight 默认会给它 coding-agent::Qoder,与团队共享的 git::* 混在同一命名空间里,无法一眼区分「这是个人记忆还是团队知识」。用独立前缀把它们分开。

只处理根目录的直接子目录,不递归为任意深度的目录建映射——否则会产出大量无意义的 bank。

关掉它(routeNonGit: false):不生成映射,让这些目录走 hindsight 默认的 coding-agent::<目录名>。

⑤ 为什么这样分工

| | git 仓库 | 非 git 目录 |
| 身份锚点 | 远程 URL(客观、人人一致) | 目录名(主观、因人而异) |
| 团队共享 | ✅ 有意义(同事 clone 同一仓库) | ❌ 无意义(个人工作区) |
| bank 前缀 | git:: | workspace:: |
| 归谁管 | 锚定远端,修正分叉 | 显式接管,命名空间隔离 |

核心判断:git 仓库有「客观身份」可锚定(修掉静默分叉);非 git 目录没有,所以用独立前缀明确标记为个人记忆,而不是假装它有团队身份。

⑥ 一条边界情况

递归扫描时发现仓库就停止向下(return):

~/Documents/
├── my-monorepo/ ← 有 .git,建映射后停止递归 │ └── packages/a/ ← 不再深入(即使它自己也是 git 仓库) └── other/ ← 继续扫描

理由:仓库内嵌套仓库极罕见,且会把扫描成本放大。

⚠️ 已知边界:非 git 目录的 bank 名仍是目录名

这意味着不同人机器上的同名目录会撞到同一个 bank(若都连同一台服务器):

你的 Mac: ~/Documents/output → coding-agent::output
同事 Mac: ~/Documents/output → coding-agent::output ← 撞车

本插件不处理这种情况(它只认 git 仓库)。需要时用 mapPathToBank 手工映射——插件绝不碰你手写的条目(见下方安全承诺)。

安全承诺

这个插件会写你的配置文件,所以约束是它的核心:

| 承诺 | 实现 |
| 绝不覆盖用户手工写的映射 | sidecar state 文件记录「我管理过哪些路径」;删除时只有值仍等于我当初写的值才动手 |
| 无变化不写盘 | 合并结果与现状相同则完全不动文件,避免 mtime 抖动触发文件监视 |
| 失败开放 | 任何异常只记日志,绝不阻断 DSH 启动或会话 |
| 原子写入 | 临时文件 + rename,权限 0600 |
| 配置损坏时不动它 | 配置文件存在但 JSON 解析失败 → 跳过并告警,绝不覆盖 |
| 无身份的仓库不碰 | 找不到 origin 远程地址的仓库不生成映射,保持 hindsight 的默认回退 |
| 非 git 目录不碰 | 扫描时只为 git 仓库生成映射;其余目录一律不介入 |

配置文件里除 mapPathToBank 之外的字段原样保留(apiUrl、serverMode 等)。

安装

前置:先装 Hindsight 编码插件

本插件是它的补充,单独装没有意义:

npx @vectorize-io/hindsight-coding-agents install dsh

或按上游文档用 dsh plugin 安装 @vectorize-io/hindsight-coding-agents。

命令行

npm 包(不用编译)
dsh plugin --profile desktop add @j62268781-alt/dsh-hindsight-bank-router

装好后 cordis.patch.yml 的 dsh.bundle.patch 会自动把本插件挂到 profile 的 bundle 链上,位置在 hindsight 插件之前(先写映射、后解析 bank)。

从源码(开发)

git clone https://github.com/j62268781-alt/dsh-hindsight-bank-router.git
dsh plugin --profile desktop add link:$PWD/dsh-hindsight-bank-router

改完 package.json 或 cordis.patch.yml 后重载:

dsh plugin --profile desktop install

装配顺序说明

本插件声明为 bundle 层,而 hindsight 插件也在 bundle 链上。顺序并不严格必要,但把本插件排在前面让意图更明确:

DSH 启动 └─ bundle 链依次加载 ├─ dsh-hindsight-bank-router ← 先写入 mapPathToBank └─ hindsight-coding-agents ← 首次会话时惰性读取

为什么顺序其实不严格:hindsight 的 readRaw() 每次调用都 readFileSync 重新读文件、没有缓存,而它的 workspaceFor(root) 是首次会话才构建的。
所以只要在同一个进程的加载期写入,就一定先于它的读取。

配置

在 cordis.patch.yml 的 config 里:

| 字段 | 默认 | 说明 |
| enabled | true | 部署级总开关(DSH profile 控制) |
| dryRun | false | 只预览不写盘 |
| roots | [] | 扫描根目录,支持 ~ 展开 |
| maxDepth | 3 | git 子树的递归深度;非 git 的直接子目录总是会被纳入 |
| gitPrefix | "git::" | git 仓库的 bank 前缀 |
| workspacePrefix | "workspace::" | 非 git 目录的 bank 前缀 |
| routeNonGit | true | 是否接管非 git 目录(false = 交给 hindsight 默认) |
| allowRepoConfig | true | 是否读取工作区内的 .hindsight.json |
| userWorkspaceDir | ~/.hindsight/workspaces | 用户级工作区配置目录(优先级高于仓库内配置) |
| configPath | ~/.hindsight/coding-agent.json | 目标配置文件 |
| statePath | ~/.hindsight/.bank-router-state.json | 我方状态文件 |

用户级总闸

在 ~/.hindsight/coding-agent.json 里:

{
"apiUrl": "http://sz-00575123-01/hindsight",
"serverMode": "self-hosted",
"enableBankRouter": true // ← 必须显式写 true 才启用 }

为什么不做成"缺省即开":本插件会改写你的配置文件,这个动作应当由你主动授权,而不是装上就默认生效。

自测

node test-harness.mjs

42 项断言,全程在临时目录运行,绝不触碰 ~/.hindsight/。覆盖:

- 开关闸门(enableBankRouter 未设 true 时彻底不动配置)
- 优先级链(用户级 → 仓库级 → git:: → workspace::,逐条验证)
- 用户级覆盖仓库级(安全承诺:仓库无法自行决定 bank)
- allowRepoConfig: false / routeNonGit: false 的降级行为
- 远程 URL 推导(HTTPS / scp 风格 / 嵌套路径 / 无 .git 后缀)
- URL 编码与 CJK(my%20repo → my-repo、中文仓库名原样保留)
- 扫描发现(worktree、无 origin 仓库、深度限制、node_modules 跳过)
- 合并安全(用户条目存活、我方条目按需清理、用户改写后不再删除)
- 失败开放(不存在的根、ctx 为 null、缺少 inject 方法)
- dryRun 不落盘、配置损坏时不覆盖

已知限制

1. 启动后新建/克隆的仓库,首次会话可能不生效。 本插件在 agent/session-start 会补扫一次,但如果该 workspace root 已被 hindsight 缓存,本次不生效、下次启动 DSH 时生效。这是有意的取舍:宁可延迟一次,也不去改动 hindsight 的内部缓存。

2. bank 改名会"丢"旧记忆。 改名前已积累的记忆在旧 bank 名下(例如 coding-agent::payments-app、team::mobile-client),不会自动迁移。需要时用 hindsight-admin export-bank / import-bank。

3. 仓库级 .hindsight.json 的安全权衡。 上游明确反对仓库携带配置("a cloned repository must not be able to turn memory on")。本插件保留它是为团队统一命名的便利,并用用户级目录始终优先来兜底。完全不接受时设 allowRepoConfig: false。

4. 约定仍需团队统一。 本插件让 bank 名由远程仓库名唯一决定(客观事实,所有人一致),但如果团队成员用不同的 fork 或镜像地址,仍会分叉——此时用 .hindsight.json 显式指定。

5. 非 git 目录的 bank 名仍是目录名。 本插件用 workspace::<目录名> 接管它们,但不同人机器上的同名目录(如 output、Qoder)若都连同一台 Hindsight 服务器会撞到同一个 bank。多人共用服务器时,用 ~/.hindsight/workspaces/<目录名>.json 显式指定区分。

更多文档

- docs/DEPLOY-DSH.md — 在 DeepSeek Harness 上部署 Hindsight 记忆系统的完整教程 (自建服务端 + pgroonga 中文检索 + 本插件 + userId 设置 + 故障排查)
- CHANGELOG.md — 每个版本的用户可见变化,以及为什么这样做
- docs/PUBLISHING.md — 发包到 npm 与上架插件市场的完整流程(维护者)
- 上游背景:Hindsight 编码插件文档 · bank 身份为何不能用目录名(issue #3950)

License

MIT
预览分享卡片长什么样 ↓j62268781-alt/dsh-hindsight-bank-router
分享 · DeepSeek Harness Hub
j62268781-alt/dsh-hindsight-bank-router

按远程仓库名统一记忆库,让同一仓库的克隆共享记忆

这是你的仓库?认领条目

回到顶部