bitey30/foreshock

GitHub: bitey30/foreshock

一个本地 AI 编程 agent hook,在文件编辑时实时分析并向 agent 注入改动的影响范围、依赖关系和测试覆盖等上下文,帮助 agent 在继续操作前主动修复连锁反应。

Stars: 1 | Forks: 0

foreshock

赋予编程 agent 边缘视野——在崩溃前感知震动。

MIT License Python 3 Zero dependencies Languages Local only

**简而言之:** foreshock 是一个微型的本地 hook,当 AI 编程 agent 编辑文件时,它会立刻告诉 agent 这次改动还会影响*什么*——*谁依赖它、哪些测试覆盖了它、compiler 无法捕获什么*——从而让 agent 在继续操作之前修复这些连锁反应。 当编程 agent 编辑文件时,它的思考是*局部的*——一个函数、一个 signature——然后就继续下一步,对它刚刚带来的风险视而不见。**foreshock 赋予了它边缘视野。** **一个具体的例子。** 一个 agent 重命名了一个函数,更新了它能看到的一个调用点,然后继续执行——却没有意识到该函数被其他 14 个文件导入,其中 3 个现在会出错。人类在一天后的 review 中发现了它。而 foreshock *在编辑的同时*就捕获了它:当 agent 碰到那个函数的瞬间,它看到*“影响范围:14 个文件 `[SHARED-CORE]`,这里是 3 个导入了被修改 symbol 的文件,这里是覆盖它的测试。”* 所以它现在就修复所有问题——或者,在开启 deep 模式下,它能在应用之前就看到**该改动将导致的实际 compiler 错误。** 它作为一个 hook 运行在 agent 循环(Claude Code / Cursor / Codex)**内部**。默认情况下,它**每次编辑触发一次**——在改动*之前*提供预览(*“此改动将会……”*);编辑后的确认是可选的(`FORESHOCK_CONFIRM=1`),因此它绝不会在 agent 的 context 中成倍增加占用空间。它不是 linter,不是 dashboard,也不是提交后的 PR 报告——而是一个在 *bug 出现之前*供 agent 使用的 context 层。它是**本地且无依赖的**(纯 Python stdlib,无网络),并且在无关紧要的编辑上保持**静默**,因此它提供的是信号,而不是噪声。 **它的闪光之处:大型、重度依赖的 codebase——修改现有代码。** foreshock 在**具有大量依赖的复杂项目**中表现最为出色,尤其是当 agent **修改既有代码**时——refactor、重命名、signature 或 schema 变更——因为这正是看似局部的小型编辑在过于庞大而无法装进 agent 脑中的 codebase 中引发连锁反应的场景。**codebase 越大、内部互联越紧密,foreshock 能提供的价值就越大**——它的价值随依赖图的增长而增长。相反,**全新的或 greenfield 项目可能还不需要 foreshock**:代码很少且没有任何依赖关系时,几乎不会产生需要呈现的连锁反应,因此它会保持安静——而那些 agent 已经能完全掌握的小型或简单项目,也无法从中获得太多收益。这是设计使然,而非缺陷:foreshock 只有在 codebase 积累了真实的依赖关系后才能体现其价值。

foreshock detecting blast radius live during an agent edit to Flask's url_for

实时捕获——一个 agent 为 Flask 的 url_for 添加了一个参数;foreshock 捕获到这个看似局部的编辑实际上是一个涉及 33 个文件的 SHARED-CORE 核心改动,并返回了要运行的测试。

## 为什么 这个领域中的其他所有工具都是在 diff 编写**之后**才去查看它——PR review、dashboard 或报告。到那时,agent 已经又往前走了三步了。foreshock 的赌注在于,真正有用的时机是在编辑**期间**:告诉 agent 它的改动比看起来要大,*趁着它还能采取行动的时候*。主动的、循环内的、单一目的的。 ## 你能得到什么 —— 数据包 对于每一次编辑,引擎都会发出一个**具备 diff 意识、symbol 级别**的数据包,描述该改动的*后果*,而不是原始的文件计数。一个数据包的内容如下: ``` foreshock — preview: this change to src/auth/session.ts would… • API change: ~validateToken (declaration) • blast radius: 11 file(s) import this [SHARED-CORE] • who imports this: → src/api/guard.ts (validateToken) → src/api/login.ts (validateToken) → src/api/logout.ts (validateToken) → src/api/middleware.ts (validateToken) → src/api/refresh.ts (validateToken) src/util/a.ts (decode) src/util/b.ts (decode) src/util/c.ts (decode) … (+3 more) • → = imports a CHANGED symbol — re-check those call sites • covered by tests: src/auth/session.test.ts ``` 逐行说明: - **公共接口发生了什么变化** —— `API change: +foo` / `~bar (declaration)` 对比 `content-only: changed the body of X, import contract intact`。(从编辑操作的前后字符串中重建。) - **谁导入了这个** —— 每个依赖项都标有它拉取的 symbol,并使用 **`→`** 标记那些导入了*被修改的* symbol 的依赖项。消除了“49 个文件但只有 2 个受到影响”的噪音。 - **由测试覆盖** —— 执行被编辑模块的测试文件。 - **变体 / 完整性** —— *“你向 `Foo` 集合添加了 `bar` —— 请在 ⟨compiler 不会标记的 dispatch 站点⟩ 处理新情况”*(TS 字符串联合类型、Python `Enum`/`Literal`、Java/C# `enum`、Go 类型化 `const`/`iota`)。 “谁导入了这个”列表是经过**排序的**——导入了*被修改的* symbol 的文件(`→`)排在前面,因此重要的文件能在截断后保留下来。至关重要的是,对于局部的、零依赖的、非 API 的编辑,它会保持**静默**。是信号,而不是噪音。 ## 预览、深度模拟与框架 - **在触碰之前进行预览。** 在 `PreToolUse` 时,foreshock 会根据提议的改动投射编辑效果,并展示它*将*做什么——*“预览:此改动将会…… API 变更:+sum;−add;影响范围:33 [SHARED-CORE]”*——这样 agent 就可以在写入任何内容之前进行调整。 - **深度模拟(可选)。** 设置 `FORESHOCK_DEEP=1`,预览就会在一个隔离的副本上运行项目的**真实**检查器,并且仅报告该改动*引入的*诊断信息——例如 `src/calc.ts(1,10): error TS2305: Module './math' has no exported member 'add'.`——这一切发生在改动生效之前。你的文件永远不会被触碰。(tsc · mypy → pyflakes → py_compile · javac · go build · ruby -c。) - **框架边缘情况。** Adapter 会恢复 import 图无法看到的耦合——例如 Django adapter 会链接那些没有 `import` 的 `ForeignKey("app.Model")` 字符串引用。(Next.js / Rails 支持即将推出。) 除了一个默认的预览数据包之外的所有内容**都是关闭的,除非你主动开启**(保持 agent 的 context 精简): | env flag | 效果 | |---|---| | `FORESHOCK_CONFIRM=1` | 同时输出编辑后的*确认*数据包(默认仅输出预览) | | `FORESHOCK_DEEP=1` | 在隔离的副本上运行项目的真实检查器;仅报告*新增*的错误 | | `FORESHOCK_SQL=1` | 启用 SQL schema 插件(tables/columns、FK 边、`CHECK` 变体) | | `FORESHOCK_RATE=1` | 追加一个 1-5 的实用性评分提示 + 会话结束时的 review | 详情见 [docs/USAGE.md](docs/USAGE.md)。 ## 快速开始 ``` git clone https://github.com/bitey30/foreshock && cd foreshock ./engine/install.sh # installs into ~/.claude/hooks; default = one preview packet per edit ``` 重启 Claude Code 并编辑一个被其他文件导入的文件——该数据包将出现在 agent 的下一轮对话中。 该 hook 会**自动寻根**到每个被编辑文件所在的 repo,因此一次全局安装即可适用于所有项目。它安装在**用户级别**——适用于每个 repo、每次会话——并且具有**自我修复**能力:如果 `~/.claude/settings.json` 丢失了这些条目,`SessionStart` hook 会重新注册 foreshock,因此它绝不会失效。 你也可以手动运行它: ``` python3 engine/impact_engine.py # repo map: blast-radius hot spots python3 engine/impact_engine.py --file src/x.ts # context packet for one file python3 selftest.py # deterministic plumbing checks ``` `selftest.py` 断言的是*事实*——hook 是否能在每种语言上触发、导入是否能解析、变体是否能解析、缓存是否具有确定性——而**不是**数据包的措辞。它刻意不固定输出:foreshock 辅助 AI 应对截然不同的 codebase,因此数据包是否*有用*是评分/评估循环([`FORESHOCK_RATE`](docs/USAGE.md))的工作,而不是 golden-file 测试的工作。 **完整的设置、数据包结构和故障排除 → [docs/USAGE.md](docs/USAGE.md)** ## 工作原理 **一个与语言无关的核心 + 每种语言一个插件。** `impact_engine.py` 负责 import 图、影响范围和数据包;每个 `lang_*.py` 负责其语言的导入、解析、导出和变体类型。**添加一种语言本质上就是添加一个文件**——放入一个 `lang_*.py` 并注册它(在插件列表中加一行,并在 hook 中加上它的扩展名即可)。 | | | |---|---| | `impact_engine.py` | 图 · 传递依赖项 · diff 重建 · 数据包 | | `lang_ts.py` | TS/JS —— `import`/`export … from`、barrels、动态 `import()`、`require()`、ts/jsconfig 路径别名、字符串字面量联合类型 | | `lang_python.py` | Python —— 绝对 + 相对导入、`sys.path` 解析、`def`/`class`/常量导出、`Enum` + `Literal[…]` | | `lang_java.py` | Java —— package→FQCN 解析(`import a.b.C;`、静态、通配符)、公共类型/方法导出、`enum` | | `lang_go.py` | Go —— 以包作为目录的导入(`module/sub` → 该包中的所有文件)、首字母大写的导出、类型化 `const`/`iota` 变体组 | | `lang_ruby.py` | Ruby —— `require_relative`/`require`、class/module/`def` 接口(无 enum) | | `lang_csharp.py` | C# —— `using` 命名空间导入(一个命名空间跨越多个文件)、公共类型/方法导出、`enum` 变体 | | `lang_sql.py` | SQL —— *可选*(`FORESHOCK_SQL=1`)。表 + 限定的 `table.column` 作为 symbol,FK/`FROM`/`JOIN` 作为边,`CHECK (col IN …)` 作为变体。精确:仅当被限定时才计算列 | | `framework_django.py` | Django adapter —— 恢复 import 图无法看到的字符串-FK 模型耦合(`ForeignKey("app.Model")`) | | `impact_hook.py` | hook —— 将编辑 payload 传递给引擎,并注入数据包(默认预览;可选确认) | | `deep_check.py` · `foreshock_*.py` | Tier-3 深度模拟;可选的 1-5 评分 + 会话结束 review;`SessionStart` 自我修复 | **纯本地,就像一个库。** 纯 Python 标准库——无依赖、无需 API key、无需账户、无网络。它作为本地子进程运行,读取一个 repo,将数据包打印回你自己的 agent context 中。不收集任何内容,不会有任何数据离开你的机器,完全离线工作。 ## 客观适用范围 foreshock 读取的是** import 形态的**耦合。它在 import *就是*耦合的地方(库、SDK、共享模块)表现强劲,而在依赖约定或运行时耦合的地方(例如由文件约定连接的 Next.js 应用路由)表现较弱。**框架 Adapter**(目前支持 Django)开始恢复那种应用耦合,但仍处于早期阶段。解析是基于 regex 的,而不是完整的 compiler 前端,因此异乎寻常的重导出 / 反射 / 动态分发可能会漏网——而且每种语言的 symbol 颗粒度各不相同(Ruby/C# 在文件/命名空间级别进行解析,因此它们显示的是*谁*依赖,而没有按 symbol 划分的 `→`)。 它是一个 **context 层,而不是保证**——是一个提示你去查看的信号,而不是证明你已经找到了一切。关于它在哪里失效(以及为什么它是一个库工具而不是应用工具)的诚实记录位于 [`experiments/bugcatch-deviation/`](experiments/bugcatch-deviation/)。 ## License [MIT](LICENSE)。
标签:AI编程助手, JS文件枚举, SOC Prime, 云安全监控, 代码分析, 代码审查, 凭证管理, 多线程, 开发工具, 日志审计, 静态分析