记忆库路由锚定 j62268781-alt/dsh-hindsight-bank-router
DeepSeek Harnessv0.1.0按远程仓库名统一记忆库,让同一仓库的克隆共享记忆
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-routernpm 源 · @j62268781-alt/dsh-hindsight-bank-router 的发布者账号已校验与本仓库一致
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · chat
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 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🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
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预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
j62268781-alt/dsh-hindsight-bank-router
按远程仓库名统一记忆库,让同一仓库的克隆共享记忆
相关插件 · 对话 / 记忆
上下文中枢oceanbase/powercontext
上下文跟着工作走 —— 跨会话、跨智能体、跨人。
★ 1,487已汉化DSH
73
智能体记忆库plastic-labs/honcho
Honcho 是记忆基础设施,用来构建有状态的智能体。这些智能体能理解不断变化的人、智能体、群组、项目和想法。
★ 7,894已汉化DSH
71.7
长程智能体控制面loopx-project/loopx
给 Agent 一个目标,让工作持续向前。
★ 6,358已汉化DSH
71.3
智能体上下文数据库volcengine/OpenViking
给 AI 智能体用的上下文数据库
★ 41,379已汉化DSH
71.3
长效机器人团队Fei2411/DS-Bot
DS Bot 是一个基于 DeepSeek Harness简称 dsh的 bot 插件,插件将 dsh 改造为 bot…
★ 5,084已汉化DSH
71.1
可携带记忆层EverMind-AI/EverOS
为什么选择 EverOS
★ 13,778已汉化DSH
71.1
这是你的仓库?认领条目