mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-08-28 08:35:57 +00:00
feat: add hashline editing and TypeScript LSP
This commit is contained in:
@@ -14,14 +14,15 @@
|
|||||||
- 该目录从上游源码仓库导入并由本仓库直接维护,不使用 npm 预编译产物,不是 submodule,也不保留嵌套 `.git`。
|
- 该目录从上游源码仓库导入并由本仓库直接维护,不使用 npm 预编译产物,不是 submodule,也不保留嵌套 `.git`。
|
||||||
- `extensions/fff-override.ts`:强制使用 FFF 官方 `override` 模式,统一接管 `find`、`grep`、`multi_grep` 和 FFF 的 `@` 补全;显式 CLI flag 仍遵循 FFF 官方优先级。
|
- `extensions/fff-override.ts`:强制使用 FFF 官方 `override` 模式,统一接管 `find`、`grep`、`multi_grep` 和 FFF 的 `@` 补全;显式 CLI flag 仍遵循 FFF 官方优先级。
|
||||||
- `context-mode`:加载上游 Pi adapter 与 skills,提供 `ctx_*` 工具、隔离式大输出处理和会话连续性。
|
- `context-mode`:加载上游 Pi adapter 与 skills,提供 `ctx_*` 工具、隔离式大输出处理和会话连续性。
|
||||||
|
- `extensions/hashline.ts`:部署组合包权威 `config/pi-hashline-edit.json` 后加载 `pi-hashline-edit`,以行哈希锚点覆盖内置 `read` / `edit`;Hashline 自带 `grep` 强制关闭。
|
||||||
- `extensions/codegraph.ts`:通过隔离的 `pi-mcp-adapter` 配置连接机器现有的 `codegraph serve --mcp`,只暴露 `codegraph_explore`。
|
- `extensions/codegraph.ts`:通过隔离的 `pi-mcp-adapter` 配置连接机器现有的 `codegraph serve --mcp`,只暴露 `codegraph_explore`。
|
||||||
- `extensions/permission-system.ts`:在权限扩展注册前,将 `config/pi-permission-system.json` 同步为全局权威配置。
|
- `extensions/permission-system.ts`:在权限扩展注册前,将 `config/pi-permission-system.json` 同步为全局权威配置。
|
||||||
- `extensions/lsp.ts`:部署 Kotlin/JDT LS 全局配置并加载 `pi-lsp`。
|
- `extensions/lsp.ts`:把组合包内 TypeScript Language Server CLI 物化为绝对命令路径,部署 TypeScript/Kotlin/JDT LS 全局配置并加载 `pi-lsp`。
|
||||||
- `extensions/tool-routing.ts`:保留 Pi 默认系统提示词,在每轮开始前按激活工具追加简短路由规则,并提供 `/dump-system-prompt` 将扩展所见的有效提示词写入 `.pi-debug/effective-system-prompt.md`。
|
- `extensions/tool-routing.ts`:保留 Pi 默认系统提示词,在每轮开始前按激活工具追加简短路由规则,并提供 `/dump-system-prompt` 将扩展所见的有效提示词写入 `.pi-debug/effective-system-prompt.md`。
|
||||||
- `pi-lsp@0.1.7`:提供声明式 LSP 接入;组合包配置 `kotlin-lsp --stdio` 与 `jdtls`。
|
- `pi-lsp@0.1.7`:提供声明式 LSP 接入;组合包内置 `typescript-language-server@5.3.0` + `typescript@6.0.3`,并配置机器级 `kotlin-lsp --stdio` 与 `jdtls`。
|
||||||
- `pi-hermes-memory@0.9.6`:提供持久记忆、会话搜索、后台学习和 secret scanning,默认使用 policy-only 模式。
|
- `pi-hermes-memory@0.9.6`:提供持久记忆、会话搜索、后台学习和 secret scanning,默认使用 policy-only 模式。
|
||||||
- `@ogulcancelik/pi-codex-compaction@0.1.3`:为 `openai-codex` 提供原生远程 compaction,默认阈值为 90%。
|
- `@ogulcancelik/pi-codex-compaction@0.1.3`:为 `openai-codex` 提供原生远程 compaction,默认阈值为 90%。
|
||||||
- 根包还固定安装 `pi-context-view`、`@firstpick/pi-extension-codex-fast-mode` 和 `@gotgenes/pi-permission-system`。
|
- 根包还固定安装 `pi-hashline-edit@0.8.3`、`typescript-language-server@5.3.0`、`typescript@6.0.3`、`pi-context-view`、`@firstpick/pi-extension-codex-fast-mode` 和 `@gotgenes/pi-permission-system`。
|
||||||
- `install.sh`:先安装根组合包,再交互检查 Kitty/Solarized Dark、Oh My Zsh/Powerlevel10k/Zsh 插件、CodeGraph、Kotlin LSP、Java 21+ 和 JDT LS;缺失项目只在用户明确选择 `Y` 后安装或配置。Powerlevel10k 默认配置来自仓库内置的 `config/p10k.zsh`(当前 Rainbow/ASCII 单行紧凑主题)。
|
- `install.sh`:先安装根组合包,再交互检查 Kitty/Solarized Dark、Oh My Zsh/Powerlevel10k/Zsh 插件、CodeGraph、Kotlin LSP、Java 21+ 和 JDT LS;缺失项目只在用户明确选择 `Y` 后安装或配置。Powerlevel10k 默认配置来自仓库内置的 `config/p10k.zsh`(当前 Rainbow/ASCII 单行紧凑主题)。
|
||||||
- `update.sh`:先通过 `pi update` 升级根组合包,再只升级当前已安装的终端环境和机器级依赖;未安装项直接跳过。升级前先查询并比较本地与远端版本,只有版本不同时才下载或替换;Powerlevel10k 配置优先从 `pi update` 后的已安装组合包读取,并与 `.zshrc` 受管块一起按内容比较后增量同步。
|
- `update.sh`:先通过 `pi update` 升级根组合包,再只升级当前已安装的终端环境和机器级依赖;未安装项直接跳过。升级前先查询并比较本地与远端版本,只有版本不同时才下载或替换;Powerlevel10k 配置优先从 `pi update` 后的已安装组合包读取,并与 `.zshrc` 受管块一起按内容比较后增量同步。
|
||||||
- `uninstall.sh`:只移除根组合包,不卸载或还原可能被其他项目共享的终端环境、CodeGraph、Kotlin LSP、JDT LS 或 Java。
|
- `uninstall.sh`:只移除根组合包,不卸载或还原可能被其他项目共享的终端环境、CodeGraph、Kotlin LSP、JDT LS 或 Java。
|
||||||
@@ -34,7 +35,8 @@
|
|||||||
- `readCompaction.enabled`、`sourceCodeFilteringEnabled` 和 `smartTruncate.enabled` 当前均默认 `false`,源码读取保持原样。未经用户明确决定,不因节省上下文而改变这些默认值。
|
- `readCompaction.enabled`、`sourceCodeFilteringEnabled` 和 `smartTruncate.enabled` 当前均默认 `false`,源码读取保持原样。未经用户明确决定,不因节省上下文而改变这些默认值。
|
||||||
- 若以后开启 read 压缩,优先考虑 `readCompaction + smartTruncate`,源码过滤仍独立评估;必须保留精确 `offset/limit` 读取、短文件和行锚点的完整性。
|
- 若以后开启 read 压缩,优先考虑 `readCompaction + smartTruncate`,源码过滤仍独立评估;必须保留精确 `offset/limit` 读取、短文件和行锚点的完整性。
|
||||||
- Context Mode 负责避免批量读取、命令研究和网页原始内容直接撑大上下文;FFF 仍负责精确字面搜索,RTK 仍处理未走 Context Mode 的普通输出。
|
- Context Mode 负责避免批量读取、命令研究和网页原始内容直接撑大上下文;FFF 仍负责精确字面搜索,RTK 仍处理未走 Context Mode 的普通输出。
|
||||||
- 工具路由规则不得替换 Pi 默认系统提示词:代码结构与符号关系优先 CodeGraph;字面搜索先用 FFF `find` 收敛文件位置,再在收敛路径内用 `grep`/`multi_grep` 获取行号,最后才按需精确 `read`。遇到大量结果、截断或上限时继续缩小 path/glob/pattern,不得靠提高 limit 或倾倒全部结果解决。
|
- Hashline 默认以 2 字符行哈希覆盖内置 `read` / `edit`,`grep` 和 `replaceText` 均关闭;它保证锚定编辑而不负责压缩,大文件上下文节省必须来自先定位/隔离分析、再做最小范围 `offset/limit` 读取。
|
||||||
|
- 工具路由规则不得替换 Pi 默认系统提示词:代码结构、调用关系和待修改 symbol 优先 CodeGraph,定义/引用/类型/诊断优先 LSP;工作区内大文件探索、分析和总结优先 `ctx_execute_file`,日志、构建与不可预测命令输出优先 Context Mode;字面搜索先用 FFF `find` 收敛文件位置,再在收敛路径内用 `grep`/`multi_grep` 获取行号;真正修改前才用小范围 Hashline `read` 获取新鲜锚点并用锚定 `edit`。遇到大量结果、截断或上限时继续缩小 path/glob/pattern,不得靠提高 limit 或倾倒全部结果解决。
|
||||||
- `.pi-debug/` 是 `/dump-system-prompt` 生成的本地诊断目录,不提交到仓库,也不作为组合包运行时配置源。
|
- `.pi-debug/` 是 `/dump-system-prompt` 生成的本地诊断目录,不提交到仓库,也不作为组合包运行时配置源。
|
||||||
- CodeGraph 扩展只配置 Pi 到外部 `codegraph` 命令的 MCP 连接。根包安装本身不安装 CodeGraph;便捷脚本仅在组合包安装完成且用户明确选择 `Y` 后调用官方安装器。仓库不执行 `codegraph init`,不创建或管理 `.codegraph/`,也不改动索引、更新或遥测设置。
|
- CodeGraph 扩展只配置 Pi 到外部 `codegraph` 命令的 MCP 连接。根包安装本身不安装 CodeGraph;便捷脚本仅在组合包安装完成且用户明确选择 `Y` 后调用官方安装器。仓库不执行 `codegraph init`,不创建或管理 `.codegraph/`,也不改动索引、更新或遥测设置。
|
||||||
- CodeGraph MCP 使用 `keep-alive` 并只直接暴露 `codegraph_explore`;命令缺失、项目未初始化或连接失败时不得阻止其他扩展加载。
|
- CodeGraph MCP 使用 `keep-alive` 并只直接暴露 `codegraph_explore`;命令缺失、项目未初始化或连接失败时不得阻止其他扩展加载。
|
||||||
@@ -45,7 +47,7 @@
|
|||||||
- reviewer 返回 `allow` 时自动批准、返回 `deny` 时直接拒绝,配置、模型、认证、超时或响应异常时必须 `defer` 到正常人工提示。`pi-permission-system` 的 delegation envelope 继续禁止 authorizer 自动批准 `path` 与 `external_directory` 请求。
|
- reviewer 返回 `allow` 时自动批准、返回 `deny` 时直接拒绝,配置、模型、认证、超时或响应异常时必须 `defer` 到正常人工提示。`pi-permission-system` 的 delegation envelope 继续禁止 authorizer 自动批准 `path` 与 `external_directory` 请求。
|
||||||
- 默认 reviewer 为 `openai-codex/codex-auto-review`、low reasoning、90 秒总重试预算和内置 Codex Guardian 风格策略;只把 active branch 中的直接用户消息与已识别结构化问答作为授权证据,assistant/tool/compaction 内容不能自行授权。
|
- 默认 reviewer 为 `openai-codex/codex-auto-review`、low reasoning、90 秒总重试预算和内置 Codex Guardian 风格策略;只把 active branch 中的直接用户消息与已识别结构化问答作为授权证据,assistant/tool/compaction 内容不能自行授权。
|
||||||
- `config/pi-permission-system.json` 必须显式配置 `authorizerChain: ["auto-review"]`,并把需要自动复核的 Git 非只读操作、包管理及其他类别声明为 `ask`;硬 `deny` 不得改成可由模型覆盖的 `ask`。
|
- `config/pi-permission-system.json` 必须显式配置 `authorizerChain: ["auto-review"]`,并把需要自动复核的 Git 非只读操作、包管理及其他类别声明为 `ask`;硬 `deny` 不得改成可由模型覆盖的 `ask`。
|
||||||
- `pi-lsp` 使用组合包部署的 `kotlin-lsp --stdio` 与 `jdtls` 配置,但根包安装本身不安装这两个系统可执行文件;便捷脚本可在用户逐项明确确认后通过 Homebrew 安装 Kotlin LSP、Java 21 和 JDT LS。
|
- `pi-lsp` 的 TypeScript/JavaScript 后端由根包固定依赖提供,`extensions/lsp.ts` 使用当前 Node 可执行文件直接启动包内 `typescript-language-server` CLI,不得依赖或调用 VS Code GUI;Kotlin/JDT LS 仍使用组合包部署的 `kotlin-lsp --stdio` 与 `jdtls` 配置,根包不安装这两个系统可执行文件,便捷脚本可在用户逐项明确确认后通过 Homebrew 安装 Kotlin LSP、Java 21 和 JDT LS。
|
||||||
- 终端环境配置只属于便捷脚本:Kitty 可选安装后可通过官方 kitten 启用 Solarized Dark;Oh My Zsh 使用不切换 shell 的 unattended 安装;Powerlevel10k 将仓库内置的 `config/p10k.zsh`(Rainbow/ASCII 单行紧凑主题)部署到 `~/.config/my-pi/p10k.zsh`(遵循 `XDG_CONFIG_HOME`),不得覆盖用户自己的 `~/.p10k.zsh`。`.zshrc` 中脚本拥有的内容必须使用 `# >>> my-pi:<id> >>>` / `# <<< my-pi:<id> <<<` 受管块,更新时只替换块内内容;标记不完整或重复时拒绝修改。实际修改前必须创建带时间戳的备份。`git`、`zsh-autosuggestions`、`zsh-syntax-highlighting` 在安装流程中逐项询问后才安装或启用。
|
- 终端环境配置只属于便捷脚本:Kitty 可选安装后可通过官方 kitten 启用 Solarized Dark;Oh My Zsh 使用不切换 shell 的 unattended 安装;Powerlevel10k 将仓库内置的 `config/p10k.zsh`(Rainbow/ASCII 单行紧凑主题)部署到 `~/.config/my-pi/p10k.zsh`(遵循 `XDG_CONFIG_HOME`),不得覆盖用户自己的 `~/.p10k.zsh`。`.zshrc` 中脚本拥有的内容必须使用 `# >>> my-pi:<id> >>>` / `# <<< my-pi:<id> <<<` 受管块,更新时只替换块内内容;标记不完整或重复时拒绝修改。实际修改前必须创建带时间戳的备份。`git`、`zsh-autosuggestions`、`zsh-syntax-highlighting` 在安装流程中逐项询问后才安装或启用。
|
||||||
- Codex 远程压缩只对 `openai-codex` 生效,默认在 turn boundary 达到 90% 时触发;Hermes Memory 默认使用 policy-only 模式。
|
- Codex 远程压缩只对 `openai-codex` 生效,默认在 turn boundary 达到 90% 时触发;Hermes Memory 默认使用 policy-only 模式。
|
||||||
- `install.sh` 默认从 `git:git@bitbucket.org:siakitem/my-pi.git` 安装,并允许用 `PI_PACKAGE_SOURCE` 覆盖来源;组合包安装失败时立即停止,机器级依赖安装失败时继续检查其余依赖并最终返回非零状态。
|
- `install.sh` 默认从 `git:git@bitbucket.org:siakitem/my-pi.git` 安装,并允许用 `PI_PACKAGE_SOURCE` 覆盖来源;组合包安装失败时立即停止,机器级依赖安装失败时继续检查其余依赖并最终返回非零状态。
|
||||||
@@ -56,7 +58,7 @@
|
|||||||
|
|
||||||
- 优先在目标扩展目录内完成改动;不要让一个扩展依赖另一个扩展的未公开内部实现。
|
- 优先在目标扩展目录内完成改动;不要让一个扩展依赖另一个扩展的未公开内部实现。
|
||||||
- 保留原项目的 `LICENSE`、版权信息和必要的来源说明。
|
- 保留原项目的 `LICENSE`、版权信息和必要的来源说明。
|
||||||
- 扩展运行目录中的 `config.json`、日志、构建产物、覆盖率目录和依赖目录属于本地状态,不应提交;`config/pi-permission-system.json` 是权限基线与 authorizer chain 的组合包权威源配置,必须提交并维护。
|
- 扩展运行目录中的 `config.json`、日志、构建产物、覆盖率目录和依赖目录属于本地状态,不应提交;`config/pi-permission-system.json`、`config/pi-hashline-edit.json` 与 `config/lsp.json` 分别是权限链、Hashline 默认行为和 LSP 后端的组合包权威源配置,必须提交并维护。
|
||||||
- 外部 Pi 扩展依赖必须在根 `package.json` 中使用精确版本,并更新根 `package-lock.json`;不要用仓库级 `.pi/settings.json` 代替组合包依赖。
|
- 外部 Pi 扩展依赖必须在根 `package.json` 中使用精确版本,并更新根 `package-lock.json`;不要用仓库级 `.pi/settings.json` 代替组合包依赖。
|
||||||
- 需要原生构建的依赖只按锁定版本加入根 `allowScripts`;当前仅允许 Hermes Memory 所需的 `better-sqlite3`,不得批量批准其他 install scripts。
|
- 需要原生构建的依赖只按锁定版本加入根 `allowScripts`;当前仅允许 Hermes Memory 所需的 `better-sqlite3`,不得批量批准其他 install scripts。
|
||||||
- Pi 核心包只作为宿主 peer dependencies,不得在组合包内再安装或打包一套 Pi runtime;保留根 `.npmrc` 的 peer 安装策略。
|
- Pi 核心包只作为宿主 peer dependencies,不得在组合包内再安装或打包一套 Pi runtime;保留根 `.npmrc` 的 peer 安装策略。
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
- `pi-mcp-adapter@2.26.0`:只用于把机器现有的 CodeGraph MCP Server 接入为 `codegraph_explore`。
|
- `pi-mcp-adapter@2.26.0`:只用于把机器现有的 CodeGraph MCP Server 接入为 `codegraph_explore`。
|
||||||
- `pi-context-view@0.4.2`:查看上下文占用。
|
- `pi-context-view@0.4.2`:查看上下文占用。
|
||||||
- `@firstpick/pi-extension-codex-fast-mode@0.1.1`:为 Codex provider 提供会话级 `/fast-mode`。
|
- `@firstpick/pi-extension-codex-fast-mode@0.1.1`:为 Codex provider 提供会话级 `/fast-mode`。
|
||||||
- `pi-lsp@0.1.7`:为 Kotlin 和 Java 提供 LSP 诊断、跳转与符号工具。
|
- `pi-lsp@0.1.7`:为 TypeScript/JavaScript、Kotlin 和 Java 提供 LSP 诊断、跳转与符号工具;TypeScript 后端由组合包内置。
|
||||||
- `pi-hermes-memory@0.9.6`:提供持久记忆、会话搜索和 secret scanning。
|
- `pi-hermes-memory@0.9.6`:提供持久记忆、会话搜索和 secret scanning。
|
||||||
- `@ogulcancelik/pi-codex-compaction@0.1.3`:为 `openai-codex` 提供原生远程压缩。
|
- `@ogulcancelik/pi-codex-compaction@0.1.3`:为 `openai-codex` 提供原生远程压缩。
|
||||||
- 本仓库维护的 `pi-permission-auto-review`:作为 `pi-permission-system` authorizer,使用 Codex Guardian 风格策略自动复核 `ask` 请求。
|
- 本仓库维护的 `pi-permission-auto-review`:作为 `pi-permission-system` authorizer,使用 Codex Guardian 风格策略自动复核 `ask` 请求。
|
||||||
@@ -115,12 +115,18 @@ CodeGraph 本体不由组合包安装。需要使用 CodeGraph 的机器应自
|
|||||||
|
|
||||||
### 工具与搜索路由
|
### 工具与搜索路由
|
||||||
|
|
||||||
`extensions/tool-routing.ts` 不替换 Pi 默认系统提示词,而是在每轮开始前根据当前激活工具追加简短规则:代码结构和符号关系优先使用 CodeGraph;字面搜索先用 FFF `find` 缩小文件或目录范围,再在已收敛的路径中用 `grep`/`multi_grep` 获取行号,最后才用带 `offset/limit` 的 `read` 读取精确区域。宽泛搜索命中上百或上千结果、发生截断或达到上限时,应继续缩小路径、glob 或 pattern,而不是提高 limit 或输出全部结果。
|
`extensions/tool-routing.ts` 不替换 Pi 默认系统提示词,而是在每轮开始前根据当前激活工具追加简短规则:代码结构、调用关系和待修改 symbol 优先使用 CodeGraph,定义、引用、类型和修改后诊断优先使用 LSP;字面搜索先用 FFF `find` 缩小文件或目录范围,再在已收敛的路径中用 `grep`/`multi_grep` 获取行号。宽泛搜索命中上百或上千结果、发生截断或达到上限时,应继续缩小路径、glob 或 pattern,而不是提高 limit 或输出全部结果。
|
||||||
|
|
||||||
日志、测试/构建输出、大文件分析和不可预测的大输出优先交给 Context Mode;需要编辑所依赖的精确原文或短文件才直接使用 `read`。已有工具结果足够时停止检索,避免对同一问题依次重复调用 CodeGraph、FFF 和 `read`。
|
日志、测试/构建输出、工作区内大文件的探索/分析/总结以及不可预测的大输出优先交给 Context Mode。只有确实需要精确源码或准备修改时,才用带 `offset/limit` 的 Hashline `read` 读取最小区域并取得 `LINE#HASH` 锚点,随后用锚定 `edit` 修改;成功编辑返回的新锚点可继续复用,只有锚点过期或下一目标区域尚未展示时才重新读取。已有工具结果足够时停止检索,避免对同一问题依次重复调用 CodeGraph、FFF 和 `read`。
|
||||||
|
|
||||||
使用 `/dump-system-prompt` 可将当前扩展所见的有效提示词写入当前项目的 `.pi-debug/effective-system-prompt.md`。`.pi-debug/` 属于本地诊断输出,默认不提交。
|
使用 `/dump-system-prompt` 可将当前扩展所见的有效提示词写入当前项目的 `.pi-debug/effective-system-prompt.md`。`.pi-debug/` 属于本地诊断输出,默认不提交。
|
||||||
|
|
||||||
|
### 精确读写归 Hashline
|
||||||
|
|
||||||
|
组合包固定加载 `pi-hashline-edit@0.8.3`,以带 `LINE#HASH` 行锚点的实现覆盖 Pi 内置 `read` 和 `edit`。`extensions/hashline.ts` 在加载扩展前把 `config/pi-hashline-edit.json` 同步到 `~/.pi/agent/hashline.json`:`hashLength` 保持最小的 `2`,`grep` 强制关闭以避免与 FFF 冲突,`replaceText` 关闭以要求修改使用可验证锚点。
|
||||||
|
|
||||||
|
Hashline 不负责压缩大文件;每行锚点本身还会增加少量 token。上下文节省来自先用 CodeGraph/LSP 定位、再用 Context Mode 隔离分析,最后只对真正准备修改的区域执行小范围 Hashline `read`。因此不应为了探索、计数、比较或总结而直接读取完整大文件。
|
||||||
|
|
||||||
### 搜索归 FFF
|
### 搜索归 FFF
|
||||||
|
|
||||||
`extensions/fff-override.ts` 强制设置 FFF 官方的 `PI_FFF_MODE=override`(显式传入
|
`extensions/fff-override.ts` 强制设置 FFF 官方的 `PI_FFF_MODE=override`(显式传入
|
||||||
@@ -191,15 +197,18 @@ codegraph status
|
|||||||
|
|
||||||
reviewer 默认使用 `openai-codex/codex-auto-review`、low reasoning 和内置 Codex Guardian 风格策略,并读取当前 session active branch 中的可信用户证据。可通过 `/permission-auto-review` 查看或调整全局/项目配置;无配置时使用源码内置默认值。
|
reviewer 默认使用 `openai-codex/codex-auto-review`、low reasoning 和内置 Codex Guardian 风格策略,并读取当前 session active branch 中的可信用户证据。可通过 `/permission-auto-review` 查看或调整全局/项目配置;无配置时使用源码内置默认值。
|
||||||
|
|
||||||
### Kotlin 与 Java LSP
|
### TypeScript、Kotlin 与 Java LSP
|
||||||
|
|
||||||
`extensions/lsp.ts` 会把 `config/lsp.json` 部署到全局 `~/.pi/agent/lsp.json`,其中启用:
|
`extensions/lsp.ts` 会把 `config/lsp.json` 部署到全局 `~/.pi/agent/lsp.json`,其中启用:
|
||||||
|
|
||||||
|
- `typescript-language-server@5.3.0 --stdio`:匹配 TypeScript/JavaScript 及 JSX/TSX、MTS/CTS、MJS/CJS;组合包同时固定 `typescript@6.0.3` 作为 `tsserver` 后端。
|
||||||
- `kotlin-lsp --stdio`:匹配 `.kt`、`.kts`。
|
- `kotlin-lsp --stdio`:匹配 `.kt`、`.kts`。
|
||||||
- `jdtls`:匹配 `.java`;官方 wrapper 会按当前项目工作目录选择 cache data 目录。
|
- `jdtls`:匹配 `.java`;官方 wrapper 会按当前项目工作目录选择 cache data 目录。
|
||||||
|
|
||||||
两者按 Gradle、Maven 或 Git marker 定位项目根,忽略常见构建输出,并为大型 Android/Gradle
|
TypeScript LSP 通过组合包内 Node 和 `typescript-language-server` CLI 直接启动,不依赖、调用或拉起 VS Code GUI;部署配置时会把包内 CLI 的绝对路径写入全局配置。它优先使用项目自身可用的 TypeScript,否则回退到组合包固定的 TypeScript 6 后端。
|
||||||
项目预留 120 秒启动时间。`pi-lsp` 只负责接入,不安装 language server 可执行文件;使用前需
|
|
||||||
|
Kotlin 与 Java 按 Gradle、Maven 或 Git marker 定位项目根,忽略常见构建输出,并为大型 Android/Gradle
|
||||||
|
项目预留 120 秒启动时间。Kotlin 与 Java 的 language server 可执行文件不由根包安装;使用前需
|
||||||
确保 `kotlin-lsp` 和 `jdtls` 位于启动 Pi 的 `PATH`。Kotlin 官方 Homebrew 安装方式为:
|
确保 `kotlin-lsp` 和 `jdtls` 位于启动 Pi 的 `PATH`。Kotlin 官方 Homebrew 安装方式为:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -1,6 +1,35 @@
|
|||||||
{
|
{
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"servers": [
|
"servers": [
|
||||||
|
{
|
||||||
|
"id": "typescript-language-server",
|
||||||
|
"enabled": true,
|
||||||
|
"include": ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts", "**/*.js", "**/*.jsx", "**/*.mjs", "**/*.cjs"],
|
||||||
|
"exclude": ["**/node_modules/**", "**/dist/**", "**/build/**", "**/coverage/**"],
|
||||||
|
"rootMarkers": [
|
||||||
|
"tsconfig.json",
|
||||||
|
"jsconfig.json",
|
||||||
|
"package.json",
|
||||||
|
".git"
|
||||||
|
],
|
||||||
|
"bin": "typescript-language-server",
|
||||||
|
"args": ["--stdio"],
|
||||||
|
"cwd": "{root}",
|
||||||
|
"languageIdByExtension": {
|
||||||
|
".ts": "typescript",
|
||||||
|
".tsx": "typescriptreact",
|
||||||
|
".mts": "typescript",
|
||||||
|
".cts": "typescript",
|
||||||
|
".js": "javascript",
|
||||||
|
".jsx": "javascriptreact",
|
||||||
|
".mjs": "javascript",
|
||||||
|
".cjs": "javascript"
|
||||||
|
},
|
||||||
|
"startupTimeoutMs": 45000,
|
||||||
|
"diagnosticsWaitMs": 2000,
|
||||||
|
"initializationOptions": {},
|
||||||
|
"settings": {}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "kotlin-lsp",
|
"id": "kotlin-lsp",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"hashLength": 2,
|
||||||
|
"grep": false,
|
||||||
|
"replaceText": false
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
||||||
|
import { dirname, join } from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
|
const BUNDLE_CONFIG_PATH = fileURLToPath(new URL("../config/pi-hashline-edit.json", import.meta.url));
|
||||||
|
|
||||||
|
export function deployBundleHashlineConfig(agentDir: string): void {
|
||||||
|
const targetPath = join(agentDir, "hashline.json");
|
||||||
|
const bundledConfig = readFileSync(BUNDLE_CONFIG_PATH, "utf8");
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (readFileSync(targetPath, "utf8") === bundledConfig) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Missing or unreadable target: replace it with the bundle-owned defaults.
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdirSync(dirname(targetPath), { recursive: true });
|
||||||
|
const temporaryPath = `${targetPath}.my-pi.tmp`;
|
||||||
|
writeFileSync(temporaryPath, bundledConfig, "utf8");
|
||||||
|
renameSync(temporaryPath, targetPath);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { getAgentDir, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
||||||
|
import { deployBundleHashlineConfig } from "./hashline-config.ts";
|
||||||
|
|
||||||
|
/** Deploy bundle defaults before pi-hashline-edit reads its module-level config. */
|
||||||
|
export default async function bundledHashlineExtension(pi: ExtensionAPI): Promise<void> {
|
||||||
|
deployBundleHashlineConfig(getAgentDir());
|
||||||
|
const { default: hashlineExtension } = await import("../node_modules/pi-hashline-edit/index.ts");
|
||||||
|
hashlineExtension(pi);
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
||||||
|
import { dirname, join } from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
|
const BUNDLE_CONFIG_PATH = fileURLToPath(new URL("../config/lsp.json", import.meta.url));
|
||||||
|
const TYPESCRIPT_SERVER_ID = "typescript-language-server";
|
||||||
|
|
||||||
|
interface LspConfigFile {
|
||||||
|
version?: number;
|
||||||
|
servers?: Array<{
|
||||||
|
id?: string;
|
||||||
|
bin?: string;
|
||||||
|
args?: string[];
|
||||||
|
[key: string]: unknown;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TypeScriptLspRuntime {
|
||||||
|
nodeBinary: string;
|
||||||
|
serverCliPath: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function materializeBundleLspConfig(source: string, runtime: TypeScriptLspRuntime): string {
|
||||||
|
const config = JSON.parse(source) as LspConfigFile;
|
||||||
|
const server = config.servers?.find((entry) => entry.id === TYPESCRIPT_SERVER_ID);
|
||||||
|
if (server === undefined) {
|
||||||
|
throw new Error(`Missing ${TYPESCRIPT_SERVER_ID} entry in bundled LSP config`);
|
||||||
|
}
|
||||||
|
|
||||||
|
server.bin = runtime.nodeBinary;
|
||||||
|
server.args = [runtime.serverCliPath, "--stdio"];
|
||||||
|
return `${JSON.stringify(config, null, 2)}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deployBundleLspConfig(agentDir: string, runtime: TypeScriptLspRuntime): void {
|
||||||
|
const targetPath = join(agentDir, "lsp.json");
|
||||||
|
const bundledConfig = materializeBundleLspConfig(readFileSync(BUNDLE_CONFIG_PATH, "utf8"), runtime);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (readFileSync(targetPath, "utf8") === bundledConfig) return;
|
||||||
|
} catch {
|
||||||
|
// Missing or unreadable target: replace it with the bundle-owned baseline.
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdirSync(dirname(targetPath), { recursive: true });
|
||||||
|
const temporaryPath = `${targetPath}.my-pi.tmp`;
|
||||||
|
writeFileSync(temporaryPath, bundledConfig, "utf8");
|
||||||
|
renameSync(temporaryPath, targetPath);
|
||||||
|
}
|
||||||
+8
-19
@@ -1,28 +1,17 @@
|
|||||||
import lspExtension from "../node_modules/pi-lsp/extensions/pi-lsp/index.ts";
|
import lspExtension from "../node_modules/pi-lsp/extensions/pi-lsp/index.ts";
|
||||||
import { getAgentDir, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
import { getAgentDir, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
||||||
import { readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
||||||
import { join } from "node:path";
|
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { deployBundleLspConfig } from "./lsp-config.ts";
|
||||||
|
|
||||||
const BUNDLE_CONFIG_PATH = fileURLToPath(new URL("../config/lsp.json", import.meta.url));
|
const TYPESCRIPT_LANGUAGE_SERVER_CLI = fileURLToPath(
|
||||||
|
new URL("../node_modules/typescript-language-server/lib/cli.mjs", import.meta.url),
|
||||||
function deployBundleLspConfig(): void {
|
);
|
||||||
const targetPath = join(getAgentDir(), "lsp.json");
|
|
||||||
const bundledConfig = readFileSync(BUNDLE_CONFIG_PATH, "utf8");
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (readFileSync(targetPath, "utf8") === bundledConfig) return;
|
|
||||||
} catch {
|
|
||||||
// Missing or unreadable target: replace it with the bundle-owned baseline.
|
|
||||||
}
|
|
||||||
|
|
||||||
const temporaryPath = `${targetPath}.my-pi.tmp`;
|
|
||||||
writeFileSync(temporaryPath, bundledConfig, "utf8");
|
|
||||||
renameSync(temporaryPath, targetPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Deploy the bundle-owned LSP config before registering pi-lsp. */
|
/** Deploy the bundle-owned LSP config before registering pi-lsp. */
|
||||||
export default function bundledLspExtension(pi: ExtensionAPI): void {
|
export default function bundledLspExtension(pi: ExtensionAPI): void {
|
||||||
deployBundleLspConfig();
|
deployBundleLspConfig(getAgentDir(), {
|
||||||
|
nodeBinary: process.execPath,
|
||||||
|
serverCliPath: TYPESCRIPT_LANGUAGE_SERVER_CLI,
|
||||||
|
});
|
||||||
lspExtension(pi);
|
lspExtension(pi);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,16 @@ export function buildToolRoutingSection(selectedTools: SelectedTools): string {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
hasTool(selectedTools, "lsp_definition") ||
|
||||||
|
hasTool(selectedTools, "lsp_references") ||
|
||||||
|
hasTool(selectedTools, "lsp_diagnostics")
|
||||||
|
) {
|
||||||
|
rules.push(
|
||||||
|
"- Use LSP for symbol definitions, references, hover/type information, and post-edit diagnostics when the language server supports the file.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (hasTool(selectedTools, "find") || hasTool(selectedTools, "grep") || hasTool(selectedTools, "multi_grep")) {
|
if (hasTool(selectedTools, "find") || hasTool(selectedTools, "grep") || hasTool(selectedTools, "multi_grep")) {
|
||||||
rules.push(
|
rules.push(
|
||||||
"- For literal search, narrow in stages instead of requesting a repository-wide dump:",
|
"- For literal search, narrow in stages instead of requesting a repository-wide dump:",
|
||||||
@@ -32,7 +42,7 @@ export function buildToolRoutingSection(selectedTools: SelectedTools): string {
|
|||||||
|
|
||||||
if (hasTool(selectedTools, "ctx_execute") || hasTool(selectedTools, "ctx_execute_file")) {
|
if (hasTool(selectedTools, "ctx_execute") || hasTool(selectedTools, "ctx_execute_file")) {
|
||||||
rules.push(
|
rules.push(
|
||||||
"- Use Context Mode for logs, test/build output, generated data, large-file analysis, and any command whose output may be large or unpredictable. Print only the derived answer needed for the task.",
|
"- Use Context Mode for logs, test/build output, generated data, large-file exploration/analysis/summarization, and any command whose output may be large or unpredictable. Prefer ctx_execute_file over read when deriving an answer from a large file inside the workspace, and print only the derived answer needed for the task.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +54,13 @@ export function buildToolRoutingSection(selectedTools: SelectedTools): string {
|
|||||||
|
|
||||||
if (hasTool(selectedTools, "read")) {
|
if (hasTool(selectedTools, "read")) {
|
||||||
rules.push(
|
rules.push(
|
||||||
"- Use read directly when exact text is needed for editing or when a file is small. Do not read a complete large file merely to copy, compare, hash, count, or summarize it.",
|
"- Use read only when exact source is needed or the file is small. Before editing, request the smallest useful offset/limit range; do not read a complete large file merely to copy, compare, hash, count, explore, or summarize it.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasTool(selectedTools, "read") && hasTool(selectedTools, "edit")) {
|
||||||
|
rules.push(
|
||||||
|
"- For existing-file changes, obtain fresh LINE#HASH anchors with a small read, then use anchored edit operations. Reuse fresh anchors returned by successful edits for chained changes; re-read only when anchors are stale or the next target region was not shown.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Generated
+166
@@ -14,9 +14,12 @@
|
|||||||
"@ogulcancelik/pi-codex-compaction": "0.1.3",
|
"@ogulcancelik/pi-codex-compaction": "0.1.3",
|
||||||
"context-mode": "1.0.169",
|
"context-mode": "1.0.169",
|
||||||
"pi-context-view": "0.4.2",
|
"pi-context-view": "0.4.2",
|
||||||
|
"pi-hashline-edit": "0.8.3",
|
||||||
"pi-hermes-memory": "0.9.6",
|
"pi-hermes-memory": "0.9.6",
|
||||||
"pi-lsp": "0.1.7",
|
"pi-lsp": "0.1.7",
|
||||||
"pi-mcp-adapter": "2.26.0",
|
"pi-mcp-adapter": "2.26.0",
|
||||||
|
"typescript": "6.0.3",
|
||||||
|
"typescript-language-server": "5.3.0",
|
||||||
"zod": "4.4.3"
|
"zod": "4.4.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -43,6 +46,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@borewit/text-codec": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz",
|
||||||
|
"integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Borewit"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@clack/core": {
|
"node_modules/@clack/core": {
|
||||||
"version": "1.4.3",
|
"version": "1.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@clack/core/-/core-1.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/@clack/core/-/core-1.4.3.tgz",
|
||||||
@@ -677,6 +690,29 @@
|
|||||||
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
|
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@tokenizer/inflate": {
|
||||||
|
"version": "0.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz",
|
||||||
|
"integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "^4.4.3",
|
||||||
|
"token-types": "^6.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Borewit"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tokenizer/token": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
|
||||||
|
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@yuuang/ffi-rs-android-arm64": {
|
"node_modules/@yuuang/ffi-rs-android-arm64": {
|
||||||
"version": "1.3.7",
|
"version": "1.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/@yuuang/ffi-rs-android-arm64/-/ffi-rs-android-arm64-1.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/@yuuang/ffi-rs-android-arm64/-/ffi-rs-android-arm64-1.3.7.tgz",
|
||||||
@@ -1189,6 +1225,12 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cuint": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz",
|
||||||
|
"integrity": "sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.4.3",
|
"version": "4.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||||
@@ -1288,6 +1330,15 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/diff": {
|
||||||
|
"version": "8.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz",
|
||||||
|
"integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/dunder-proto": {
|
"node_modules/dunder-proto": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||||
@@ -1528,6 +1579,24 @@
|
|||||||
"@yuuang/ffi-rs-win32-x64-msvc": "1.3.7"
|
"@yuuang/ffi-rs-win32-x64-msvc": "1.3.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/file-type": {
|
||||||
|
"version": "21.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.4.tgz",
|
||||||
|
"integrity": "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@tokenizer/inflate": "^0.4.1",
|
||||||
|
"strtok3": "^10.3.4",
|
||||||
|
"token-types": "^6.1.1",
|
||||||
|
"uint8array-extras": "^1.4.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sindresorhus/file-type?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/file-uri-to-path": {
|
"node_modules/file-uri-to-path": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||||
@@ -2107,6 +2176,23 @@
|
|||||||
"@earendil-works/pi-tui": "*"
|
"@earendil-works/pi-tui": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pi-hashline-edit": {
|
||||||
|
"version": "0.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/pi-hashline-edit/-/pi-hashline-edit-0.8.3.tgz",
|
||||||
|
"integrity": "sha512-eQWvwvR8aS7e/8CLTt8sdGhFXJguPTyFK+m9IB2muQQHsPnyMlF/cXKzEo5mQ898LxAM1jl7UVhPsmNXR8y/Mw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"diff": "^8.0.2",
|
||||||
|
"file-type": "^21.3.0",
|
||||||
|
"xxhashjs": "^0.2.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@earendil-works/pi-ai": ">=0.74.0",
|
||||||
|
"@earendil-works/pi-coding-agent": ">=0.74.0",
|
||||||
|
"@earendil-works/pi-tui": "*",
|
||||||
|
"@sinclair/typebox": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pi-hermes-memory": {
|
"node_modules/pi-hermes-memory": {
|
||||||
"version": "0.9.6",
|
"version": "0.9.6",
|
||||||
"resolved": "https://registry.npmjs.org/pi-hermes-memory/-/pi-hermes-memory-0.9.6.tgz",
|
"resolved": "https://registry.npmjs.org/pi-hermes-memory/-/pi-hermes-memory-0.9.6.tgz",
|
||||||
@@ -2735,6 +2821,22 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/strtok3": {
|
||||||
|
"version": "10.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz",
|
||||||
|
"integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@tokenizer/token": "^0.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Borewit"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/synckit": {
|
"node_modules/synckit": {
|
||||||
"version": "0.9.2",
|
"version": "0.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz",
|
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz",
|
||||||
@@ -2788,6 +2890,24 @@
|
|||||||
"node": ">=0.6"
|
"node": ">=0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/token-types": {
|
||||||
|
"version": "6.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz",
|
||||||
|
"integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@borewit/text-codec": "^0.2.1",
|
||||||
|
"@tokenizer/token": "^0.3.0",
|
||||||
|
"ieee754": "^1.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Borewit"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tree-sitter-bash": {
|
"node_modules/tree-sitter-bash": {
|
||||||
"version": "0.25.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/tree-sitter-bash/-/tree-sitter-bash-0.25.1.tgz",
|
"resolved": "https://registry.npmjs.org/tree-sitter-bash/-/tree-sitter-bash-0.25.1.tgz",
|
||||||
@@ -2875,6 +2995,43 @@
|
|||||||
"url": "https://opencollective.com/express"
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "6.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
|
||||||
|
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/typescript-language-server": {
|
||||||
|
"version": "5.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript-language-server/-/typescript-language-server-5.3.0.tgz",
|
||||||
|
"integrity": "sha512-5puofxZHgFdAYtfNpmwCAvgtaYgg8wrUnH30m7Ze3QuguId5RNRadKASpOpyDxTyUdAF51FjhTdjntLw/EuWcQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"typescript-language-server": "lib/cli.mjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/uint8array-extras": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/unpipe": {
|
"node_modules/unpipe": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||||
@@ -2975,6 +3132,15 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/xxhashjs": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz",
|
||||||
|
"integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cuint": "^0.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/zod": {
|
"node_modules/zod": {
|
||||||
"version": "4.4.3",
|
"version": "4.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz",
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
"extensions": [
|
"extensions": [
|
||||||
"./extensions/fff-override.ts",
|
"./extensions/fff-override.ts",
|
||||||
"./pi-rtk-optimizer/index.ts",
|
"./pi-rtk-optimizer/index.ts",
|
||||||
|
"./extensions/hashline.ts",
|
||||||
"./node_modules/context-mode/build/adapters/pi/extension.js",
|
"./node_modules/context-mode/build/adapters/pi/extension.js",
|
||||||
"./extensions/codegraph.ts",
|
"./extensions/codegraph.ts",
|
||||||
"./extensions/lsp.ts",
|
"./extensions/lsp.ts",
|
||||||
@@ -46,9 +47,12 @@
|
|||||||
"@ogulcancelik/pi-codex-compaction": "0.1.3",
|
"@ogulcancelik/pi-codex-compaction": "0.1.3",
|
||||||
"context-mode": "1.0.169",
|
"context-mode": "1.0.169",
|
||||||
"pi-context-view": "0.4.2",
|
"pi-context-view": "0.4.2",
|
||||||
|
"pi-hashline-edit": "0.8.3",
|
||||||
"pi-hermes-memory": "0.9.6",
|
"pi-hermes-memory": "0.9.6",
|
||||||
"pi-lsp": "0.1.7",
|
"pi-lsp": "0.1.7",
|
||||||
"pi-mcp-adapter": "2.26.0",
|
"pi-mcp-adapter": "2.26.0",
|
||||||
|
"typescript": "6.0.3",
|
||||||
|
"typescript-language-server": "5.3.0",
|
||||||
"zod": "4.4.3"
|
"zod": "4.4.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import assert from "node:assert/strict";
|
||||||
|
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
import { join } from "node:path";
|
||||||
|
import test from "node:test";
|
||||||
|
|
||||||
|
import { deployBundleHashlineConfig } from "../extensions/hashline-config.ts";
|
||||||
|
|
||||||
|
const EXPECTED_CONFIG = {
|
||||||
|
hashLength: 2,
|
||||||
|
grep: false,
|
||||||
|
replaceText: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
test("hashline config deployment enforces bundle defaults", async () => {
|
||||||
|
const agentDir = await mkdtemp(join(tmpdir(), "my-pi-hashline-"));
|
||||||
|
const targetPath = join(agentDir, "hashline.json");
|
||||||
|
|
||||||
|
try {
|
||||||
|
await writeFile(targetPath, '{"grep":true}\n', "utf8");
|
||||||
|
deployBundleHashlineConfig(agentDir);
|
||||||
|
assert.deepEqual(JSON.parse(await readFile(targetPath, "utf8")), EXPECTED_CONFIG);
|
||||||
|
|
||||||
|
const first = await readFile(targetPath, "utf8");
|
||||||
|
deployBundleHashlineConfig(agentDir);
|
||||||
|
assert.equal(await readFile(targetPath, "utf8"), first);
|
||||||
|
} finally {
|
||||||
|
await rm(agentDir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import assert from "node:assert/strict";
|
||||||
|
import { mkdtemp, readFile, rm } from "node:fs/promises";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
import { join } from "node:path";
|
||||||
|
import test from "node:test";
|
||||||
|
|
||||||
|
import { deployBundleLspConfig, materializeBundleLspConfig } from "../extensions/lsp-config.ts";
|
||||||
|
|
||||||
|
const RUNTIME = {
|
||||||
|
nodeBinary: "/runtime/node",
|
||||||
|
serverCliPath: "/bundle/typescript-language-server/lib/cli.mjs",
|
||||||
|
};
|
||||||
|
|
||||||
|
test("LSP config materializes the bundled TypeScript server command", () => {
|
||||||
|
const source = JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
servers: [
|
||||||
|
{ id: "typescript-language-server", bin: "typescript-language-server", args: ["--stdio"] },
|
||||||
|
{ id: "jdtls", bin: "jdtls", args: [] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const config = JSON.parse(materializeBundleLspConfig(source, RUNTIME));
|
||||||
|
|
||||||
|
assert.deepEqual(config.servers[0], {
|
||||||
|
id: "typescript-language-server",
|
||||||
|
bin: RUNTIME.nodeBinary,
|
||||||
|
args: [RUNTIME.serverCliPath, "--stdio"],
|
||||||
|
});
|
||||||
|
assert.deepEqual(config.servers[1], { id: "jdtls", bin: "jdtls", args: [] });
|
||||||
|
});
|
||||||
|
|
||||||
|
test("LSP config deployment writes the materialized bundle config", async () => {
|
||||||
|
const agentDir = await mkdtemp(join(tmpdir(), "my-pi-lsp-"));
|
||||||
|
try {
|
||||||
|
deployBundleLspConfig(agentDir, RUNTIME);
|
||||||
|
const config = JSON.parse(await readFile(join(agentDir, "lsp.json"), "utf8"));
|
||||||
|
const server = config.servers.find((entry: { id?: string }) => entry.id === "typescript-language-server");
|
||||||
|
|
||||||
|
assert.equal(server.bin, RUNTIME.nodeBinary);
|
||||||
|
assert.deepEqual(server.args, [RUNTIME.serverCliPath, "--stdio"]);
|
||||||
|
assert.deepEqual(server.include, [
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx",
|
||||||
|
"**/*.mts",
|
||||||
|
"**/*.cts",
|
||||||
|
"**/*.js",
|
||||||
|
"**/*.jsx",
|
||||||
|
"**/*.mjs",
|
||||||
|
"**/*.cjs",
|
||||||
|
]);
|
||||||
|
} finally {
|
||||||
|
await rm(agentDir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -21,13 +21,29 @@ test("search routing narrows files before requesting matching line numbers", ()
|
|||||||
assert.match(section, /Use read with offset\/limit only for the exact matching region/);
|
assert.match(section, /Use read with offset\/limit only for the exact matching region/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("ctx_execute_file routing warns about its project-root boundary", () => {
|
test("context routing analyzes large files without a full read", () => {
|
||||||
const section = buildToolRoutingSection(["ctx_execute_file"]);
|
const section = buildToolRoutingSection(["ctx_execute", "ctx_execute_file"]);
|
||||||
|
|
||||||
|
assert.match(section, /Prefer ctx_execute_file over read when deriving an answer from a large file/);
|
||||||
assert.match(section, /ctx_execute_file is confined to the current project root/);
|
assert.match(section, /ctx_execute_file is confined to the current project root/);
|
||||||
assert.match(section, /host permission approval does not bypass this Context Mode boundary/);
|
assert.match(section, /host permission approval does not bypass this Context Mode boundary/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("hashline routing uses small reads and fresh anchors for edits", () => {
|
||||||
|
const section = buildToolRoutingSection(["read", "edit"]);
|
||||||
|
|
||||||
|
assert.match(section, /smallest useful offset\/limit range/);
|
||||||
|
assert.match(section, /obtain fresh LINE#HASH anchors with a small read/);
|
||||||
|
assert.match(section, /Reuse fresh anchors returned by successful edits/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("LSP routing covers navigation and post-edit diagnostics", () => {
|
||||||
|
const section = buildToolRoutingSection(["lsp_definition", "lsp_references", "lsp_diagnostics"]);
|
||||||
|
|
||||||
|
assert.match(section, /Use LSP for symbol definitions, references/);
|
||||||
|
assert.match(section, /post-edit diagnostics/);
|
||||||
|
});
|
||||||
|
|
||||||
test("routing includes only guidance for active optional tools", () => {
|
test("routing includes only guidance for active optional tools", () => {
|
||||||
const section = buildToolRoutingSection(["read"]);
|
const section = buildToolRoutingSection(["read"]);
|
||||||
|
|
||||||
@@ -35,7 +51,8 @@ test("routing includes only guidance for active optional tools", () => {
|
|||||||
assert.doesNotMatch(section, /For literal search/);
|
assert.doesNotMatch(section, /For literal search/);
|
||||||
assert.doesNotMatch(section, /Use Context Mode/);
|
assert.doesNotMatch(section, /Use Context Mode/);
|
||||||
assert.doesNotMatch(section, /ctx_execute_file is confined/);
|
assert.doesNotMatch(section, /ctx_execute_file is confined/);
|
||||||
assert.match(section, /Use read directly/);
|
assert.doesNotMatch(section, /fresh LINE#HASH anchors/);
|
||||||
|
assert.match(section, /Use read only when exact source is needed/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("routing is appended once", () => {
|
test("routing is appended once", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user