npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

dsh-git-push-gate

v1.1.1

Published

Tool-layer gate: refuses agent-initiated git push at tools/pre-execute, before any shell runs. Local commits stay untouched. Matching core lives in the cross-agent shared gate repo, located through GIT_PUSH_GATE_HOME (portable default <homedir>/shared/hoo

Readme

dsh-git-push-gate(DSH 侧适配器)

工具层闸门:agent 发起的 git push 在 shell 执行之前被拦下。本地 git add / git commit 完全不受影响。

本文件只是 DSH 侧适配器的说明书。闸门是跨 agent 共用资产,正本仓库在 共用闸门仓库(默认 <homedir>/shared/hooks/git-push-gate,可用环境变量 GIT_PUSH_GATE_HOME 覆盖), 总览见那里的 README.md。判定核心、回归矩阵、压缩提交脚本、审计日志都在那个仓库里, 不在 DSH 检出内 —— 换 agent、换 DSH 版本都不会动它。

为什么必须在这一层

命令层拦截放在 shell 之后都拦不住:

| 方案 | 实测结果 | |---|---| | pre-push 钩子改写 | 只在远端为空/可快进时有效。本地历史与上游压缩提交必然分叉,git push 是 non-fast-forward,git 在调用 pre-push 之前就拒绝,钩子拿不到机会(本地路径远端与 file:// 远端同样如此)。 | | git config alias.push | 覆盖不了内置命令,实测仍走内置 push。 | | 工具层 tools/pre-execute | 命中即返回 {kind:'deny'},原命令根本不执行。本插件。 |

挂载

走官方 bundle 机制,vendor tgz 实体安装(0 junction)——这不是偏好:link: 依赖会在 pnpm install 重建 node_modules 时被清掉,重启后报 cannot resolve profile bundle(本机 2026-08-15 实撞,见 ops/PATCHES.md 中危 5)。装法一步到位:

pwsh -File <shared-gate-home>\adapters\dsh\install.ps1 -Profile web -AlsoProfile headless

它做的事:

  1. npm pack 源码目录 → $DSH_HOME/profiles/<p>/vendor/dsh-git-push-gate-<ver>.tgz
  2. profile package.json:dependencies["dsh-git-push-gate"] = "file:vendor/dsh-git-push-gate-<ver>.tgz", dsh.profile.bundles 加 "dsh-git-push-gate"
  3. 解包 tgz 到 profiles/<p>/node_modules/dsh-git-push-gate/(实体目录,非 junction)
  4. 重启 app 生效(宿主插件无 HMR:base 的 hmr 行 disabled: true)

改源码后必须重装:node_modules 里是 tgz 解出来的拷贝,不是软链。改完 lib/index.js 重跑一次 adapters\dsh\install.ps1 即可。 判定逻辑不在这个包里(在 <shared-gate-home>/scripts/git-push-gate-core.mjs),所以只改判据时无需重装。

验证挂载:dsh --profile <p> --dump-config EXIT=0 且出现 # == dsh-git-push-gate 行; profiles/<p>/node_modules 的 junction 计数应保持 0。

判定逻辑

判定核心只有一份:<shared-gate-home>/scripts/git-push-gate-core.mjs。DSH 插件与 Claude Code 的 PreToolUse 钩子都 import 它,两侧不会漂移。

改核心必须重启宿主:插件在加载阶段 import 核心并常驻内存,只改文件不重启,会话里仍按旧 核心判定(实测分裂:同一条命令,新起的 node 进程判 HIT,宿主内判 miss)。Claude Code 侧每次 钩子调用都新起进程,改完即生效。

判定是静态、引号感知的命令分析(不调 git、不联网):

  • 顶层按 ; | & && || 换行切段
  • 每段剥掉 env 赋值、& 调用符、透明包装器后取命令字。包装器分三类:通用 (sudo env nice timeout xargs stdbuf …)、本机 CLI 代理(rtk,它把 rtk git push 原样转给 git)、shell 内建(command builtin exec,它们绕过函数与 别名,后面的词才是真跑的);再往下一层是包/脚本启动器(npx npm pnpm yarn bunx bun uv uvx pipx),它们紧跟的 dlx / exec / x / run 只起引导作用
  • 命令字是 git(含 git.exe、绝对路径)→ 跳 -c -C --git-dir 等全局选项, 子命令是 push 即命中
  • 命令字是 shell(sh bash zsh pwsh powershell cmd wsl …)→ 取出它要执行的 载荷(-c / -Command / -lc / /c)后递归分析,深度上限 6

反斜杠在 POSIX shell 与 PowerShell 里含义不同,所以分析跑两遍,严的一遍胜出:

  • 第一遍:反斜杠后面是普通字母时保留字面,Windows 路径分隔符不被吃掉 (D:\Tools\Git\cmd\git.exe push 认得出 git)
  • 第二遍:把所有 \x 折成 x,即 POSIX shell 的解释 (\g\i\t push origin master 一并认出)

覆盖:git push、git -c <any> push、git -C <dir> push、--force / --force-with-lease、 cd x && git push、a; git push、pwsh -Command "git push"、bash -lc 'git push'、 cmd /c "git push"、嵌套 shell、"C:\Program Files\Git\cmd\git.exe" push、 & "C:\...\git.exe" push、D:\Tools\Git\cmd\git.exe push、\g\i\t push、 rtk git push、rtk.exe git push、"rtk" git push、rtk git -C <dir> push、 command git push、builtin git push、exec git push、npx git push、 npm exec git push、pnpm dlx git push、yarn dlx git push、bun x git push、 uvx git push、uv run git push、pipx run git push、 timeout 5 git push、timeout -k 5 10 git push、timeout -s KILL 5 git push、 timeout --foreground 5 git push、timeout 0.5s git push、command -p git push、 builtin -p git push、exec -a <name> git push、xargs -I {} git push、 sudo -u <user> git push、env -u <var> git push、stdbuf -o L git push。

包装器的接值选项按包装器分表:同一面 -p 在 sudo 那里接值(--prompt),在 command 那里是纯开关;exec -a <name> 接值。timeout 除选项外还吃 1 个裸位置参数(duration), 所以 timeout 5 git push 剥完词头是 git 而不是 5。未单列的包装器回落共享表。

不误伤:git fetch / log / status / commit / add / diff / ls-remote / config / commit-tree 一律放行;git commit -m "explain git push" 这类引号里的字面量 不命中(因为引号内容只在命令字是 shell 时才递归分析)。包装器剥完后命令字不是 git 的同样 放行:rtk status、rtk log、rtk git status、npx create-react-app、pnpm install、 uv run pytest。同一批包装器带非 push 子命令的也放行:timeout 5 ls、 timeout 5 git status、timeout 5 git log --oneline -5、timeout 5(根本没有命令)、 command -p git status、command -p ls、command -v git、command -V git、exec ls、 exec -c ls、exec -a foo ls、nice -n 10 git status、sudo -u root git status、 env -u FOO git status、stdbuf -o L git status。

git push --dry-run / -n:放行,并在审计日志写 allow-dry-run。 git push --help:放行,写 allow-help。

回归矩阵:node <shared-gate-home>/scripts/git-push-gate-selftest.mjs(117 例,含反例)。

配置

cordis 行配置(cordis.patch.yml 里那行加 config:):

  • mode: deny(默认)—— 拒绝 + 指路,原命令不执行
  • mode: squash —— 拒绝,但代跑 publish.ps1 生成压缩提交,回报生成的提交 sha 与推送命令
  • mode: off —— 放行,但仍写审计(bypass-mode-off)
  • allowDryRun: true(默认)
  • onAnalyzeError: deny(默认)—— 判定器抛异常时保守拦下(见「失败模式」); 设 allow 才放行,属显式选择
  • onCoreUnavailable: regex(默认)—— 判定核心整个文件加载不到时的兜底(见「失败模式」); deny-all 一律拒,allow 不挂钩子(显式选择,等于关掉闸门)

审计

每次命中写一行 JSONL 到 <shared-gate-home>/logs/gate-audit.log(可用 GIT_PUSH_GATE_AUDIT_LOG 覆盖):

{"ts":"…","source":"dsh","tool":"pwsh","decision":"deny","rule":"git-push","cwd":"…","cmd":"git push origin master"}

source 区分 dsh / claude。decision ∈ deny / allow-dry-run / allow-help / bypass-mode-off / deny+squash / deny+squash-noop / deny+squash-failed / deny+analyze-error / core-missing。写日志失败不影响判定。

失败模式(刻意选边)

  • 判定核心 git-push-gate-core.mjs 丢失或 import 失败 → 插件仍然挂 pre-execute,改用 保守正则兜底(FALLBACK_PUSH_RE,与 Claude 侧钩子同一份口径;这条例正则有三个字面量, 见根 README「一处要改三处的地方」): 明显像 push 就拒,其余放行。启动时打印 [git-push-gate] DEGRADED (regex) …,审计逐条写 decision=core-missing / rule=fallback / outcome=deny|allow。 这条是粗网:只判"像不像 push",所以降级态下 git push --dry-run 也会被拒(正常态放行)。 匹配前会先去引号(replace(/["']/g, ''))再套正则:shell 本来就去引号,所以 git "push" origin master / git 'push' origin master / git pu"sh" 都是真推送, 不去引号的网会整条漏掉。代价只有一个方向 —— 这张网只会更严,不会更松:
    • 误拒(正常态放行、降级态拒):git push --dry-run、git push --help、 echo git push origin master、git commit -m "docs: explain push" —— 正则只认"像 git … push 的样子",不问它是不是真在推。
    • 已无「漏判」:去引号后,带引号与不带引号的子命令走同一条判定。 换句话说,降级态不会放过正常态会拦的东西;但它仍然是判「像不像」,不是判「是不是」。 配置 onCoreUnavailable: regex(默认)/ deny-all(连 echo 都拒)/ allow(不挂钩子)。 默认刻意不做 deny-all:核心被误删时全拦会让 agent 连 echo 都跑不了。
  • 判定抛异常(核心在,但分析器炸了)→ 失败关闭:返回 deny(文案 已拦下:git push 闸门判定器异常,保守拒绝本次命令), 审计写 deny+analyze-error。判定器有解析 bug 时不静默漏过;确需放行得显式配 onAnalyzeError: allow 并重启宿主。两侧口径一致:核心出问题时本侧不比 Claude 侧松。
  • 本闸门管的是"配了它的 agent"。用户手敲终端不受管;挡的是 agent 的顺手与故意,不是"任何人"。
  • 硬边界仍是凭证:能推上游的凭证不在 agent 手里。闸门 + 凭证两层,缺一不可。
  • 静态匹配覆盖不到的路径(已知,未拦):git p 这类展开成 push 的别名;git push 字符串 经其它语言运行时二次解释(如 python -c "subprocess.run(['git','push'])");从别的机器/别的 工具推送。这些属于"凭证边界"该管的部分。

相关

  • <shared-gate-home>/scripts/git-push-gate-core.mjs —— 判定核心(单一正本)
  • <shared-gate-home>/scripts/git-push-gate-selftest.mjs —— 117 例回归矩阵
  • <shared-gate-home>/scripts/publish.ps1 —— 生成唯一一条压缩提交(纯 plumbing,不推送)
  • ~/.claude/hooks/git-push-gate.mjs —— Claude Code 侧 PreToolUse 钩子
  • <shared-gate-home>/adapters/dsh/test/pre-execute.test.mjs —— 插件契约测试(假 ctx 驱动真事件)