mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-10-26 02:35:24 +00:00
feat(pi-hermes-memory): 接入持久记忆扩展并移除 Hippo
用仓库内 pi-hermes-memory 替换 hippo-memory-pi,清理 Hippo 在组合包中的
全部注入点。
移除 Hippo:
- 删除 hippo-memory-pi/ 与 config/hippo-memory-version。
- extensions/plugin-init.ts 只保留 CodeGraph 初始化目标与预检。
- install.sh / update.sh / uninstall.sh 移除 Hippo CLI 的安装、升级、卸载
与版本校验逻辑;uninstall.sh 同时删除仅服务于该提示的死代码 ask_yes_no。
- 删除 tests/hippo-extension.test.ts 与 tests/hippo-scripts.test.ts。
- tests/extension-load.test.ts 反转“不得加载 Hermes”的断言为必须加载,
并断言其排在 pi-compaction 之前。
接入 pi-hermes-memory:
- package.json 增加 file:./pi-hermes-memory 依赖、files 条目与加载入口;
加载顺序位于 pi-compaction 之前,否则 flushOnCompact 会被 pi-compaction
的 { cancel: true } 静默跳过。
- 六个记忆工具在 Tool Search 中常驻:memory_search 与 session_search 连同
context_tree_query 归入 memory-recall,memory_add / memory_replace /
memory_remove / skill_manage 归入 memory-management。常驻是必需的——注入
的 memory policy 会在普通工作中指示模型调用它们,schema 不可见会让模型
静默放弃而不是去 tool_search,同时常驻也保持请求前缀缓存稳定。
- bundleDefaultsVersion 由 2 升到 3;migrate-tool-search-settings.mjs 新增
上一版快照到当前版本的迁移路径,仍只迁移完整匹配的默认快照并保留自定义
配置。
- config/pi-permission-policy.jsonc 为六个工具设置 allow:它们只写扩展自己的
agent 目录数据、不触碰工作区,且 memory_search 每轮都可能被调用,落入
默认 ask 会产生每轮 AutoReview 往返。
依赖:
- pi-hermes-memory 的 devDependencies 由上游 ^0.80.2 等范围改为与根包一致的
精确版本。上游范围与根包 0.84.2 不兼容,会让 npm 在本目录嵌套安装第二套
Pi runtime(实测 229 MB),违反“不得在组合包内再安装一套 Pi runtime”的
纪律。
- 根 allowScripts 保持只有 better-sqlite3@12.11.1。hermes 的
better-sqlite3@13 运行时 dlopen 的是包内 N-API 预编译二进制
prebuilds/<platform>.node,npm 因 binding.gyp 推断出的 node-gyp rebuild
无需执行;实测删除 build/ 目录后仍正常工作。
文档:AGENTS.md 与 README.md 改写记忆章节,新增 pi-hermes-memory 开发约定,
替换文末的 Hippo 项目记忆块。
验证:根 12 个测试文件、pi-tool-search 的 typecheck/test/build、
pi-permission-system 的 typecheck/2569 测试、pi-hermes-memory 自带的 59 个
测试文件全部通过;隔离 Pi 环境联合加载无错误并生成 bundleDefaultsVersion 3;
npm install 幂等;迁移 helper 对 v2 快照迁移、对自定义配置保留。
This commit is contained in:
@@ -25,7 +25,7 @@
|
||||
- `@keenable/pi-search`:加载 `keenable_search` / `keenable_fetch` 及其 skill,默认 keyless,`KEENABLE_API_KEY` 仅用于提高速率限制。
|
||||
- `search_config.sh` 与 `extensions/search-config.ts`:以交互式或参数方式把三家搜索 key 写入用户级 `search.env`(权限 `600`),并在搜索扩展初始化前加载;显式进程环境变量优先。
|
||||
- `extensions/mcp.ts`:通过单个共享 `pi-mcp-adapter` 实例同时连接 Exa 托管 MCP 与机器现有的 `codegraph serve --mcp`,避免重复注册 Pi 的全局 MCP flag 与命令;Exa key 只通过 `x-api-key` 请求头发送,原始工具映射为统一的来源前缀形式,CodeGraph 只暴露 `codegraph_explore`。
|
||||
- `extensions/plugin-init.ts`:注册用户显式触发的 `/plugin_init`,在确认当前项目后只初始化缺失的 CodeGraph/Hippo 状态,并在全部成功后调用 Pi 官方热重载。
|
||||
- `extensions/plugin-init.ts`:注册用户显式触发的 `/plugin_init`,在确认当前项目后只初始化缺失的 CodeGraph 状态,并在全部成功后调用 Pi 官方热重载。
|
||||
- `context-mode`:加载上游 Pi adapter 与 skills,提供 `ctx_*` 工具、隔离式大输出处理和会话连续性。
|
||||
- `extensions/hashline.ts`:部署组合包权威 `config/pi-hashline-edit.json` 后加载 `pi-hashline-edit`,以行哈希锚点覆盖内置 `read` / `edit`;Hashline 自带 `grep` 强制关闭。
|
||||
- `extensions/permission-system.ts`:直接加载仓库内 `pi-permission-system/src/index.ts`;只在目标缺失时把 `config/pi-permission-policy.jsonc` / `config/pi-permission-hard-guards.jsonc` 原子部署为 owner-only 全局用户配置,旧全局 `config.json` 仅复制迁移为 `policy.jsonc`。项目级 policy 不读取;`update.sh` 只可在当前文件精确匹配已知旧默认种子时备份并迁移到新种子,自定义或无效文件必须保留。
|
||||
@@ -44,10 +44,10 @@
|
||||
- 初始参考快照:`dcd461925db2edf69a43c8135db1180d418afd54`(2026-08-24)。
|
||||
- 该目录由本仓库直接维护,保留上游 MIT 许可证与来源说明;根组合包直接加载源码并通过本地 `file:` 依赖打包。
|
||||
- `pi-lsp@0.1.7`:提供声明式 LSP 接入;组合包内置 `typescript-language-server@5.3.0` + `typescript@6.0.3`,并配置机器级 `kotlin-lsp --stdio` 与 `jdtls`。
|
||||
- `hippo-memory-pi/`:从官方 `hippo-memory` 仓库的 `extensions/pi-extension/` 导入并由本仓库直接维护;提供 session start 项目记忆注入、工具错误过滤捕获、session shutdown sleep 和 5 个 `hippo_*` 工具。
|
||||
- 上游来源:<https://github.com/kitfunso/hippo-memory>
|
||||
- 初始导入快照:`e928179a3b35e8fe5837878aed071d6025ced45c`(`v1.33.0`)。
|
||||
- 官方 npm tarball 不包含 Pi Extension;仓库保留官方 README、LICENSE 和来源说明,匹配 CLI 版本由 `config/hippo-memory-version` 声明。
|
||||
- `pi-hermes-memory/`:从官方 `pi-hermes-memory` 仓库导入并由本仓库直接维护的 Hermes 风格持久记忆扩展;提供全局/项目/用户/失败四类记忆、SQLite FTS5 trigram 会话与记忆检索、`SKILL.md` 过程记忆、correction 检测、自动 consolidation、`STANDING.md` 常驻指令和 6 个记忆工具。
|
||||
- 上游来源:<https://github.com/chandra447/pi-hermes-memory>
|
||||
- 初始导入快照:`df624056a2e8c68bc89ba0a81e1c120856ec9665`(`v0.9.10`)。
|
||||
- 该目录从明确 tag 导入并由本仓库直接维护,不使用 npm 预编译产物,不是 submodule,也不保留嵌套 `.git`、上游 `.github` 工作流或构建产物。
|
||||
- `pi-minimal-footer/`:基于上游紧凑 footer,保留上下文仪表、订阅用量条和扩展状态,并在 Codex Fast 模式开启时显示 `Fast on`。
|
||||
- 上游来源:<https://github.com/ogulcancelik/pi-extensions/tree/main/packages/pi-minimal-footer>
|
||||
- 初始导入快照:`77bd1e175003cd08e6d05d9e7fed695f86ae87b7`(`@ogulcancelik/pi-minimal-footer@0.1.10`)。
|
||||
@@ -64,10 +64,10 @@
|
||||
- 上游参考一:<https://github.com/jjuraszek/pi-condense>,快照 `59c95110aacd8131db71e21296765dd80b72fae8`(`v2.9.1`)。
|
||||
- 上游来源二:<https://github.com/ogulcancelik/pi-extensions/tree/main/packages/pi-codex-compaction>,导入快照 `c4d6ca8fb484ba51684296ba8b4715f8c7b3ac91`(`0.1.4`)。
|
||||
- `pi-compaction` 采用全新的 `pi-compaction` version 1 Session 记录和 `settings.json.piCompaction` 配置,不读取或迁移旧 `context-prune-*`、`contextPrune` 或 `openai-codex-native-compaction` 状态;切换后应使用新 Session。两份上游 MIT 许可证及来源差异保留在包内。
|
||||
- 根包还固定安装 `@tavily/pi-extension@0.1.2`、`@keenable/pi-search@0.1.2`、`pi-hashline-edit@0.8.3`、`typescript-language-server@5.3.0` 和 `typescript@6.0.3`;`@firstpick/pi-extension-codex-fast-mode`、`@gotgenes/pi-permission-system`、`@smoose/pi-notify`、`pi-ask-user`、`pi-chrome`、`pi-compaction`、`pi-minimal-footer`、`pi-ssh` 与 `pi-tool-search` 分别指向仓库内对应目录的本地 `file:` 依赖;`pi-subagents/` 仅保留源码,不作为根运行时依赖。
|
||||
- `install.sh`:先安装根组合包,再交互检查 Hippo Memory CLI、Kitty/Solarized Dark、Oh My Zsh/Powerlevel10k/Zsh 插件、CodeGraph、Kotlin LSP、Java 21+ 和 JDT LS;缺失项目只在用户明确选择 `Y` 后安装或配置。Hippo 项目目录与 `hippo init` 始终由用户自行决定。Powerlevel10k 默认配置来自仓库内置的 `config/p10k.zsh`(当前 Rainbow/ASCII 单行紧凑主题)。
|
||||
- `update.sh`:先通过 `pi update` 升级根组合包,再只升级当前已安装的终端环境和机器级依赖;未安装项直接跳过。不得在 `pi update` 前预清理安装缓存;仅当默认 Git 来源首次升级失败、Pi 的精确中断标记存在、canonical checkout 根目录和 `origin` 均通过身份校验时,才允许对该缓存执行最多 4 次连续 `git clean -fdx`,随后只重试一次 `pi update`,由 Pi 自己恢复依赖和清除标记;自定义来源、标记缺失或身份不匹配必须保持 fail-fast。升级成功后只能调用可定位的更新后组合包 Node helper 检查 Pi agent `settings.json`:只有 `toolSearch` 完整精确匹配旧版 `3` 组 / `20` 工具默认快照时,才创建从生成起即为权限 `600` 的时间戳备份、以同目录临时文件原子迁移到 `5` / `28` 并写入 `bundleDefaultsVersion: 2`;任何自定义或无效配置必须保留,提交前检测到内容变化必须中止。无法从 `PI_PACKAGE_SOURCE` 定位更新后 helper 时必须保留配置并报告失败,不能回退到可能陈旧的脚本目录 helper。其他组件升级前先查询并比较本地与远端版本,只有版本不同时才下载或替换;Powerlevel10k 配置优先从 `pi update` 后的已安装组合包读取,并与 `.zshrc` 受管块一起按内容比较后增量同步。
|
||||
- `uninstall.sh`:先移除根组合包;若检测到 Hippo Memory CLI,再明确询问是否卸载确认属于 npm 全局安装的 `hippo-memory`,默认保留且始终不删除 `.hippo/` 或用户记忆数据。其他共享终端环境和机器工具不卸载。
|
||||
- 根包还固定安装 `@tavily/pi-extension@0.1.2`、`@keenable/pi-search@0.1.2`、`pi-hashline-edit@0.8.3`、`typescript-language-server@5.3.0` 和 `typescript@6.0.3`;`@firstpick/pi-extension-codex-fast-mode`、`@gotgenes/pi-permission-system`、`@smoose/pi-notify`、`pi-ask-user`、`pi-chrome`、`pi-compaction`、`pi-hermes-memory`、`pi-minimal-footer`、`pi-ssh` 与 `pi-tool-search` 分别指向仓库内对应目录的本地 `file:` 依赖;`pi-subagents/` 仅保留源码,不作为根运行时依赖。
|
||||
- `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` 升级根组合包,再只升级当前已安装的终端环境和机器级依赖;未安装项直接跳过。不得在 `pi update` 前预清理安装缓存;仅当默认 Git 来源首次升级失败、Pi 的精确中断标记存在、canonical checkout 根目录和 `origin` 均通过身份校验时,才允许对该缓存执行最多 4 次连续 `git clean -fdx`,随后只重试一次 `pi update`,由 Pi 自己恢复依赖和清除标记;自定义来源、标记缺失或身份不匹配必须保持 fail-fast。升级成功后只能调用可定位的更新后组合包 Node helper 检查 Pi agent `settings.json`:只有 `toolSearch` 完整精确匹配旧版 `3` 组 / `20` 工具或上一版 `5` 组 / `28` 工具默认快照时,才创建从生成起即为权限 `600` 的时间戳备份、以同目录临时文件原子迁移到当前默认值并写入当前 `bundleDefaultsVersion`;任何自定义或无效配置必须保留,提交前检测到内容变化必须中止。无法从 `PI_PACKAGE_SOURCE` 定位更新后 helper 时必须保留配置并报告失败,不能回退到可能陈旧的脚本目录 helper。其他组件升级前先查询并比较本地与远端版本,只有版本不同时才下载或替换;Powerlevel10k 配置优先从 `pi update` 后的已安装组合包读取,并与 `.zshrc` 受管块一起按内容比较后增量同步。
|
||||
- `uninstall.sh`:先移除根组合包,其他共享终端环境和机器工具不卸载。不得删除 `.pi/agent/pi-hermes-memory/`、`projects-memory/`、`.hippo/` 或任何用户记忆数据。
|
||||
|
||||
## 当前职责与默认行为
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
- 标准组合包工具全部命中预置目录时 Tool Search 不得调用模型、创建用户缓存或发送隐藏 schema。只有存在未识别第三方工具时才允许惰性模型增强;生成结果必须保留全部预置 assignment,模型元数据不得替代真实名称、参数或 schema。缓存必须位于 agent 目录且权限 `600`;模型失败必须继续使用预置目录 + 确定性未知工具分组。`groupOverrides` 优先级高于预置分组。
|
||||
- Tool Search 必须直接使用当前 `@earendil-works/pi-coding-agent`、`typebox`、`ModelRegistry.complete()` 和 `setActiveTools()`;首次纯增加载使用当前 Pi 的增量结果传播,LRU 替换允许走宿主安全 fallback。不得安装或映射旧 `@mariozechner` runtime,不得恢复 provider payload 改写、代理分发或隐藏 `sendMessage` steer/retry 兼容循环。Tool Search 是唯一可调用 `getActiveTools()` / `setActiveTools()` 管理工具显隐的组合包扩展;`pi-permission-system` 只能在调用时裁决,不能读写 active tool 列表。
|
||||
- `.pi-debug/` 是 `/dump-system-prompt` 生成的本地诊断目录,不提交到仓库,也不作为组合包运行时配置源。
|
||||
- CodeGraph MCP 扩展只配置 Pi 到外部 `codegraph` 命令的连接。根包安装本身不安装 CodeGraph;便捷脚本仅在组合包安装完成且用户明确选择 `Y` 后调用官方安装器。安装、升级和普通启动流程不得执行 `codegraph init`;只有用户在目标项目显式调用 `/plugin_init` 并确认后,命令才可以 `.codegraph/codegraph.db` 与 `.hippo/hippo.db` 为权威标志初始化缺失状态。命令必须先预检两个 CLI,任一步失败不得热重载,也不得隐瞒此前已完成的部分初始化;不得改动 CodeGraph 更新或遥测设置。
|
||||
- CodeGraph MCP 扩展只配置 Pi 到外部 `codegraph` 命令的连接。根包安装本身不安装 CodeGraph;便捷脚本仅在组合包安装完成且用户明确选择 `Y` 后调用官方安装器。安装、升级和普通启动流程不得执行 `codegraph init`;只有用户在目标项目显式调用 `/plugin_init` 并确认后,命令才可以 `.codegraph/codegraph.db` 为权威标志初始化缺失状态。命令必须先预检 CLI,失败不得热重载,也不得隐瞒此前已完成的部分初始化;不得改动 CodeGraph 更新或遥测设置。
|
||||
- CodeGraph MCP 使用 `keep-alive` 并只直接暴露 `codegraph_explore`;命令缺失、项目未初始化或连接失败时不得阻止其他扩展加载。
|
||||
- 所有 MCP 服务必须合并到 `extensions/mcp.ts` 创建的唯一 `pi-mcp-adapter` 实例;不得新增独立 adapter 扩展入口,否则会重复注册 `--mcp-config`、`/mcp` 等全局接口。根测试必须扫描全部本地扩展入口并强制这一不变量。
|
||||
- Exa MCP 使用托管 Streamable HTTP 端点和 `eager` lifecycle;`EXA_API_KEY` 通过 `x-api-key` 请求头发送,不得放入 URL、仓库文件或日志。所有在线搜索工具统一采用“来源名 + 原始语义工具名”的命名形式,例如 `tavily_web_search`、`exa_web_search`、`keenable_search`。Exa 高级搜索必须显式约束结果数量和文本长度;Tavily 输出默认控制 `max_results` 且非必要不请求 raw content;Keenable 优先利用中文、站点和日期筛选能力。
|
||||
@@ -110,13 +110,14 @@
|
||||
- 终端环境配置只属于便捷脚本: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` 在安装流程中逐项询问后才安装或启用。
|
||||
- 原生远程压缩限定为 `openai-codex` + `openai-codex-responses` 与 `sub-api` + `openai-responses`,默认在 turn boundary 达到 effective 下界 90% 时触发。`pi-compaction/src/native-provider.ts` 统一资格与传输选择:Codex 保留原 endpoint/JWT/account ID;SubAPI 从显式 `baseUrl` 解析公共 `/responses`,使用配置的 API key 和请求头,不解析 ChatGPT account ID、不硬编码网关域名,也不调用独立 `/responses/compact`。两者复用 projection、`compaction_trigger`、SSE 结果、native checkpoint 恢复和 portable 模型切换摘要;SubAPI 部署须支持原生 compaction v2。普通聊天支持不代表压缩支持,失败回退 Pi,不自动改写用户模型配置。
|
||||
- SubAPI 原生压缩认证必须保留非空显式 `Authorization`(大小写不敏感)的优先级;没有该头时才使用非空 API key 生成 Bearer,均缺失时可接受非空 `cf-aig-authorization` 代理认证头。不得要求 header-only 配置额外提供假 API key,也不得生成 `Bearer undefined`;空白或缺失凭据在网络请求前拒绝并回退 Pi。Codex 仍必须提供有效 JWT,不能用请求头绕过。
|
||||
- 官方 Hippo Extension 在已初始化项目的 session start 注入上下文、过滤捕获工具错误,并在 session shutdown 执行 `hippo sleep`。
|
||||
- Hippo Extension 直接使用 PATH 中的官方全局 `hippo` CLI;`install.sh` 只在 CLI 缺失且用户明确确认后安装 `config/hippo-memory-version` 指定的 npm 版本,不得扫描项目、选择初始化目录或执行 `hippo init`。用户必须在自己选择的项目目录中手动初始化。
|
||||
- `update.sh` 只升级已经安装且属于 npm 全局包的 Hippo CLI;必须从 `pi update` 后的组合包读取目标版本,先用 `npm view` 确认,再比较本地版本,版本相同不得重新安装,CLI 缺失或来源未知时保留现状。
|
||||
- `uninstall.sh` 只在根包移除成功后询问是否卸载 npm 全局 Hippo CLI,默认拒绝;不得删除项目 `.hippo/`、全局记忆或旧 Hermes 数据。
|
||||
- 记忆由 `pi-hermes-memory` 统一负责:默认 `policy-only` 模式只注入 `<memory-policy>` 指令而不注入记忆正文,因此它不改变 system prompt 前缀、对 DeepSeek 前缀缓存友好;模型需要历史上下文时主动调用 `memory_search` / `session_search`,写入用 `memory_add` / `memory_replace` / `memory_remove`,过程记忆用 `skill_manage`。
|
||||
- 六个记忆工具必须由 `pi-tool-search` 的 `alwaysEnabled` 常驻,不得放入动态组:注入的 policy 会在普通工作中指示模型调用它们,模型看不到 schema 就会静默放弃而不是去 `tool_search`;常驻同时保持请求前缀稳定。它们只写扩展自己的 agent 目录数据,不触碰工作区,因此在权限策略中默认 `allow`,避免每轮 AutoReview 往返。
|
||||
- `pi-hermes-memory` 必须注册在 `pi-compaction` 之前。`ExtensionRunner.emit()` 在第一个返回 `{ cancel: true }` 的 `session_before_compact` handler 处立即停止,而 `pi-compaction` 在判定无需压缩时正是这样返回;注册在其后的 handler 会被跳过,从而静默禁用 `flushOnCompact`。
|
||||
- `pi-hermes-memory` 依赖 `better-sqlite3@13`,运行时实际 `dlopen` 的是包内 N-API 预编译二进制 `prebuilds/<platform>.node`,不依赖 node-gyp 编译产物。npm 会因该包存在 `binding.gyp` 而推断出隐式 `node-gyp rebuild`,该步骤既无必要也不得批准;根 `allowScripts` 必须保持不新增条目,同时保留既有的 `better-sqlite3@12.11.1` 放行。
|
||||
- `skill_manage` 是 `pi-hermes-memory` 提供的能力,不是 Pi 宿主能力:Pi 只发现并加载 skill 目录(`~/.pi/agent/skills/` 加扩展通过 `resources_discover` 注册的目录),没有创建 skill 的工具。该扩展同时承担 skill 写入与目录注册;不加载它,`projects-memory/<项目>/skills/` 下的既有 skill 也不会被 Pi 发现。
|
||||
- `install.sh` 默认从 `git:git@bitbucket.org:siakitem/my-pi.git` 安装,并允许用 `PI_PACKAGE_SOURCE` 覆盖来源;组合包安装失败时立即停止,机器级依赖安装失败时继续检查其余依赖并最终返回非零状态。
|
||||
- CodeGraph 的可选安装使用官方远程安装脚本;macOS Kitty 使用 Homebrew cask,其他系统使用 Kitty 官方安装器;Kotlin LSP、Java 21 和 JDT LS 使用 Homebrew;Oh My Zsh 使用官方安装器,Powerlevel10k 与两个第三方 Zsh 插件从各自官方 Git 仓库克隆。升级时 Git checkout、Homebrew 包、Kitty 官方安装和 CodeGraph 都必须先比较本地与远端版本,版本一致时不得重新下载安装;无法可靠比较时保留现状并报告。缺少 Homebrew、curl 或 git 时保留已经完成的组合包安装并报告失败。
|
||||
- `install.sh` 不检查默认不需要的 `rtk` CLI;`update.sh` 和 `uninstall.sh` 使用同一个 `PI_PACKAGE_SOURCE` 规则分别调用 `pi update` 和 `pi remove`。运行 `update.sh` 视为明确授权升级已安装组件及同步已有受管配置;其中 Tool Search 只能自动迁移完整匹配仓库旧默认快照的配置,不能逐字段猜测或覆盖自定义值。不得安装缺失组件或改写受管块之外的用户配置;卸载仅可在用户当次明确确认后移除 npm 全局 Hippo CLI,其他共享机器级工具保持不变。
|
||||
- `install.sh` 不检查默认不需要的 `rtk` CLI;`update.sh` 和 `uninstall.sh` 使用同一个 `PI_PACKAGE_SOURCE` 规则分别调用 `pi update` 和 `pi remove`。运行 `update.sh` 视为明确授权升级已安装组件及同步已有受管配置;其中 Tool Search 只能自动迁移完整匹配仓库已知默认快照的配置,不能逐字段猜测或覆盖自定义值。不得安装缺失组件或改写受管块之外的用户配置;卸载不得删除用户记忆数据,其他共享机器级工具保持不变。
|
||||
|
||||
## 修改边界
|
||||
|
||||
@@ -124,7 +125,7 @@
|
||||
- 保留原项目的 `LICENSE`、版权信息和必要的来源说明。
|
||||
- 扩展运行目录中的用户 `policy.jsonc` / `hard-guards.jsonc`、旧 `config.json`、审批 ledger/proposals、日志、构建产物、覆盖率目录和依赖目录属于本地状态,不应提交;`config/pi-permission-policy.jsonc`、`config/pi-permission-hard-guards.jsonc`、`config/pi-hashline-edit.json` 与 `config/lsp.json` 分别是新安装权限种子、Hashline 默认行为和 LSP 后端的组合包权威源配置,必须提交并维护。
|
||||
- 外部 Pi 扩展依赖必须在根 `package.json` 中使用精确版本,自维护 Fast mode 等扩展必须使用本地 `file:` 依赖,并更新根 `package-lock.json`;不要用仓库级 `.pi/settings.json` 代替组合包依赖。
|
||||
- 需要原生构建的依赖只按锁定版本加入根 `allowScripts`;当前 `better-sqlite3@12.11.1` 仍由 Context Mode 使用并保持精确放行。官方 Hippo CLI 是独立机器级 npm 包,不得因此向根包批量批准其他 install scripts。
|
||||
- 需要原生构建的依赖只按锁定版本加入根 `allowScripts`;当前 `better-sqlite3@12.11.1` 仍由 Context Mode 使用并保持精确放行。`pi-hermes-memory` 的 `better-sqlite3@13` 运行时使用包内 N-API 预编译二进制,npm 因 `binding.gyp` 推断出的 `node-gyp rebuild` 无需执行,不得为此新增放行条目。
|
||||
- Pi 核心包只作为宿主 peer dependencies,不得在组合包内再安装或打包一套 Pi runtime;保留根 `.npmrc` 的 peer 安装策略。
|
||||
- `pi-tool-search/` 的根依赖必须保持 `file:./pi-tool-search`,组合包直接加载 `pi-tool-search/extensions/index.ts`;同步上游时先核对本地增量动态工具改造,禁止用旧 npm 入口或上游旧宿主实现直接覆盖。
|
||||
- 新引入或同步自维护扩展时,必须先联网确认其官方源码仓库地址,再从官方 Git 仓库的明确 tag/commit 拉取源码快照;禁止以 npm tarball、npm 缓存、`node_modules`、`dist/` 或其他预编译发布产物作为导入源。npm registry 只能用于核对包名、版本和发布元数据,不能替代源码仓库。
|
||||
@@ -171,12 +172,16 @@
|
||||
- 修改 reviewer 策略、可信证据边界、配置结构、hard-guard precedence、ledger、后台分析、模型解析或 authorizer/lifecycle 注册时,必须同步更新 README、配置文档、架构文档、schema 和相应测试。
|
||||
|
||||
|
||||
## `hippo-memory-pi` 开发约定
|
||||
## `pi-hermes-memory` 开发约定
|
||||
|
||||
- 扩展入口是 `hippo-memory-pi/index.ts`,来源记录是 `hippo-memory-pi/UPSTREAM.md`;只从官方 `kitfunso/hippo-memory` 的明确 tag/commit 移植 `extensions/pi-extension/`,保留官方 README、LICENSE 和版权。
|
||||
- 官方 npm tarball 当前不包含 Pi Extension,因此根包直接打包并加载本地源码;不得同时加载第三方 `@the-forge-flow/hippo-memory-pi` 或第二个 Hippo Pi Extension。
|
||||
- CLI 版本权威源是 `config/hippo-memory-version`;修改它时必须同步上游源码快照、README、AGENTS、安装/升级 mock 测试和扩展加载验证。
|
||||
- 扩展依赖 PATH 中的全局 `hippo`。CLI 缺失时必须 fail-safe,不得阻止其他扩展加载;项目未执行 `hippo init` 时不得擅自创建 `.hippo/`。
|
||||
- 扩展入口是 `pi-hermes-memory/src/index.ts`,来源记录是 `pi-hermes-memory/UPSTREAM.md`;只从官方 `chandra447/pi-hermes-memory` 的明确 tag/commit 移植并保留官方 README、LICENSE 和版权。
|
||||
- 根包必须通过 `file:./pi-hermes-memory` 提供依赖并直接加载仓库源码;不得同时安装或加载 npm 预编译入口,也不得保留第二个记忆扩展(`hippo-memory-pi` 已移除)。
|
||||
- `package.json` 的 `pi.extensions` 必须把 `./pi-hermes-memory/src/index.ts` 排在 `./pi-compaction/index.ts` 之前,否则 `flushOnCompact` 会被 `pi-compaction` 的 `{ cancel: true }` 静默跳过;根 `tests/extension-load.test.ts` 必须断言该顺序。
|
||||
- 六个记忆工具必须在 `pi-tool-search/extensions/config.ts` 的 `BUNDLE_TOOL_SEARCH_DEFAULTS.alwaysEnabled` 中常驻,并由 `pi-tool-search/extensions/bundle-groups.ts` 的 `memory-recall` / `memory-management` 分组提供目录元数据;`context_tree_query` 继续留在 `memory-recall`。新增、删除或重命名这些工具时必须同步 `bundle-groups.ts`、Tool Search 测试和权限策略。
|
||||
- 依赖必须保持 `better-sqlite3@^13.0.3`:13.x 运行时使用包内 N-API 预编译二进制 `prebuilds/<platform>.node`,不依赖 node-gyp 编译产物,必须能由 npm 嵌套安装并与根 `better-sqlite3@12.11.1` 并存。不得把它降级回 12.x(Node 24 下会在进程退出时 SIGABRT),也不得让根包提升为单一版本。
|
||||
- `devDependencies` 中的 `@earendil-works/*` 必须与根包当前版本精确一致。若写成 `^0.80.2` 这类与根不兼容的范围,npm 会在本目录嵌套安装第二套 Pi runtime,违反"不得在组合包内再安装一套 Pi runtime"的纪律;`typebox`、`tsx`、`typescript`、`@types/better-sqlite3` 同理,必须钉到根已有版本。
|
||||
- 全局记忆、项目记忆与 skill 位于 `~/.pi/agent/pi-hermes-memory/` 与 `~/.pi/agent/projects-memory/`,配置位于 `~/.pi/agent/hermes-memory-config.json`;这些属于用户数据,安装、升级与卸载脚本不得创建、迁移或删除。
|
||||
- 修改工具注册、`before_agent_start` 注入策略、`resources_discover` 目录注册、SQLite 迁移或 consolidation 后,必须同步包内 README/CHANGELOG/docs,并运行根扩展联合加载与 Tool Search 测试。
|
||||
|
||||
## `pi-extension-codex-fast-mode` 开发约定
|
||||
|
||||
@@ -274,7 +279,7 @@
|
||||
|
||||
安装、升级、卸载、搜索或 SSH 配置脚本变化时,至少运行 `sh -n install.sh`、`sh -n update.sh`、`sh -n uninstall.sh`、`sh -n search_config.sh`、`sh -n ssh_config.sh` 和 ShellCheck,并核对脚本仍具有可执行权限、仍包含在根 `package.json` 的 `files` 中、README 描述与实际流程一致。Git 升级中断恢复必须用隔离 agent dir 和 mock `pi` / `git` 覆盖合法标记连续清理后成功、无标记保持失败、checkout 身份不匹配拒绝清理及单次 Pi 重试边界。Tool Search settings 迁移必须用隔离 agent dir 覆盖完整旧快照迁移、当前值幂等、自定义配置保留、无效 JSON fail-safe、备份/权限、`pi update` 失败不迁移和 `PI_CODING_AGENT_DIR`。搜索配置测试只能使用虚拟 key 和隔离 HOME;SSH 配置测试必须使用隔离 HOME、虚拟凭据与 mock transport,不得连接真实服务器或把真实密码写入测试输出。涉及真实 `pi install`、`pi update`、`pi remove`、Homebrew、远程安装器、Git 克隆、真实 SSH 连接或真实用户终端配置的端到端验证属于外部写操作,未经明确要求不得执行;可以使用隔离的临时 HOME 和 mock 命令验证分支行为。
|
||||
|
||||
Hippo 脚本变化必须额外用隔离 HOME/PATH 和 mock `pi`、`npm`、`hippo` 验证:安装缺失 CLI 但不调用 `hippo init`、相同版本不升级、不同版本精确升级、未知来源 CLI 不替换、卸载默认保留、明确确认后只卸载 npm 全局包且保留数据。不得在测试中执行真实全局 npm 写入或修改真实 `.hippo/`。
|
||||
记忆扩展变化必须额外验证:`pi-hermes-memory` 在隔离临时 Pi agent 目录中可加载且注册 6 个工具、`pi.extensions` 中排在 `pi-compaction` 之前、Tool Search 目录把六个工具归入 `memory-recall` / `memory-management` 且全部常驻、权限策略默认 `allow`、`better-sqlite3@13` 嵌套安装与根 `12.11.1` 并存。不得在测试中连接真实数据库或修改真实 `~/.pi/agent/` 记忆数据。
|
||||
|
||||
在 `pi-permission-system/` 内至少运行 `npm run typecheck`、`npm run test` 和 `npm run build`。涉及内置 AutoReview、硬防御或后台分析时,还要验证 authorizer 对普通工具与 `path` 的 `allow` / `deny` / `defer`、hard-guard precedence、500 次 checkpoint、owner-only 持久化、shutdown resume 与 same-session follow-up。
|
||||
|
||||
@@ -295,28 +300,13 @@ Hippo 脚本变化必须额外用隔离 HOME/PATH 和 mock `pi`、`npm`、`hippo
|
||||
|
||||
若环境缺少依赖或未执行某项验证,交付时明确说明,不以静态检查代替运行结果。
|
||||
|
||||
<!-- hippo:start -->
|
||||
## Project Memory (Hippo)
|
||||
<!-- pi-hermes-memory:start -->
|
||||
## 项目记忆(pi-hermes-memory)
|
||||
|
||||
At the start of every task, run:
|
||||
```bash
|
||||
hippo context --auto --budget 1500
|
||||
```
|
||||
Read the output before writing any code.
|
||||
持久记忆由 `pi-hermes-memory` 提供,默认 `policy-only` 模式:system prompt 只注入 `<memory-policy>` 指令,不注入记忆正文。
|
||||
|
||||
On errors or unexpected behaviour:
|
||||
```bash
|
||||
hippo remember "<description of what went wrong>" --error
|
||||
```
|
||||
|
||||
On task completion:
|
||||
```bash
|
||||
hippo outcome --good
|
||||
```
|
||||
|
||||
When Hippo's Codex wrapper is installed, session-end capture runs automatically.
|
||||
If the wrapper is not installed, capture a brief summary manually:
|
||||
```bash
|
||||
hippo capture --stdin <<< '<decisions, errors, lessons — 2-5 bullets>'
|
||||
```
|
||||
<!-- hippo:end -->
|
||||
- 任务开始时若可能依赖历史上下文,先调用 `memory_search`;需要历史对话原话时用 `session_search`。
|
||||
- 发现值得持久化的事实、偏好、约定、纠正或失败教训时,主动用 `memory_add` 保存,不必等用户开口;更新或删除用 `memory_replace` / `memory_remove`。
|
||||
- 完成需要反复试错、或用户教了具体流程的任务后,用 `skill_manage` 把可复用流程沉淀为 skill(`scope: "global"` 跨项目,`scope: "project"` 本项目)。
|
||||
- 记忆与 skill 数据位于 `~/.pi/agent/pi-hermes-memory/` 与 `~/.pi/agent/projects-memory/`,属于用户数据,不得提交、自动迁移或删除。
|
||||
<!-- pi-hermes-memory:end -->
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
- `extensions/context-view.ts`:通过 `/context` 只读显示 `pi-compaction` 的 effective context,不建立第二套上下文口径。
|
||||
- 本仓库维护的 `pi-extension-codex-fast-mode`:基于 `@firstpick/pi-extension-codex-fast-mode@0.1.1`,为 Codex provider 提供跨会话持久化的 `/fast-mode`。
|
||||
- `pi-lsp@0.1.7`:为 TypeScript/JavaScript、Kotlin 和 Java 提供 LSP 诊断、跳转与符号工具;TypeScript 后端由组合包内置。
|
||||
- `hippo-memory-pi/`:从官方 `hippo-memory v1.33.0` 源码导入的 Pi Extension,提供项目记忆注入、错误捕获和 sleep consolidation。
|
||||
- `pi-hermes-memory/`:从官方 `pi-hermes-memory v0.9.10` 源码导入的持久记忆扩展,提供全局/项目/用户/失败四类记忆、SQLite FTS5 会话与记忆检索、`SKILL.md` 过程记忆、correction 检测与自动 consolidation。
|
||||
- 本仓库维护的 `pi-minimal-footer`:基于 `@ogulcancelik/pi-minimal-footer@0.1.10`,用紧凑的上下文仪表和订阅用量条替换默认 footer,并在 Codex Fast 模式开启时显示 `Fast on`。
|
||||
- 本仓库维护的 `pi-notify`:基于 `@smoose/pi-notify@0.1.1`,使用 Kitty OSC 99 在 Pi 完全 settled 或权限系统即将请求人工确认时通知,并支持点击精确聚焦来源 Kitty 窗口。
|
||||
- 本仓库维护的 `pi-compaction`:以增量投影统一确定性工具结果压缩、后台语义摘要、effective token 计量、`context_tree_query`、portable checkpoint 与 Codex native compaction。
|
||||
@@ -108,14 +108,22 @@ PI_PACKAGE_SOURCE="git:git@bitbucket.org:siakitem/my-pi.git" ./update.sh
|
||||
|
||||
根 `package.json` 对外部依赖使用精确版本,对本仓库维护的包(包括 Codex Fast mode)使用 `file:` 路径;`package-lock.json` 固定完整依赖树。不要再用项目级 `.pi/settings.json` 重复安装这些扩展,否则同一扩展可能被加载两次。
|
||||
|
||||
Hippo 官方 Pi Extension 存在于上游 Git 仓库的 `extensions/pi-extension/`,但没有包含在
|
||||
`hippo-memory@1.33.0` npm tarball 中。本仓库从官方 `v1.33.0` /
|
||||
`e928179a3b35e8fe5837878aed071d6025ced45c` 导入源码到 `hippo-memory-pi/` 并直接加载;
|
||||
匹配的 CLI 版本由 `config/hippo-memory-version` 声明。`install.sh` 会在 CLI 缺失时询问是否执行
|
||||
`npm install -g hippo-memory@<固定版本>`,但安装脚本不会选择或初始化项目。进入目标项目后可在 Pi 中执行
|
||||
`/plugin_init`:命令会显示当前目录并要求确认,以 `.codegraph/codegraph.db` 和 `.hippo/hippo.db` 为
|
||||
初始化标志,只为缺失状态运行 `codegraph init` 与 `hippo init`;全部成功后通过 Pi 官方热重载重新连接 CodeGraph MCP 并重新触发 Hippo
|
||||
session start。也可以继续在终端手动执行两个 CLI 的 `init` 命令。
|
||||
持久记忆由本仓库维护的 `pi-hermes-memory` 提供。它从官方 `chandra447/pi-hermes-memory`
|
||||
的 `v0.9.10` / `df624056a2e8c68bc89ba0a81e1c120856ec9665` 导入源码到 `pi-hermes-memory/` 并直接加载,
|
||||
通过本地 `file:` 依赖安装,不需要额外的机器级 CLI。
|
||||
|
||||
默认 `policy-only` 模式只把 `<memory-policy>` 指令注入 system prompt,不注入记忆正文,因此不会改变
|
||||
prompt 前缀、对前缀缓存友好。模型需要历史上下文时主动调用 `memory_search` / `session_search`,
|
||||
写入用 `memory_add` / `memory_replace` / `memory_remove`,过程记忆用 `skill_manage`。这六个工具由
|
||||
Tool Search 常驻,保证 policy 指示与模型可见工具一致。
|
||||
|
||||
记忆与 skill 数据位于 `~/.pi/agent/pi-hermes-memory/`(全局)与 `~/.pi/agent/projects-memory/<项目>/`
|
||||
(项目级),可选配置为 `~/.pi/agent/hermes-memory-config.json`。这些是用户数据,安装、升级与卸载脚本
|
||||
不会创建、迁移或删除。
|
||||
|
||||
进入目标项目后可在 Pi 中执行 `/plugin_init`:命令会显示当前目录并要求确认,以
|
||||
`.codegraph/codegraph.db` 为初始化标志,只为缺失状态运行 `codegraph init`;全部成功后通过 Pi 官方热重载
|
||||
重新连接 CodeGraph MCP。也可以继续在终端手动执行 `codegraph init`。
|
||||
|
||||
默认配置不需要系统 `rtk` CLI。只有以后在 `/rtk` 中主动开启 RTK command rewriting 时,才需要另外安装 `rtk` 可执行文件。
|
||||
|
||||
@@ -261,13 +269,13 @@ codegraph --version
|
||||
codegraph status
|
||||
```
|
||||
|
||||
在当前项目需要同时初始化 CodeGraph 和 Hippo 时,可直接执行:
|
||||
在当前项目需要初始化 CodeGraph 时,可直接执行:
|
||||
|
||||
```text
|
||||
/plugin_init
|
||||
```
|
||||
|
||||
该命令拒绝文件系统根目录和用户主目录,先确认两个 CLI 均可用,再依次初始化缺失数据库状态。任一步失败都不会热重载,也不会回滚此前已成功完成的初始化;两者已初始化时仍可用该命令快速热重载扩展。
|
||||
该命令拒绝文件系统根目录和用户主目录,先确认 CLI 可用,再初始化缺失数据库状态。失败时不会热重载,也不会回滚此前已成功完成的初始化;已初始化时仍可用该命令快速热重载扩展。
|
||||
|
||||
### 统一权限系统、硬防御与 AutoReview
|
||||
|
||||
@@ -310,23 +318,30 @@ brew install JetBrains/utils/kotlin-lsp
|
||||
|
||||
JDT LS 需要 Java 21 或更高版本;可使用带 `jdtls` wrapper 的官方发行包或系统包。
|
||||
|
||||
### Hippo Memory
|
||||
### 持久记忆(pi-hermes-memory)
|
||||
|
||||
官方扩展要求全局 `hippo` CLI,并要求目标项目存在 `.hippo/`。组合包加载仓库内官方扩展源码,
|
||||
注册 `hippo_recall`、`hippo_remember`、`hippo_outcome`、`hippo_status` 和 `hippo_context` 五个工具。
|
||||
Session 启动时它注入项目记忆上下文,失败工具结果经过噪声过滤、每 Session 上限和去重后自动记录,
|
||||
Session 关闭时运行 `hippo sleep`,从 Git 学习、整理记忆并执行官方共享逻辑。项目 `.hippo/` 是
|
||||
本机记忆状态,本仓库默认忽略提交。
|
||||
组合包加载仓库内 `pi-hermes-memory` 源码并注册六个工具:
|
||||
|
||||
Hermes 的 `memory_add` / `session_search` / `skill_manage` 与 Markdown/SQLite 数据不会被 Hippo 自动迁移;
|
||||
切换扩展不会删除 `~/.pi/agent/pi-hermes-memory/` 或 `~/.pi/agent/projects-memory/` 中的旧数据。需要保留的
|
||||
旧记忆应另行审核后导入,不在安装、升级或卸载流程中自动转换或删除。
|
||||
| 工具 | 作用 |
|
||||
|---|---|
|
||||
| `memory_search` | 检索持久记忆:全局、用户、项目、失败四类 |
|
||||
| `session_search` | 在 SQLite FTS5 trigram 索引中检索历史会话原话 |
|
||||
| `memory_add` | 新增一条持久记忆 |
|
||||
| `memory_replace` | 按子串匹配修改已有记忆 |
|
||||
| `memory_remove` | 按子串匹配删除已有记忆 |
|
||||
| `skill_manage` | 创建、查看、修补、重写或删除 `SKILL.md` 过程记忆 |
|
||||
|
||||
`install.sh` 只对缺失的 Hippo CLI 询问一次,默认拒绝;缺少 npm 时保留已完成的组合包安装并报告失败。
|
||||
脚本不会扫描仓库、选择目录或执行 `hippo init`。`update.sh` 只处理已经存在且确认属于 npm 全局安装的
|
||||
Hippo CLI:从升级后的组合包读取固定版本,先通过 npm 确认版本存在,再比较本地版本,只有不同时才升级。
|
||||
`uninstall.sh` 移除组合包后询问是否一并执行 `npm uninstall -g hippo-memory`,默认保留;无论选择什么,
|
||||
`.hippo/` 和用户记忆数据都不会被脚本删除。
|
||||
默认 `policy-only` 模式不在 system prompt 中注入记忆正文,只注入 `<memory-policy>` 指令告诉模型何时
|
||||
检索、何时写入。因此首轮 token 成本很低,也保持 prompt 前缀稳定。写工具只写扩展自己的数据目录,
|
||||
不触碰工作区。
|
||||
|
||||
`skill_manage` 是扩展提供的能力:Pi 宿主只负责发现并加载 skill 目录,本身没有创建 skill 的工具。
|
||||
该扩展同时注册 `~/.pi/agent/projects-memory/<项目>/skills/` 供 Pi 发现,因此不加载它时,这些既有
|
||||
skill 也不会出现在 Pi 的 skill 索引中。
|
||||
|
||||
记忆与 skill 位于 `~/.pi/agent/pi-hermes-memory/`(全局)和 `~/.pi/agent/projects-memory/<项目>/`
|
||||
(项目级),配置位于 `~/.pi/agent/hermes-memory-config.json`。这些是用户数据:安装、升级和卸载脚本都
|
||||
不会创建、迁移或删除它们,也不会自动转换旧 Hippo `.hippo/` 数据。
|
||||
|
||||
### Kitty 完成与权限确认通知
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
1.33.0
|
||||
@@ -45,6 +45,17 @@
|
||||
"tool_search": "allow",
|
||||
"ask_user_question": "allow",
|
||||
|
||||
// Hermes memory tools write only to the extension's own agent-directory
|
||||
// stores and never touch the workspace, so they stay allowed. memory_search
|
||||
// is invoked by the injected memory policy on most turns and must not
|
||||
// trigger an AutoReview round trip each time.
|
||||
"memory_search": "allow",
|
||||
"session_search": "allow",
|
||||
"memory_add": "allow",
|
||||
"memory_replace": "allow",
|
||||
"memory_remove": "allow",
|
||||
"skill_manage": "allow",
|
||||
|
||||
"ssh_connect": "ask",
|
||||
"ssh_cd": "ask",
|
||||
"ssh_read": "ask",
|
||||
|
||||
@@ -16,8 +16,8 @@ export type PluginInitExecutor = (
|
||||
) => Promise<ExecResult>;
|
||||
|
||||
interface InitTarget {
|
||||
name: "CodeGraph" | "Hippo";
|
||||
command: "codegraph" | "hippo";
|
||||
name: "CodeGraph";
|
||||
command: "codegraph";
|
||||
database: string;
|
||||
initArgs(cwd: string): string[];
|
||||
}
|
||||
@@ -29,12 +29,6 @@ const INIT_TARGETS: InitTarget[] = [
|
||||
database: join(".codegraph", "codegraph.db"),
|
||||
initArgs: (cwd) => ["init", cwd],
|
||||
},
|
||||
{
|
||||
name: "Hippo",
|
||||
command: "hippo",
|
||||
database: join(".hippo", "hippo.db"),
|
||||
initArgs: () => ["init"],
|
||||
},
|
||||
];
|
||||
|
||||
async function isFile(path: string): Promise<boolean> {
|
||||
@@ -100,7 +94,7 @@ export async function initializeMissingPlugins(
|
||||
): Promise<void> {
|
||||
const projectRoot = validatePluginInitCwd(cwd);
|
||||
|
||||
// Preflight both CLIs before creating either project's state.
|
||||
// Preflight every CLI before creating project state.
|
||||
for (const target of INIT_TARGETS) {
|
||||
await executeChecked(exec, target, ["--version"], projectRoot, "检测");
|
||||
}
|
||||
@@ -123,7 +117,7 @@ export async function initializeMissingPlugins(
|
||||
|
||||
export default function pluginInitExtension(pi: ExtensionAPI): void {
|
||||
pi.registerCommand("plugin_init", {
|
||||
description: "Initialize CodeGraph and Hippo for the current project, then hot-reload Pi",
|
||||
description: "Initialize CodeGraph for the current project, then hot-reload Pi",
|
||||
handler: async (_args, ctx) => {
|
||||
if (!ctx.hasUI) return;
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Keith So
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,77 +0,0 @@
|
||||
# Hippo Memory - Pi Extension
|
||||
|
||||
> **my-pi packaging note:** this official source is bundled and loaded directly by the root package; do not copy it to another Pi extension directory. Use the root `install.sh`, `update.sh`, and `uninstall.sh` prompts to manage the matching global CLI. Upstream's original manual instructions are retained below for reference.
|
||||
|
||||
Biologically-inspired memory for [Pi coding agent](https://github.com/badlogic/pi-mono). Memories decay by default, retrieval strengthens them, errors stick longer, and sleep consolidation compresses episodes into patterns.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
# Install hippo CLI
|
||||
npm install -g hippo-memory
|
||||
|
||||
# Init hippo in your project (or scan all repos)
|
||||
hippo init
|
||||
# or: hippo init --scan ~
|
||||
|
||||
# Copy extension to Pi's extensions directory
|
||||
cp -r extensions/pi-extension ~/.pi/agent/extensions/hippo-memory
|
||||
```
|
||||
|
||||
Or for project-level:
|
||||
```bash
|
||||
cp -r extensions/pi-extension .pi/extensions/hippo-memory
|
||||
```
|
||||
|
||||
## What it does
|
||||
|
||||
| Automatic | When |
|
||||
|---|---|
|
||||
| Injects memory context | Session start |
|
||||
| Captures tool errors | After any failed tool call |
|
||||
| Learns from git | On `hippo sleep` (session end) |
|
||||
| Consolidates memories | On `hippo sleep` (session end) |
|
||||
| Shares to global store | On `hippo sleep` (session end) |
|
||||
|
||||
Error capture has three filters to prevent memory pollution:
|
||||
1. Noise pattern filter (timeouts, ECONNREFUSED, etc.)
|
||||
2. Per-session rate limit (max 5 error memories)
|
||||
3. Per-session deduplication
|
||||
|
||||
## Tools
|
||||
|
||||
The extension registers 5 tools for the LLM:
|
||||
|
||||
| Tool | Description |
|
||||
|---|---|
|
||||
| `hippo_recall` | Search memories by topic |
|
||||
| `hippo_remember` | Store a new memory |
|
||||
| `hippo_outcome` | Report if memories were helpful |
|
||||
| `hippo_status` | Check memory health |
|
||||
| `hippo_context` | Smart context from git state |
|
||||
|
||||
## Config
|
||||
|
||||
Edit the `DEFAULT_CONFIG` object at the top of `index.ts`:
|
||||
|
||||
```typescript
|
||||
const DEFAULT_CONFIG = {
|
||||
budget: 4000, // Token budget for recall
|
||||
contextBudget: 1500, // Token budget for auto-context
|
||||
framing: 'observe', // observe | suggest | assert
|
||||
autoContext: true, // Inject context at session start
|
||||
autoLearn: true, // Capture tool errors
|
||||
autoSleep: true, // Run consolidation on exit
|
||||
maxErrorsPerSession: 5, // Error capture rate limit
|
||||
};
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- `hippo-memory` CLI installed globally: `npm install -g hippo-memory`
|
||||
- `.hippo/` initialized in your project: `hippo init`
|
||||
- Pi coding agent
|
||||
|
||||
## How it differs from the OpenClaw plugin
|
||||
|
||||
Same core behavior. The Pi extension uses Pi's event system (`session_start`, `tool_result`, `session_shutdown`) instead of OpenClaw's hook system. Both use `execFileSync` with args arrays (no shell injection).
|
||||
@@ -1,11 +0,0 @@
|
||||
# 上游来源
|
||||
|
||||
- 上游仓库:<https://github.com/kitfunso/hippo-memory>
|
||||
- 导入版本:`v1.33.0`
|
||||
- 导入 commit:`e928179a3b35e8fe5837878aed071d6025ced45c`
|
||||
- 导入源码路径:`extensions/pi-extension/`
|
||||
- 初始导入日期:2026-08-19
|
||||
|
||||
本目录包含官方 Hippo Memory Pi 扩展源码。该源码存在于上游 Git 仓库中,但被排除在 `hippo-memory@1.33.0` 的 npm tarball 之外。该扩展由 `my-pi` 直接维护,不是 submodule,也不保留嵌套 `.git` 目录。
|
||||
|
||||
匹配的 CLI 版本声明在 `config/hippo-memory-version` 中,由组合包便捷脚本作为可选的机器级依赖管理。未来同步上游时必须从明确 tag/commit 移植,并与本地改动对照审查,不得整体覆盖本目录。
|
||||
@@ -1,356 +0,0 @@
|
||||
/**
|
||||
* Hippo Memory - Pi Coding Agent Extension
|
||||
*
|
||||
* Auto-injects memory context at session start, captures tool errors,
|
||||
* and runs consolidation on shutdown. Registers hippo tools for the LLM.
|
||||
*
|
||||
* Install: copy to ~/.pi/agent/extensions/hippo-memory/
|
||||
* Or add to .pi/extensions/ in your project.
|
||||
*
|
||||
* Requires: hippo-memory CLI installed globally (npm install -g hippo-memory)
|
||||
*/
|
||||
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Config
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
interface HippoConfig {
|
||||
budget: number;
|
||||
contextBudget: number;
|
||||
framing: 'observe' | 'suggest' | 'assert';
|
||||
autoContext: boolean;
|
||||
autoLearn: boolean;
|
||||
autoSleep: boolean;
|
||||
maxErrorsPerSession: number;
|
||||
}
|
||||
|
||||
const DEFAULT_CONFIG: HippoConfig = {
|
||||
budget: 4000,
|
||||
contextBudget: 1500,
|
||||
framing: 'observe',
|
||||
autoContext: true,
|
||||
autoLearn: true,
|
||||
autoSleep: true,
|
||||
maxErrorsPerSession: 5,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Noise filtering (matches openclaw-plugin patterns)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const NOISE_ERROR_PATTERNS: RegExp[] = [
|
||||
/Local media path is not under an allowed directory/i,
|
||||
/timed out\.?\s*Restart/i,
|
||||
/EISDIR:\s*illegal operation on a directory/i,
|
||||
/Missing required parameter:\s*path/i,
|
||||
/ENOENT:\s*no such file or directory/i,
|
||||
/EACCES:\s*permission denied/i,
|
||||
/EPERM:\s*operation not permitted/i,
|
||||
/socket hang up/i,
|
||||
/ECONNREFUSED/i,
|
||||
/ECONNRESET/i,
|
||||
/ERR_SOCKET_CONNECTION_TIMEOUT/i,
|
||||
/net::ERR_/i,
|
||||
/Navigation timeout/i,
|
||||
];
|
||||
|
||||
function isNoiseError(error: string): boolean {
|
||||
return NOISE_ERROR_PATTERNS.some((p) => p.test(error));
|
||||
}
|
||||
|
||||
/** True for a string value, without assuming the input's shape — used on
|
||||
* fields read off the untyped Pi extension event/plugin surface. */
|
||||
function isString<T>(value: T): value is T & string {
|
||||
return typeof value === 'string';
|
||||
}
|
||||
|
||||
function hashError(toolName: string, error: string): string {
|
||||
return `${toolName}::${error.replace(/\s+/g, ' ').trim().slice(0, 80).toLowerCase()}`;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Session state
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
let sessionErrorCount = 0;
|
||||
const sessionErrorHashes = new Set<string>();
|
||||
let sessionMemoryCount = 0;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Hippo CLI wrapper (no shell, args as array)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function runHippo(args: readonly string[], cwd?: string): string {
|
||||
try {
|
||||
const result = execFileSync('hippo', args, {
|
||||
cwd: cwd || process.cwd(),
|
||||
encoding: 'utf8',
|
||||
timeout: 30_000,
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
});
|
||||
// `encoding: 'utf8'` above selects the ExecFileSyncOptionsWithStringEncoding
|
||||
// overload, so `result` is always a `string` here — no runtime check needed.
|
||||
return result.trim();
|
||||
} catch (err: any) {
|
||||
return err.stdout?.trim() || err.message || 'hippo command failed';
|
||||
}
|
||||
}
|
||||
|
||||
function hippoAvailable(): boolean {
|
||||
try {
|
||||
execFileSync('hippo', ['status'], {
|
||||
encoding: 'utf8',
|
||||
timeout: 5000,
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
});
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function hippoInitialized(cwd: string): boolean {
|
||||
return existsSync(join(cwd, '.hippo'));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Extension entry point
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export default function (pi: any) {
|
||||
// Check hippo is available
|
||||
if (!hippoAvailable()) {
|
||||
console.error('[hippo] hippo-memory CLI not found. Install: npm install -g hippo-memory');
|
||||
return;
|
||||
}
|
||||
|
||||
const config = { ...DEFAULT_CONFIG };
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Session start: inject memory context
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
pi.on('session_start', async (_event: any, ctx: any) => {
|
||||
sessionErrorCount = 0;
|
||||
sessionErrorHashes.clear();
|
||||
sessionMemoryCount = 0;
|
||||
|
||||
if (!config.autoContext) return;
|
||||
|
||||
const cwd = ctx.cwd || process.cwd();
|
||||
if (!hippoInitialized(cwd)) return;
|
||||
|
||||
const context = runHippo(
|
||||
['context', '--auto', '--budget', String(config.contextBudget), '--framing', config.framing],
|
||||
cwd,
|
||||
);
|
||||
|
||||
if (context && context.length > 10 && !context.includes('No hippo store')) {
|
||||
return {
|
||||
systemPromptAppend: `\n\n## Project Memory (Hippo)\n\n${context}\n\nWhen you learn something important, tell the user to run: hippo remember "<lesson>"\nWhen you encounter errors, suggest: hippo remember "<error description>" --error`,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Tool errors: auto-capture (with noise filtering + rate limiting + dedup)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
if (config.autoLearn) {
|
||||
pi.on('tool_result', async (event: any, ctx: any) => {
|
||||
if (!event.isError) return;
|
||||
|
||||
const error = isString(event.content)
|
||||
? event.content
|
||||
: Array.isArray(event.content)
|
||||
? event.content.map((c: any) => c.text || '').join(' ')
|
||||
: '';
|
||||
|
||||
if (!error || error.length < 10) return;
|
||||
|
||||
// Filter 1: noise
|
||||
if (isNoiseError(error)) return;
|
||||
|
||||
// Filter 2: rate limit
|
||||
if (sessionErrorCount >= config.maxErrorsPerSession) return;
|
||||
|
||||
// Filter 3: dedup
|
||||
const hash = hashError(event.toolName || 'unknown', error);
|
||||
if (sessionErrorHashes.has(hash)) return;
|
||||
|
||||
const cwd = ctx.cwd || process.cwd();
|
||||
if (!hippoInitialized(cwd)) return;
|
||||
|
||||
const toolTag = (event.toolName || 'unknown')
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9-]+/g, '-')
|
||||
.replace(/^-+|-+$/g, '')
|
||||
.slice(0, 30);
|
||||
|
||||
const truncated = error.replace(/\s+/g, ' ').trim().slice(0, 500);
|
||||
const text = `Tool '${event.toolName || 'unknown'}' failed: ${truncated}`;
|
||||
|
||||
const args: string[] = ['remember', text, '--error', '--observed', '--tag', 'pi-agent'];
|
||||
if (toolTag) args.push('--tag', toolTag);
|
||||
|
||||
const result = runHippo(args, cwd);
|
||||
if (result.includes('Remembered [')) {
|
||||
sessionErrorHashes.add(hash);
|
||||
sessionErrorCount++;
|
||||
sessionMemoryCount++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Session shutdown: run consolidation
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
pi.on('session_shutdown', async (_event: any, ctx: any) => {
|
||||
if (!config.autoSleep) return;
|
||||
|
||||
const cwd = ctx.cwd || process.cwd();
|
||||
if (!hippoInitialized(cwd)) return;
|
||||
|
||||
// Only sleep if we captured some memories this session
|
||||
if (sessionMemoryCount < 1) {
|
||||
// Still run sleep for the auto-learn-from-git + auto-share features
|
||||
try { runHippo(['sleep'], cwd); } catch { /* best effort */ }
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
runHippo(['sleep'], cwd);
|
||||
} catch {
|
||||
// Best effort — don't block shutdown
|
||||
}
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Tools: hippo_recall
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
pi.registerTool({
|
||||
name: 'hippo_recall',
|
||||
description: 'Search project memory for relevant context. Returns memories ranked by relevance and strength.',
|
||||
promptSnippet: 'hippo_recall: Search project memory by topic',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
query: { type: 'string', description: 'What to search for (natural language)' },
|
||||
budget: { type: 'number', description: 'Max tokens to return (default: 4000)' },
|
||||
},
|
||||
required: ['query'],
|
||||
},
|
||||
async execute(_toolCallId: string, params: any, _signal: any, _onUpdate: any, ctx: any) {
|
||||
const cwd = ctx.cwd || process.cwd();
|
||||
const budget = params.budget || config.budget;
|
||||
const result = runHippo(
|
||||
['recall', params.query, '--budget', String(budget), '--framing', config.framing],
|
||||
cwd,
|
||||
);
|
||||
return { content: [{ type: 'text', text: result || 'No relevant memories found.' }] };
|
||||
},
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Tools: hippo_remember
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
pi.registerTool({
|
||||
name: 'hippo_remember',
|
||||
description: 'Store a lesson, insight, or error in project memory. Memories decay over time unless retrieved.',
|
||||
promptSnippet: 'hippo_remember: Store a memory (lessons, errors, decisions)',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
text: { type: 'string', description: 'The memory content' },
|
||||
error: { type: 'boolean', description: 'Tag as error (doubles retention)' },
|
||||
tag: { type: 'string', description: 'Optional tag for categorization' },
|
||||
pin: { type: 'boolean', description: 'Pin memory (never decays)' },
|
||||
},
|
||||
required: ['text'],
|
||||
},
|
||||
async execute(_toolCallId: string, params: any, _signal: any, _onUpdate: any, ctx: any) {
|
||||
const cwd = ctx.cwd || process.cwd();
|
||||
const args: string[] = ['remember', params.text];
|
||||
if (params.error) args.push('--error');
|
||||
if (params.pin) args.push('--pin');
|
||||
if (params.tag) {
|
||||
const safe = params.tag.toLowerCase().replace(/[^a-z0-9-]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 30);
|
||||
if (safe) args.push('--tag', safe);
|
||||
}
|
||||
const result = runHippo(args, cwd);
|
||||
if (result.includes('Remembered [')) sessionMemoryCount++;
|
||||
return { content: [{ type: 'text', text: result || 'Memory stored.' }] };
|
||||
},
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Tools: hippo_outcome
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
pi.registerTool({
|
||||
name: 'hippo_outcome',
|
||||
description: 'Report whether recalled memories were helpful. Strengthens or weakens them.',
|
||||
promptSnippet: 'hippo_outcome: Rate recalled memories as good or bad',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
good: { type: 'boolean', description: 'true = helpful, false = not helpful' },
|
||||
},
|
||||
required: ['good'],
|
||||
},
|
||||
async execute(_toolCallId: string, params: any, _signal: any, _onUpdate: any, ctx: any) {
|
||||
const cwd = ctx.cwd || process.cwd();
|
||||
const flag = params.good ? '--good' : '--bad';
|
||||
const result = runHippo(['outcome', flag], cwd);
|
||||
return { content: [{ type: 'text', text: result || 'Outcome recorded.' }] };
|
||||
},
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Tools: hippo_status
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
pi.registerTool({
|
||||
name: 'hippo_status',
|
||||
description: 'Show memory health: counts, strength distribution, conflicts.',
|
||||
promptSnippet: 'hippo_status: Check memory store health',
|
||||
parameters: { type: 'object', properties: {} },
|
||||
async execute(_toolCallId: string, _params: any, _signal: any, _onUpdate: any, ctx: any) {
|
||||
const cwd = ctx.cwd || process.cwd();
|
||||
const result = runHippo(['status'], cwd);
|
||||
return { content: [{ type: 'text', text: result || 'No hippo store found.' }] };
|
||||
},
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Tools: hippo_context
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
pi.registerTool({
|
||||
name: 'hippo_context',
|
||||
description: 'Get smart memory context based on current git state and task.',
|
||||
promptSnippet: 'hippo_context: Smart context injection from git state',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
budget: { type: 'number', description: 'Token budget (default: 1500)' },
|
||||
},
|
||||
},
|
||||
async execute(_toolCallId: string, params: any, _signal: any, _onUpdate: any, ctx: any) {
|
||||
const cwd = ctx.cwd || process.cwd();
|
||||
const budget = params.budget || config.contextBudget;
|
||||
const result = runHippo(
|
||||
['context', '--auto', '--budget', String(budget), '--framing', config.framing],
|
||||
cwd,
|
||||
);
|
||||
return { content: [{ type: 'text', text: result || 'No context available.' }] };
|
||||
},
|
||||
});
|
||||
}
|
||||
-60
@@ -13,7 +13,6 @@ fi
|
||||
|
||||
PACKAGE_SOURCE=${PI_PACKAGE_SOURCE:-git:git@bitbucket.org:siakitem/my-pi.git}
|
||||
POWERLEVEL10K_PRESET_FILE=$SCRIPT_DIR/config/p10k.zsh
|
||||
HIPPO_MEMORY_VERSION_FILE=$SCRIPT_DIR/config/hippo-memory-version
|
||||
CODEGRAPH_INSTALL_URL=https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.sh
|
||||
KITTY_INSTALL_URL=https://sw.kovidgoyal.net/kitty/installer.sh
|
||||
OH_MY_ZSH_INSTALL_URL=https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh
|
||||
@@ -92,43 +91,6 @@ install_codegraph() {
|
||||
command -v codegraph >/dev/null 2>&1
|
||||
}
|
||||
|
||||
read_hippo_memory_version() {
|
||||
if [ ! -f "$HIPPO_MEMORY_VERSION_FILE" ]; then
|
||||
warn "找不到 Hippo Memory 版本文件:$HIPPO_MEMORY_VERSION_FILE"
|
||||
return 1
|
||||
fi
|
||||
hippo_target_version=$(sed -n '1p' "$HIPPO_MEMORY_VERSION_FILE")
|
||||
case $hippo_target_version in
|
||||
''|*[!0-9.]*)
|
||||
warn "Hippo Memory 版本文件内容无效:$hippo_target_version"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
printf '%s\n' "$hippo_target_version"
|
||||
}
|
||||
|
||||
installed_hippo_memory_version() {
|
||||
hippo --version 2>/dev/null | sed -n 's/^[^0-9]*\([0-9][0-9.]*\).*$/\1/p' | sed -n '1p'
|
||||
}
|
||||
|
||||
install_hippo_memory() {
|
||||
if ! command -v npm >/dev/null 2>&1; then
|
||||
warn "安装 Hippo Memory CLI 需要 npm。"
|
||||
return 1
|
||||
fi
|
||||
hippo_target_version=$(read_hippo_memory_version) || return 1
|
||||
npm install -g "hippo-memory@$hippo_target_version" || return 1
|
||||
if ! command -v hippo >/dev/null 2>&1; then
|
||||
warn "npm 已完成安装,但当前 PATH 中仍找不到 hippo。"
|
||||
return 1
|
||||
fi
|
||||
hippo_installed_version=$(installed_hippo_memory_version)
|
||||
if [ "$hippo_installed_version" != "$hippo_target_version" ]; then
|
||||
warn "Hippo Memory CLI 版本校验失败:期望 $hippo_target_version,实际 ${hippo_installed_version:-未知}。"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
install_kotlin_lsp() {
|
||||
require_brew || return 1
|
||||
brew install JetBrains/utils/kotlin-lsp || return 1
|
||||
@@ -373,28 +335,6 @@ fi
|
||||
|
||||
say "Pi 组合包安装完成,开始检查终端环境和机器级 CLI 依赖。"
|
||||
|
||||
if command -v hippo >/dev/null 2>&1; then
|
||||
hippo_installed_version=$(installed_hippo_memory_version)
|
||||
hippo_target_version=$(read_hippo_memory_version || true)
|
||||
if [ -n "$hippo_installed_version" ] && [ "$hippo_installed_version" = "$hippo_target_version" ]; then
|
||||
say "✓ Hippo Memory CLI $hippo_installed_version 已安装"
|
||||
else
|
||||
say "✓ Hippo Memory CLI ${hippo_installed_version:-未知版本} 已安装;组合包目标版本为 ${hippo_target_version:-未知},可运行 update.sh 同步。"
|
||||
fi
|
||||
elif ask_to_install "Hippo Memory CLI(官方 Pi 记忆扩展运行时)"; then
|
||||
if install_hippo_memory; then
|
||||
say "✓ Hippo Memory CLI 安装完成"
|
||||
else
|
||||
record_install_failure "Hippo Memory CLI"
|
||||
fi
|
||||
else
|
||||
say "- 已跳过 Hippo Memory CLI;官方 Hippo Pi 扩展本次将不可用。"
|
||||
fi
|
||||
|
||||
if command -v hippo >/dev/null 2>&1; then
|
||||
say "- 未自动运行 hippo init;请由用户在需要启用记忆的项目目录中自行执行。"
|
||||
fi
|
||||
|
||||
kitty_ok=0
|
||||
if detect_kitty; then
|
||||
kitty_ok=1
|
||||
|
||||
Generated
+580
@@ -21,6 +21,7 @@
|
||||
"pi-chrome": "file:./pi-chrome",
|
||||
"pi-compaction": "file:./pi-compaction",
|
||||
"pi-hashline-edit": "0.8.3",
|
||||
"pi-hermes-memory": "file:./pi-hermes-memory",
|
||||
"pi-lsp": "0.1.7",
|
||||
"pi-mcp-adapter": "2.26.0",
|
||||
"pi-ssh": "file:./pi-ssh",
|
||||
@@ -2611,6 +2612,448 @@
|
||||
"node": ">=22.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz",
|
||||
"integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz",
|
||||
"integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz",
|
||||
"integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz",
|
||||
"integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz",
|
||||
"integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz",
|
||||
"integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz",
|
||||
"integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz",
|
||||
"integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz",
|
||||
"integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz",
|
||||
"integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz",
|
||||
"integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz",
|
||||
"integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz",
|
||||
"integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz",
|
||||
"integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz",
|
||||
"integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz",
|
||||
"integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz",
|
||||
"integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz",
|
||||
"integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz",
|
||||
"integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz",
|
||||
"integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz",
|
||||
"integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openharmony-arm64": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz",
|
||||
"integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openharmony"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz",
|
||||
"integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz",
|
||||
"integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz",
|
||||
"integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz",
|
||||
"integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@ff-labs/fff-bin-android-arm64": {
|
||||
"version": "0.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@ff-labs/fff-bin-android-arm64/-/fff-bin-android-arm64-0.10.5.tgz",
|
||||
@@ -4174,6 +4617,16 @@
|
||||
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/better-sqlite3": {
|
||||
"version": "7.6.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz",
|
||||
"integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/chai": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
|
||||
@@ -4561,6 +5014,18 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "6.4.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz",
|
||||
"integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/asn1": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||
@@ -5218,6 +5683,48 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz",
|
||||
"integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.28.2",
|
||||
"@esbuild/android-arm": "0.28.2",
|
||||
"@esbuild/android-arm64": "0.28.2",
|
||||
"@esbuild/android-x64": "0.28.2",
|
||||
"@esbuild/darwin-arm64": "0.28.2",
|
||||
"@esbuild/darwin-x64": "0.28.2",
|
||||
"@esbuild/freebsd-arm64": "0.28.2",
|
||||
"@esbuild/freebsd-x64": "0.28.2",
|
||||
"@esbuild/linux-arm": "0.28.2",
|
||||
"@esbuild/linux-arm64": "0.28.2",
|
||||
"@esbuild/linux-ia32": "0.28.2",
|
||||
"@esbuild/linux-loong64": "0.28.2",
|
||||
"@esbuild/linux-mips64el": "0.28.2",
|
||||
"@esbuild/linux-ppc64": "0.28.2",
|
||||
"@esbuild/linux-riscv64": "0.28.2",
|
||||
"@esbuild/linux-s390x": "0.28.2",
|
||||
"@esbuild/linux-x64": "0.28.2",
|
||||
"@esbuild/netbsd-arm64": "0.28.2",
|
||||
"@esbuild/netbsd-x64": "0.28.2",
|
||||
"@esbuild/openbsd-arm64": "0.28.2",
|
||||
"@esbuild/openbsd-x64": "0.28.2",
|
||||
"@esbuild/openharmony-arm64": "0.28.2",
|
||||
"@esbuild/sunos-x64": "0.28.2",
|
||||
"@esbuild/win32-arm64": "0.28.2",
|
||||
"@esbuild/win32-ia32": "0.28.2",
|
||||
"@esbuild/win32-x64": "0.28.2"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
@@ -6749,6 +7256,10 @@
|
||||
"@sinclair/typebox": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/pi-hermes-memory": {
|
||||
"resolved": "pi-hermes-memory",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/pi-lsp": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/pi-lsp/-/pi-lsp-0.1.7.tgz",
|
||||
@@ -7580,6 +8091,21 @@
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
|
||||
"integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^6.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-json-comments": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz",
|
||||
@@ -7755,6 +8281,25 @@
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/tsx": {
|
||||
"version": "4.23.15",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.15.tgz",
|
||||
"integrity": "sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "~0.28.0"
|
||||
},
|
||||
"bin": {
|
||||
"tsx": "dist/cli.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
@@ -8282,6 +8827,41 @@
|
||||
"@earendil-works/pi-coding-agent": "*"
|
||||
}
|
||||
},
|
||||
"pi-hermes-memory": {
|
||||
"version": "0.9.10",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"better-sqlite3": "^13.0.3",
|
||||
"strip-ansi": "7.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@earendil-works/pi-ai": "0.84.2",
|
||||
"@earendil-works/pi-coding-agent": "0.84.2",
|
||||
"@earendil-works/pi-tui": "0.84.2",
|
||||
"@types/better-sqlite3": "7.6.13",
|
||||
"tsx": "4.23.15",
|
||||
"typebox": "1.3.7",
|
||||
"typescript": "6.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@earendil-works/pi-ai": ">=0.80.6",
|
||||
"@earendil-works/pi-coding-agent": ">=0.80.6",
|
||||
"@earendil-works/pi-tui": "*"
|
||||
}
|
||||
},
|
||||
"pi-hermes-memory/node_modules/better-sqlite3": {
|
||||
"version": "13.0.3",
|
||||
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-13.0.3.tgz",
|
||||
"integrity": "sha512-RbOBxmLBG8uvFUc15X9+9SFemKcQ0WBuISBVkpuiaUB2qblC8UWlHEjdWVoZ8AdhSwmoEgsiXKfopX0CQxaACQ==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"node-addon-api": "^8.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22"
|
||||
}
|
||||
},
|
||||
"pi-minimal-footer": {
|
||||
"name": "@ogulcancelik/pi-minimal-footer",
|
||||
"version": "0.1.10-my-pi.1",
|
||||
|
||||
+3
-2
@@ -10,11 +10,11 @@
|
||||
],
|
||||
"files": [
|
||||
"extensions",
|
||||
"hippo-memory-pi",
|
||||
"pi-ask-user",
|
||||
"pi-compaction",
|
||||
"pi-chrome",
|
||||
"pi-extension-codex-fast-mode",
|
||||
"pi-hermes-memory",
|
||||
"pi-rtk-optimizer",
|
||||
"pi-minimal-footer",
|
||||
"pi-subagents",
|
||||
@@ -59,7 +59,7 @@
|
||||
"./node_modules/context-mode/build/adapters/pi/extension.js",
|
||||
"./extensions/lsp.ts",
|
||||
"./pi-extension-codex-fast-mode/index.ts",
|
||||
"./hippo-memory-pi/index.ts",
|
||||
"./pi-hermes-memory/src/index.ts",
|
||||
"./pi-compaction/index.ts",
|
||||
"./extensions/context-view.ts",
|
||||
"./pi-minimal-footer/index.ts",
|
||||
@@ -90,6 +90,7 @@
|
||||
"pi-chrome": "file:./pi-chrome",
|
||||
"pi-compaction": "file:./pi-compaction",
|
||||
"pi-hashline-edit": "0.8.3",
|
||||
"pi-hermes-memory": "file:./pi-hermes-memory",
|
||||
"pi-lsp": "0.1.7",
|
||||
"pi-mcp-adapter": "2.26.0",
|
||||
"pi-ssh": "file:./pi-ssh",
|
||||
|
||||
@@ -4,20 +4,40 @@
|
||||
- 导入发布 tag:`v0.9.10`
|
||||
- 导入 commit:`df624056a2e8c68bc89ba0a81e1c120856ec9665`(`chore: bump version to 0.9.10 (#277)`,2026-10-02)
|
||||
- 初始本地导入:2026-10-13
|
||||
- 接入组合包:2026-10-14
|
||||
- 许可证:MIT;上游 `LICENSE` 文件原样保留。
|
||||
|
||||
快照取自官方 Git 仓库中 `v0.9.10` tag 指向的 commit。导入时上游 `main` 已领先该 tag(`24fcd8ad26fc27badb8d7dbaf8034645de07b893`,2026-10-11);选择 tag 是为了让导入状态对应一个已发布、可复现的版本。不保留嵌套 `.git`、上游 `.github` 工作流、依赖目录、覆盖率与构建产物。
|
||||
|
||||
## 本地维护
|
||||
|
||||
本目录由 `my-pi` 组合包直接维护,不是 submodule。根包直接加载 `pi-hermes-memory/src/index.ts`。
|
||||
本目录由 `my-pi` 组合包直接维护,不是 submodule。根包通过 `file:./pi-hermes-memory` 依赖并直接加载 `pi-hermes-memory/src/index.ts`,不得同时安装或加载 npm 预编译入口。
|
||||
|
||||
导入时尚未接入组合包。启用该扩展前必须先完成以下事项,记录在此以免待决决定丢失:
|
||||
### 加载顺序
|
||||
|
||||
- 根 `package.json` 必须新增本地 `file:./pi-hermes-memory` 依赖,并在 `pi.extensions` 中注册扩展入口。
|
||||
- 注册顺序很重要:该扩展必须在 `pi-compaction` **之前**加载。`ExtensionRunner.emit()` 会在遇到第一个返回 `{ cancel: true }` 的 `session_before_compact` handler 时停止,而 `pi-compaction/index.ts` 在判定无需压缩时正是这样返回。注册在它之后的 handler 在这些轮次会被跳过,从而静默禁用该扩展的 `flushOnCompact`。
|
||||
- 该扩展依赖 `better-sqlite3@^13.0.3`。根 `allowScripts` 当前只放行 `better-sqlite3@12.11.1`(Context Mode 使用);安装前必须对齐原生构建批准。
|
||||
- `strip-ansi@7.2.0` 是第二个运行时依赖,必须能与现有依赖树干净解析。
|
||||
- 它注册 `memory_add` / `memory_replace` / `memory_remove` / `memory_search` / `session_search` / `skill_manage`,以及 `/memory-*` 命令。这些名称当前与组合包中的任何内容都不冲突。
|
||||
**该扩展必须注册在 `pi-compaction` 之前。** `ExtensionRunner.emit()` 在遇到第一个返回 `{ cancel: true }` 的 `session_before_compact` handler 时立即返回,而 `pi-compaction/index.ts` 在判定无需压缩时正是这样返回。注册在它之后的 handler 在这些轮次会被跳过,从而静默禁用该扩展的 `flushOnCompact`。
|
||||
|
||||
### 依赖
|
||||
|
||||
- `better-sqlite3@^13.0.3`:13.x 运行时实际 `dlopen` 的是包内 N-API 预编译二进制 `prebuilds/<platform>.node`,**不依赖 node-gyp 编译产物**。npm 会因该包存在 `binding.gyp` 而推断出隐式 `node-gyp rebuild`,但该步骤既无必要也不得批准。npm 会把它嵌套安装到本目录的 `node_modules/`,与 Context Mode 使用的根 `better-sqlite3@12.11.1` 并存;因此根 `allowScripts` 无需新增条目,也不得删除既有的 `better-sqlite3@12.11.1` 放行。
|
||||
- `strip-ansi@7.2.0`:解析到根 `node_modules`。
|
||||
|
||||
### 本地改动
|
||||
|
||||
- `devDependencies` 中的 `@earendil-works/pi-ai`、`@earendil-works/pi-coding-agent`、`@earendil-works/pi-tui`、`typebox`、`tsx`、`typescript`、`@types/better-sqlite3` 已从上游的 `^0.80.2` 等范围改为与根包一致的精确版本。上游的宽松范围与根包的 `0.84.2` 不兼容,会让 npm 在本目录嵌套安装第二套 Pi runtime(实测 229 MB),违反组合包"不得再安装一套 Pi runtime"的纪律;`pi-notify/package.json` 是同样的先例。运行时依赖(`better-sqlite3`、`strip-ansi`)与 `peerDependencies` 保持上游原样。
|
||||
|
||||
### 工具与常驻
|
||||
|
||||
该扩展注册 `memory_add`、`memory_replace`、`memory_remove`、`memory_search`、`session_search`、`skill_manage`,以及 `/memory-*` 命令。六个工具全部通过 `pi-tool-search` 的 `alwaysEnabled` 常驻,并在 `pi-permission-system` 策略中默认 `allow`。
|
||||
|
||||
注意 `skill_manage` 由本扩展提供,不是 Pi 宿主能力:Pi 只负责发现并加载 skill 目录(`~/.pi/agent/skills/` 加扩展通过 `resources_discover` 注册的目录),本身没有创建 skill 的工具。因此本扩展同时承担 skill 的**写入**与**目录注册**两件事;不加载它,`projects-memory/<项目>/skills/` 下的既有 skill 也不会被 Pi 发现。
|
||||
|
||||
### 用户数据
|
||||
|
||||
- 全局记忆与 skill:`~/.pi/agent/pi-hermes-memory/`
|
||||
- 项目记忆与 skill:`~/.pi/agent/projects-memory/<项目>/`
|
||||
- 配置:`~/.pi/agent/hermes-memory-config.json`(可选,缺省时使用内置默认值)
|
||||
|
||||
这些路径是用户数据,不属于组合包,也不应提交或自动清理。
|
||||
|
||||
同步上游时,请从明确 tag/commit 移植改动,并与本地改动对照审查,不得整体覆盖本目录。
|
||||
|
||||
Generated
+114
-164
@@ -13,13 +13,13 @@
|
||||
"strip-ansi": "7.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@earendil-works/pi-ai": "^0.80.2",
|
||||
"@earendil-works/pi-coding-agent": "^0.80.2",
|
||||
"@earendil-works/pi-tui": "^0.80.2",
|
||||
"@types/better-sqlite3": "^7.6.13",
|
||||
"tsx": "^4.21.0",
|
||||
"typebox": "^1.1.33",
|
||||
"typescript": "^6.0.3"
|
||||
"@earendil-works/pi-ai": "0.84.2",
|
||||
"@earendil-works/pi-coding-agent": "0.84.2",
|
||||
"@earendil-works/pi-tui": "0.84.2",
|
||||
"@types/better-sqlite3": "7.6.13",
|
||||
"tsx": "4.23.15",
|
||||
"typebox": "1.3.7",
|
||||
"typescript": "6.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@earendil-works/pi-ai": ">=0.80.6",
|
||||
@@ -514,23 +514,23 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@earendil-works/pi-ai": {
|
||||
"version": "0.80.2",
|
||||
"resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.80.2.tgz",
|
||||
"integrity": "sha512-5GNKfdrRJ4uZ5Zd9iudoXggi/BbUcKnD/xfRHtdR+7q4vWqPvfx8auFuaT+ewGBVI8K4wj87eigFQ/iCSuy9RQ==",
|
||||
"version": "0.84.2",
|
||||
"resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.84.2.tgz",
|
||||
"integrity": "sha512-6MzsrYIYNVlE7SfpbL2yYb67Qo58p/7Q+xWG1RZvoX1P80aRCHSod2/13aFpxkow1lPO2LEh3c495J0Gwmyjig==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "0.91.1",
|
||||
"@aws-sdk/client-bedrock-runtime": "3.1048.0",
|
||||
"@earendil-works/pi-telemetry": "^0.84.2",
|
||||
"@google/genai": "1.52.0",
|
||||
"@mistralai/mistralai": "2.2.6",
|
||||
"@opentelemetry/api": "1.9.0",
|
||||
"@smithy/node-http-handler": "4.7.3",
|
||||
"http-proxy-agent": "7.0.2",
|
||||
"https-proxy-agent": "7.0.6",
|
||||
"openai": "6.26.0",
|
||||
"openai": "6.40.0",
|
||||
"partial-json": "0.1.7",
|
||||
"typebox": "1.1.38"
|
||||
"typebox": "1.3.7"
|
||||
},
|
||||
"bin": {
|
||||
"pi-ai": "dist/cli.js"
|
||||
@@ -561,21 +561,24 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@earendil-works/pi-coding-agent": {
|
||||
"version": "0.80.2",
|
||||
"resolved": "https://registry.npmjs.org/@earendil-works/pi-coding-agent/-/pi-coding-agent-0.80.2.tgz",
|
||||
"integrity": "sha512-m9v7OUit0s9LklWfh61ca/XY5INjUzjtYtNZwy3cNvyjOLk3IpBgghP8aAp0iH35rLaiRwuuWiJ8t88ODMWY+A==",
|
||||
"version": "0.84.2",
|
||||
"resolved": "https://registry.npmjs.org/@earendil-works/pi-coding-agent/-/pi-coding-agent-0.84.2.tgz",
|
||||
"integrity": "sha512-l4E+B7hgXKWddRo8bC/eSue2aWZjEgJ9xIpf5p0Og+lq8a2TArCwJ0HCoCPCgaBP/tN4zbYH/wOwvx9pJpeLCA==",
|
||||
"dev": true,
|
||||
"hasShrinkwrap": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@earendil-works/pi-agent-core": "^0.80.2",
|
||||
"@earendil-works/pi-ai": "^0.80.2",
|
||||
"@earendil-works/pi-tui": "^0.80.2",
|
||||
"@earendil-works/pi-agent-core": "^0.84.2",
|
||||
"@earendil-works/pi-ai": "^0.84.2",
|
||||
"@earendil-works/pi-client": "^0.84.2",
|
||||
"@earendil-works/pi-protocol": "^0.84.2",
|
||||
"@earendil-works/pi-tui": "^0.84.2",
|
||||
"@silvia-odwyer/photon-node": "0.3.4",
|
||||
"chalk": "5.6.2",
|
||||
"cross-spawn": "7.0.6",
|
||||
"diff": "8.0.4",
|
||||
"glob": "13.0.6",
|
||||
"grok-mermaid": "0.2.2",
|
||||
"highlight.js": "10.7.3",
|
||||
"hosted-git-info": "9.0.3",
|
||||
"ignore": "7.0.5",
|
||||
@@ -583,8 +586,8 @@
|
||||
"minimatch": "10.2.5",
|
||||
"proper-lockfile": "4.1.2",
|
||||
"semver": "7.8.0",
|
||||
"typebox": "1.1.38",
|
||||
"undici": "8.5.0",
|
||||
"typebox": "1.3.7",
|
||||
"undici": "8.9.0",
|
||||
"yaml": "2.9.0"
|
||||
},
|
||||
"bin": {
|
||||
@@ -1060,14 +1063,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-agent-core": {
|
||||
"version": "0.80.2",
|
||||
"resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-0.80.2.tgz",
|
||||
"version": "0.84.2",
|
||||
"resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-0.84.2.tgz",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@earendil-works/pi-ai": "^0.80.2",
|
||||
"@earendil-works/pi-ai": "^0.84.2",
|
||||
"@earendil-works/pi-telemetry": "^0.84.2",
|
||||
"diff": "8.0.4",
|
||||
"ignore": "7.0.5",
|
||||
"typebox": "1.1.38",
|
||||
"typebox": "1.3.7",
|
||||
"yaml": "2.9.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1075,22 +1080,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-ai": {
|
||||
"version": "0.80.2",
|
||||
"resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.80.2.tgz",
|
||||
"version": "0.84.2",
|
||||
"resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.84.2.tgz",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "0.91.1",
|
||||
"@aws-sdk/client-bedrock-runtime": "3.1048.0",
|
||||
"@earendil-works/pi-telemetry": "^0.84.2",
|
||||
"@google/genai": "1.52.0",
|
||||
"@mistralai/mistralai": "2.2.6",
|
||||
"@opentelemetry/api": "1.9.0",
|
||||
"@smithy/node-http-handler": "4.7.3",
|
||||
"http-proxy-agent": "7.0.2",
|
||||
"https-proxy-agent": "7.0.6",
|
||||
"openai": "6.26.0",
|
||||
"openai": "6.40.0",
|
||||
"partial-json": "0.1.7",
|
||||
"typebox": "1.1.38"
|
||||
"typebox": "1.3.7"
|
||||
},
|
||||
"bin": {
|
||||
"pi-ai": "dist/cli.js"
|
||||
@@ -1099,9 +1104,42 @@
|
||||
"node": ">=22.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-client": {
|
||||
"version": "0.84.2",
|
||||
"resolved": "https://registry.npmjs.org/@earendil-works/pi-client/-/pi-client-0.84.2.tgz",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@earendil-works/pi-protocol": "^0.84.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-protocol": {
|
||||
"version": "0.84.2",
|
||||
"resolved": "https://registry.npmjs.org/@earendil-works/pi-protocol/-/pi-protocol-0.84.2.tgz",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"typebox": "1.3.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-telemetry": {
|
||||
"version": "0.84.2",
|
||||
"resolved": "https://registry.npmjs.org/@earendil-works/pi-telemetry/-/pi-telemetry-0.84.2.tgz",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=22.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-tui": {
|
||||
"version": "0.80.2",
|
||||
"resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.80.2.tgz",
|
||||
"version": "0.84.2",
|
||||
"resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.84.2.tgz",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1327,27 +1365,6 @@
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@earendil-works/pi-coding-agent/node_modules/@mistralai/mistralai": {
|
||||
"version": "2.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-2.2.6.tgz",
|
||||
"integrity": "sha512-W8pX7zHxjJvMIpw8JMxeJEleapXX0Q9NPszdNzqkM3MIEoIGPObdodujj+WHteXEvGfaP/AMwlNyRfEzSY6dQQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/semantic-conventions": "^1.40.0",
|
||||
"ws": "^8.18.0",
|
||||
"zod": "^3.25.0 || ^4.0.0",
|
||||
"zod-to-json-schema": "^3.25.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.9.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@opentelemetry/api": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@earendil-works/pi-coding-agent/node_modules/@nodable/entities": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz",
|
||||
@@ -1371,16 +1388,6 @@
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@earendil-works/pi-coding-agent/node_modules/@opentelemetry/semantic-conventions": {
|
||||
"version": "1.41.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.41.1.tgz",
|
||||
"integrity": "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/aspromise": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
||||
@@ -1652,16 +1659,16 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@earendil-works/pi-coding-agent/node_modules/brace-expansion": {
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
|
||||
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
|
||||
"version": "5.0.9",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
|
||||
"integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@earendil-works/pi-coding-agent/node_modules/buffer-equal-constant-time": {
|
||||
@@ -1926,6 +1933,16 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@earendil-works/pi-coding-agent/node_modules/grok-mermaid": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/grok-mermaid/-/grok-mermaid-0.2.2.tgz",
|
||||
"integrity": "sha512-XcJEP5dDC8liHBh52mlLjU18fNvu1ckFsu0QpIG3+APZ270fsj9wxpiA6cOURmbUEuoMVgjbC2+UYgTdCqqgzA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@earendil-works/pi-coding-agent/node_modules/highlight.js": {
|
||||
"version": "10.7.3",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
|
||||
@@ -2155,14 +2172,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@earendil-works/pi-coding-agent/node_modules/openai": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/openai/-/openai-6.26.0.tgz",
|
||||
"integrity": "sha512-zd23dbWTjiJ6sSAX6s0HrCZi41JwTA1bQVs0wLQPZ2/5o2gxOJA5wh7yOAUgwYybfhDXyhwlpeQf7Mlgx8EOCA==",
|
||||
"version": "6.40.0",
|
||||
"resolved": "https://registry.npmjs.org/openai/-/openai-6.40.0.tgz",
|
||||
"integrity": "sha512-MWtTjd/gQt4jpbji61NTgFWJLoY/PdRJ6wG9/ZDRMYNMlBKrCrSlkLI+KgHP1vR1qT6LKSAyAqIxno6lcK9JiA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"openai": "bin/cli"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ws": "^8.18.0",
|
||||
"zod": "^3.25 || ^4.0"
|
||||
@@ -2270,9 +2284,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@earendil-works/pi-coding-agent/node_modules/protobufjs": {
|
||||
"version": "7.6.4",
|
||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.4.tgz",
|
||||
"integrity": "sha512-RJJPTTpvFfHcWLkIa2JFWK4XvtSzS0yEWDmunqHXli1h3JlkbcQZXDZdcWxv+JK3Xsl5/UFDPZ0iGm7DAengYw==",
|
||||
"version": "7.6.5",
|
||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.5.tgz",
|
||||
"integrity": "sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "BSD-3-Clause",
|
||||
@@ -2395,16 +2409,16 @@
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/@earendil-works/pi-coding-agent/node_modules/typebox": {
|
||||
"version": "1.1.38",
|
||||
"resolved": "https://registry.npmjs.org/typebox/-/typebox-1.1.38.tgz",
|
||||
"integrity": "sha512-pZ0aQPmMmXoUvSbeuWf/Hzsc+avNw/Zd6VeE8CFgkVGWyuHPJvqeJJDeJqLve+K70LvjYIoleGcoJHPT17cWoA==",
|
||||
"version": "1.3.7",
|
||||
"resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.7.tgz",
|
||||
"integrity": "sha512-meKuifc33Pccx0O6PdIzYMq3Og8zvP4TIi/a+Bw3AEMZMxOD0+RHGQvpglEe6Zdy3wZ8nqn/j95h8LUZLk/6Hg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@earendil-works/pi-coding-agent/node_modules/undici": {
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-8.5.0.tgz",
|
||||
"integrity": "sha512-xamtWoB1EshgjpmlXd7GGm2VfdDtw1+rD8uhry8pSNW3If6S8E0m2T2+orSKeZXEn/aPJMviCpDBA65WJt8zhg==",
|
||||
"version": "8.9.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-8.9.0.tgz",
|
||||
"integrity": "sha512-aWZpUj7XoGonMClx4gdDRfgBjqeA+F473aDmROQQbM9n6PRfK/u1q/a0X4wMTgcHfT8H6fpbt98PFuDUwFg2YA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -2498,30 +2512,20 @@
|
||||
"url": "https://github.com/sponsors/eemeli"
|
||||
}
|
||||
},
|
||||
"node_modules/@earendil-works/pi-coding-agent/node_modules/zod": {
|
||||
"version": "3.25.76",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||
"node_modules/@earendil-works/pi-telemetry": {
|
||||
"version": "0.84.4",
|
||||
"resolved": "https://registry.npmjs.org/@earendil-works/pi-telemetry/-/pi-telemetry-0.84.4.tgz",
|
||||
"integrity": "sha512-8e2CuxM+ht+hedQXTZmi5JVl6/xDK9RpSDL2+MbITevKYQhMZ/z6lJOTFgox3HQyGxO8mOZEtYGVeQNaD4OzqA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
},
|
||||
"node_modules/@earendil-works/pi-coding-agent/node_modules/zod-to-json-schema": {
|
||||
"version": "3.25.2",
|
||||
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz",
|
||||
"integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"zod": "^3.25.28 || ^4"
|
||||
"engines": {
|
||||
"node": ">=22.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@earendil-works/pi-tui": {
|
||||
"version": "0.80.2",
|
||||
"resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.80.2.tgz",
|
||||
"integrity": "sha512-OvOAMIbXiC9OSse17YMiXIsI9AS5XM/ZV8N/k+UzdlRpPILDQYmLElevgGW92kkXR8qHBClIdzhCjuzlBGvphA==",
|
||||
"version": "0.84.2",
|
||||
"resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.84.2.tgz",
|
||||
"integrity": "sha512-ds2TLihOnM5sLJB3VpXV6y0uR5efVuHf4MN7yDpsty6hA2DUO/EDVzjp/0od0G2JslzVLMjT8T8zavtxVb+qbg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2999,27 +3003,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mistralai/mistralai": {
|
||||
"version": "2.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-2.2.6.tgz",
|
||||
"integrity": "sha512-W8pX7zHxjJvMIpw8JMxeJEleapXX0Q9NPszdNzqkM3MIEoIGPObdodujj+WHteXEvGfaP/AMwlNyRfEzSY6dQQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/semantic-conventions": "^1.40.0",
|
||||
"ws": "^8.18.0",
|
||||
"zod": "^3.25.0 || ^4.0.0",
|
||||
"zod-to-json-schema": "^3.25.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.9.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@opentelemetry/api": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/api": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
|
||||
@@ -3030,16 +3013,6 @@
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/semantic-conventions": {
|
||||
"version": "1.41.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.41.1.tgz",
|
||||
"integrity": "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@protobufjs/aspromise": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
||||
@@ -3704,14 +3677,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/openai": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/openai/-/openai-6.26.0.tgz",
|
||||
"integrity": "sha512-zd23dbWTjiJ6sSAX6s0HrCZi41JwTA1bQVs0wLQPZ2/5o2gxOJA5wh7yOAUgwYybfhDXyhwlpeQf7Mlgx8EOCA==",
|
||||
"version": "6.40.0",
|
||||
"resolved": "https://registry.npmjs.org/openai/-/openai-6.40.0.tgz",
|
||||
"integrity": "sha512-MWtTjd/gQt4jpbji61NTgFWJLoY/PdRJ6wG9/ZDRMYNMlBKrCrSlkLI+KgHP1vR1qT6LKSAyAqIxno6lcK9JiA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"openai": "bin/cli"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ws": "^8.18.0",
|
||||
"zod": "^3.25 || ^4.0"
|
||||
@@ -3831,9 +3801,9 @@
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/tsx": {
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.4.tgz",
|
||||
"integrity": "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==",
|
||||
"version": "4.23.15",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.15.tgz",
|
||||
"integrity": "sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -3850,9 +3820,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typebox": {
|
||||
"version": "1.1.38",
|
||||
"resolved": "https://registry.npmjs.org/typebox/-/typebox-1.1.38.tgz",
|
||||
"integrity": "sha512-pZ0aQPmMmXoUvSbeuWf/Hzsc+avNw/Zd6VeE8CFgkVGWyuHPJvqeJJDeJqLve+K70LvjYIoleGcoJHPT17cWoA==",
|
||||
"version": "1.3.7",
|
||||
"resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.7.tgz",
|
||||
"integrity": "sha512-meKuifc33Pccx0O6PdIzYMq3Og8zvP4TIi/a+Bw3AEMZMxOD0+RHGQvpglEe6Zdy3wZ8nqn/j95h8LUZLk/6Hg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -3908,26 +3878,6 @@
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
|
||||
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
},
|
||||
"node_modules/zod-to-json-schema": {
|
||||
"version": "3.25.2",
|
||||
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz",
|
||||
"integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"zod": "^3.25.28 || ^4"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,13 +53,13 @@
|
||||
"@earendil-works/pi-tui": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@earendil-works/pi-ai": "^0.80.2",
|
||||
"@earendil-works/pi-coding-agent": "^0.80.2",
|
||||
"@earendil-works/pi-tui": "^0.80.2",
|
||||
"@types/better-sqlite3": "^7.6.13",
|
||||
"tsx": "^4.21.0",
|
||||
"typebox": "^1.1.33",
|
||||
"typescript": "^6.0.3"
|
||||
"@earendil-works/pi-ai": "0.84.2",
|
||||
"@earendil-works/pi-coding-agent": "0.84.2",
|
||||
"@earendil-works/pi-tui": "0.84.2",
|
||||
"@types/better-sqlite3": "7.6.13",
|
||||
"tsx": "4.23.15",
|
||||
"typebox": "1.3.7",
|
||||
"typescript": "6.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"better-sqlite3": "^13.0.3",
|
||||
|
||||
@@ -11,12 +11,14 @@
|
||||
- Added limits for active groups, generated group size, and total dynamic tools, plus current-host catalog/config/LRU tests.
|
||||
- Added checked-in authoritative groups for every tool exposed by the standard my-pi bundle, eliminating first-use model generation and user caching unless unrecognized third-party tools are present.
|
||||
- Replaced the Hermes and third-party Hippo assignments with the five tools exposed by the official Hippo Pi extension, grouped into recall and management workflows.
|
||||
- Replaced the retired Hippo `hippo_*` assignments with the vendored Hermes memory extension's six tools, grouped into `memory-recall` (`memory_search`, `session_search`, `context_tree_query`) and `memory-management` (`memory_add`, `memory_replace`, `memory_remove`, `skill_manage`).
|
||||
- Added the enabled bundle's `Agent`, `get_subagent_result`, and `steer_subagent` tools to a checked-in `subagents` orchestration group.
|
||||
- Added authoritative `ssh-connection`, `ssh-remote-files`, `ssh-remote-search`, and `ssh-remote-shell` groups for the maintained `pi-ssh` tools, keeping reviewed connection, bounded discovery, structured file operations, and reviewed shell execution separate; the connection workflow now requires sequential `ssh_connect` to succeed before dependent tools.
|
||||
- Added sequential `ssh_cd` to the authoritative remote-shell workflow and direct the model to wait for a successful workspace change before dependent remote calls, avoiding both the unknown-tools fallback and cwd races.
|
||||
- Added the authoritative `user-interaction` group for the maintained `ask_user_question` TUI tool.
|
||||
- Added authoritative `chrome-navigation`, `chrome-interaction`, and `chrome-debugging` groups for all 21 tools registered by the pinned `pi-chrome` bridge after explicit session authorization.
|
||||
- Raised the bundle defaults to five active groups and 28 dynamic tools, and pinned the high-frequency Context Mode execution trio alongside CodeGraph exploration and LSP diagnostics.
|
||||
- Pinned all six Hermes memory tools in the bundle defaults and raised `bundleDefaultsVersion` to 3. The injected memory policy expects these schemas every turn, and pinning also keeps the request prefix cache-stable instead of paying a `tool_search` round trip plus a cache invalidation. `scripts/migrate-tool-search-settings.mjs` migrates the complete version-2 snapshot while leaving customized configurations untouched.
|
||||
- Added atomic multi-group activation through `tool_search.groups`, bounded to five exact group ids and rejected without state changes when the complete workflow cannot fit.
|
||||
- Weighted exact tool names, group metadata, and common SSH/Chrome/Context aliases during query routing; weak or ambiguous matches now return candidates instead of activating arbitrarily.
|
||||
- Added `/tool-search-status` for catalog source, capacity, active-group LRU order, and latest eviction diagnostics.
|
||||
|
||||
@@ -39,8 +39,8 @@ Run `/tool-search-status` to inspect the current catalog source, pinned count, d
|
||||
| `context-knowledge` | Context indexing, searching, and web-doc ingestion |
|
||||
| `context-observability` | Context stats, doctor, and Insight |
|
||||
| `context-administration` | Context upgrade and destructive purge |
|
||||
| `memory-recall` | Official Hippo recall/context/status and condensed-output recovery |
|
||||
| `memory-management` | Official Hippo remember/outcome tools |
|
||||
| `memory-recall` | Hermes memory/session search and condensed-output recovery |
|
||||
| `memory-management` | Hermes memory writes and procedural skill management |
|
||||
| `subagents` | Launch agents, collect background results, and steer running agents |
|
||||
| `user-interaction` | `ask_user_question` model-initiated TUI/RPC questions |
|
||||
| `chrome-navigation` | Chrome bridge setup, tabs, navigation, snapshots/find/inspect, waits, and screenshots |
|
||||
@@ -48,7 +48,7 @@ Run `/tool-search-status` to inspect the current catalog source, pinned count, d
|
||||
| `chrome-debugging` | Chrome page evaluation, console/network diagnostics, response retrieval, and file upload |
|
||||
| `mcp-management` | Shared MCP adapter management |
|
||||
|
||||
Pinned tools are removed from these dynamic groups at runtime. The defaults pin `codegraph_explore`, `lsp_diagnostics`, and the three high-frequency Context Mode execution tools, so none of them consume dynamic group or tool capacity.
|
||||
Pinned tools are removed from these dynamic groups at runtime. The defaults pin `codegraph_explore`, `lsp_diagnostics`, the three high-frequency Context Mode execution tools, and all six Hermes memory tools, so none of them consume dynamic group or tool capacity.
|
||||
|
||||
## Loading and eviction
|
||||
|
||||
@@ -84,21 +84,27 @@ Pi reads this configuration from `<agent-dir>/settings.json` (normally `~/.pi/ag
|
||||
"lsp_diagnostics",
|
||||
"ctx_execute",
|
||||
"ctx_execute_file",
|
||||
"ctx_batch_execute"
|
||||
"ctx_batch_execute",
|
||||
"memory_search",
|
||||
"session_search",
|
||||
"memory_add",
|
||||
"memory_replace",
|
||||
"memory_remove",
|
||||
"skill_manage"
|
||||
],
|
||||
"showToolSearchFooterStatus": false,
|
||||
"maxActiveGroups": 5,
|
||||
"maxToolsPerGroup": 8,
|
||||
"maxDynamicTools": 28,
|
||||
"groupOverrides": {},
|
||||
"bundleDefaultsVersion": 2
|
||||
"bundleDefaultsVersion": 3
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Key | Bundle default | Description |
|
||||
|---|---|---|
|
||||
| `alwaysEnabled` | CodeGraph explore, LSP diagnostics, Context execution trio | Exact names pinned alongside Pi core tools |
|
||||
| `alwaysEnabled` | CodeGraph explore, LSP diagnostics, Context execution trio, all six Hermes memory tools | Exact names pinned alongside Pi core tools |
|
||||
| `showToolSearchFooterStatus` | `false` | Show active tool/group counts |
|
||||
| `maxActiveGroups` | `5` | Maximum simultaneously active dynamic groups |
|
||||
| `maxToolsPerGroup` | `8` | Maximum size accepted from model-generated groups |
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
`pi-tool-search` keeps full low-frequency schemas hidden behind a compact, validated group catalog.
|
||||
|
||||
1. `session_start` activates `tool_search`, the six cross-platform Pi core tools, and configured `alwaysEnabled` names. The my-pi defaults also pin CodeGraph exploration, LSP diagnostics, and the three high-frequency Context Mode execution tools. On Windows, `powershell` is pinned beside `bash`; on non-Windows hosts it is excluded. Pinned tools do not consume the dynamic-group or dynamic-tool budget.
|
||||
1. `session_start` activates `tool_search`, the six cross-platform Pi core tools, and configured `alwaysEnabled` names. The my-pi defaults also pin CodeGraph exploration, LSP diagnostics, the three high-frequency Context Mode execution tools, and the six Hermes memory tools. On Windows, `powershell` is pinned beside `bash`; on non-Windows hosts it is excluded. Pinned tools do not consume the dynamic-group or dynamic-tool budget.
|
||||
2. Hidden definitions are hashed with grouping constraints and matched against `extensions/bundle-groups.ts`. Available standard tools receive checked-in groups; unavailable optional tools are omitted.
|
||||
3. If every hidden tool is recognized, the bundle catalog is used directly. No model call or per-user cache is required.
|
||||
4. Additional tools receive immediate deterministic prefix/source groups. A matching model-enriched cache is reused only if it preserves all checked-in bundle assignments.
|
||||
@@ -13,7 +13,7 @@
|
||||
9. Natural-language lookup gives exact tool names and workflow aliases more weight than descriptive overlap. Weak or closely tied matches return candidates without activation.
|
||||
10. `turn_start` notices tool or grouping-configuration changes by hash, resets stale dynamic groups, and reconstructs the checked-in/hybrid catalog.
|
||||
|
||||
The standard bundle assigns the official Hippo Pi extension's five `hippo_*` tools to recall and management groups, and assigns `Agent`, `get_subagent_result`, and `steer_subagent` to the `subagents` orchestration group. `context_tree_query` remains in `memory-recall` because it recovers condensed tool output; retired Hermes names and third-party `tff-memory_*` names are not seeded.
|
||||
The standard bundle assigns the Hermes memory extension's six tools to recall and management groups: `memory_search` and `session_search` join `context_tree_query` in `memory-recall`, while `memory_add`, `memory_replace`, `memory_remove`, and `skill_manage` form `memory-management`. All six are pinned by default, because the injected `<memory-policy>` tells the model to consult and write memory during normal work, and a schema the model cannot see would silently disable that loop. Third-party `tff-memory_*` names and the retired Hippo `hippo_*` names are not seeded. It also assigns `Agent`, `get_subagent_result`, and `steer_subagent` to the `subagents` orchestration group.
|
||||
The maintained `pi-ssh` tools are also split by workflow: sequential reviewed agent-controlled connection lives in `ssh-connection`, structured `ssh_read`/`ssh_write`/`ssh_edit` file operations live in `ssh-remote-files`, bounded `ssh_find`/`ssh_grep` discovery lives in `ssh-remote-search`, and sequential reviewed workspace changes plus reviewed command execution live in `ssh-remote-shell`. The workflows tell the model to call `ssh_connect` or `ssh_cd` separately and wait for success before issuing remote calls that depend on the new connection or cwd.
|
||||
The maintained `ask_user_question` tool has its own `user-interaction` group so the model can load the complete interactive schema only when a missing decision or preference must come from the user.
|
||||
The pinned `pi-chrome` bridge registers its 21 `chrome_*` tools only after explicit per-session authorization. Once registered, catalog refresh assigns them to `chrome-navigation`, `chrome-interaction`, and `chrome-debugging`; the 28-tool default allows all three groups to coexist. Revocation remains enforced inside every upstream tool execution even if a schema is later reactivated by Tool Search.
|
||||
|
||||
@@ -141,21 +141,21 @@ export const BUNDLE_GROUP_DEFINITIONS: BundleGroupDefinition[] = [
|
||||
},
|
||||
{
|
||||
id: "memory-recall",
|
||||
title: "Official Hippo memory recall",
|
||||
summary: "Recall project memories, inspect Hippo status/context, or recover pruned tool-call output.",
|
||||
useWhen: ["Past project lessons, errors, decisions, or condensed outputs may answer the question"],
|
||||
avoidWhen: ["The required evidence is already in the current visible context"],
|
||||
keywords: ["memory", "recall", "context", "status", "recover", "记忆", "召回", "恢复输出"],
|
||||
tools: ["hippo_recall", "hippo_context", "hippo_status", "context_tree_query"],
|
||||
title: "Persistent memory recall",
|
||||
summary: "Search durable user, global, project, and failure memories, search indexed past conversations, or recover pruned tool-call output.",
|
||||
useWhen: ["The task may depend on durable context from previous sessions: preferences, project conventions, prior decisions, known failures, corrections, or condensed outputs"],
|
||||
avoidWhen: ["The required evidence is already in the current visible context, or the question is a generic one-off that durable memory would not help"],
|
||||
keywords: ["memory", "recall", "search", "session", "history", "recover", "记忆", "召回", "检索", "历史会话", "恢复输出"],
|
||||
tools: ["memory_search", "session_search", "context_tree_query"],
|
||||
},
|
||||
{
|
||||
id: "memory-management",
|
||||
title: "Official Hippo memory management",
|
||||
summary: "Store durable project lessons or report whether recalled Hippo memories were useful.",
|
||||
useWhen: ["A non-obvious project lesson, error, or decision should persist, or recalled memories need feedback"],
|
||||
avoidWhen: ["The information is temporary task progress or derivable from the repository"],
|
||||
keywords: ["remember", "outcome", "lesson", "error", "记住", "反馈", "项目经验"],
|
||||
tools: ["hippo_remember", "hippo_outcome"],
|
||||
title: "Persistent memory management",
|
||||
summary: "Save, update, or remove durable memories, and create or maintain reusable procedural skills.",
|
||||
useWhen: ["A durable fact, preference, convention, correction, or non-obvious reusable procedure should persist across sessions"],
|
||||
avoidWhen: ["The information is temporary task progress, derivable from the repository, or a one-off detail that would add noise"],
|
||||
keywords: ["remember", "save", "replace", "remove", "skill", "procedure", "记住", "保存", "修改记忆", "技能", "流程"],
|
||||
tools: ["memory_add", "memory_replace", "memory_remove", "skill_manage"],
|
||||
},
|
||||
{
|
||||
id: "subagents",
|
||||
|
||||
@@ -20,6 +20,12 @@ export const BUNDLE_TOOL_SEARCH_DEFAULTS = {
|
||||
"ctx_execute",
|
||||
"ctx_execute_file",
|
||||
"ctx_batch_execute",
|
||||
"memory_search",
|
||||
"session_search",
|
||||
"memory_add",
|
||||
"memory_replace",
|
||||
"memory_remove",
|
||||
"skill_manage",
|
||||
],
|
||||
showToolSearchFooterStatus: false,
|
||||
maxActiveGroups: 5,
|
||||
@@ -28,7 +34,7 @@ export const BUNDLE_TOOL_SEARCH_DEFAULTS = {
|
||||
groupOverrides: {},
|
||||
} as const;
|
||||
|
||||
export const BUNDLE_TOOL_SEARCH_DEFAULTS_VERSION = 2;
|
||||
export const BUNDLE_TOOL_SEARCH_DEFAULTS_VERSION = 3;
|
||||
|
||||
export interface ToolSearchConfig {
|
||||
alwaysEnabled: string[];
|
||||
|
||||
@@ -27,8 +27,10 @@ test("bundle seed names are unique and cover the declared my-pi tools", () => {
|
||||
assert.equal(result.catalog.tools.length, names.length);
|
||||
assert.equal(result.catalog.tools.find((card) => card.name === "ctx_purge")?.primaryGroup, "context-administration");
|
||||
assert.equal(result.catalog.tools.find((card) => card.name === "tavily_web_fetch")?.primaryGroup, "web-tavily");
|
||||
assert.equal(result.catalog.tools.find((card) => card.name === "hippo_recall")?.primaryGroup, "memory-recall");
|
||||
assert.equal(result.catalog.tools.find((card) => card.name === "hippo_remember")?.primaryGroup, "memory-management");
|
||||
assert.equal(result.catalog.tools.find((card) => card.name === "memory_search")?.primaryGroup, "memory-recall");
|
||||
assert.equal(result.catalog.tools.find((card) => card.name === "session_search")?.primaryGroup, "memory-recall");
|
||||
assert.equal(result.catalog.tools.find((card) => card.name === "memory_add")?.primaryGroup, "memory-management");
|
||||
assert.equal(result.catalog.tools.find((card) => card.name === "skill_manage")?.primaryGroup, "memory-management");
|
||||
assert.equal(result.catalog.tools.find((card) => card.name === "Agent")?.primaryGroup, "subagents");
|
||||
assert.equal(result.catalog.tools.find((card) => card.name === "get_subagent_result")?.primaryGroup, "subagents");
|
||||
assert.equal(result.catalog.tools.find((card) => card.name === "steer_subagent")?.primaryGroup, "subagents");
|
||||
|
||||
@@ -24,6 +24,12 @@ test("writes bundle defaults when toolSearch is absent", async () => {
|
||||
"ctx_execute",
|
||||
"ctx_execute_file",
|
||||
"ctx_batch_execute",
|
||||
"memory_search",
|
||||
"session_search",
|
||||
"memory_add",
|
||||
"memory_replace",
|
||||
"memory_remove",
|
||||
"skill_manage",
|
||||
]);
|
||||
assert.deepEqual(JSON.parse(await readFile(join(agentDir, "settings.json"), "utf8")), {
|
||||
toolSearch: {
|
||||
@@ -33,7 +39,7 @@ test("writes bundle defaults when toolSearch is absent", async () => {
|
||||
maxToolsPerGroup: 8,
|
||||
maxDynamicTools: 28,
|
||||
groupOverrides: {},
|
||||
bundleDefaultsVersion: 2,
|
||||
bundleDefaultsVersion: 3,
|
||||
},
|
||||
});
|
||||
assert.equal((await stat(join(agentDir, "settings.json"))).mode & 0o777, 0o600);
|
||||
|
||||
@@ -14,7 +14,7 @@ export const LEGACY_TOOL_SEARCH_DEFAULTS = {
|
||||
groupOverrides: {},
|
||||
};
|
||||
|
||||
const CURRENT_TOOL_SEARCH_RUNTIME_DEFAULTS = {
|
||||
const PREVIOUS_TOOL_SEARCH_RUNTIME_DEFAULTS = {
|
||||
alwaysEnabled: [
|
||||
"codegraph_explore",
|
||||
"lsp_diagnostics",
|
||||
@@ -29,9 +29,35 @@ const CURRENT_TOOL_SEARCH_RUNTIME_DEFAULTS = {
|
||||
groupOverrides: {},
|
||||
};
|
||||
|
||||
export const PREVIOUS_TOOL_SEARCH_DEFAULTS = {
|
||||
...PREVIOUS_TOOL_SEARCH_RUNTIME_DEFAULTS,
|
||||
bundleDefaultsVersion: 2,
|
||||
};
|
||||
|
||||
export const CURRENT_TOOL_SEARCH_RUNTIME_DEFAULTS = {
|
||||
alwaysEnabled: [
|
||||
"codegraph_explore",
|
||||
"lsp_diagnostics",
|
||||
"ctx_execute",
|
||||
"ctx_execute_file",
|
||||
"ctx_batch_execute",
|
||||
"memory_search",
|
||||
"session_search",
|
||||
"memory_add",
|
||||
"memory_replace",
|
||||
"memory_remove",
|
||||
"skill_manage",
|
||||
],
|
||||
showToolSearchFooterStatus: false,
|
||||
maxActiveGroups: 5,
|
||||
maxToolsPerGroup: 8,
|
||||
maxDynamicTools: 28,
|
||||
groupOverrides: {},
|
||||
};
|
||||
|
||||
export const CURRENT_TOOL_SEARCH_DEFAULTS = {
|
||||
...CURRENT_TOOL_SEARCH_RUNTIME_DEFAULTS,
|
||||
bundleDefaultsVersion: 2,
|
||||
bundleDefaultsVersion: 3,
|
||||
};
|
||||
|
||||
function isObject(value) {
|
||||
@@ -95,7 +121,11 @@ export async function migrateToolSearchSettings(settingsPath, options = {}) {
|
||||
exactJsonValue(settings.toolSearch, CURRENT_TOOL_SEARCH_DEFAULTS)
|
||||
|| exactJsonValue(settings.toolSearch, CURRENT_TOOL_SEARCH_RUNTIME_DEFAULTS)
|
||||
) return { status: "current" };
|
||||
if (!exactJsonValue(settings.toolSearch, LEGACY_TOOL_SEARCH_DEFAULTS)) return { status: "custom" };
|
||||
const matchesPreviousSnapshot = exactJsonValue(settings.toolSearch, PREVIOUS_TOOL_SEARCH_DEFAULTS)
|
||||
|| exactJsonValue(settings.toolSearch, PREVIOUS_TOOL_SEARCH_RUNTIME_DEFAULTS);
|
||||
if (!exactJsonValue(settings.toolSearch, LEGACY_TOOL_SEARCH_DEFAULTS) && !matchesPreviousSnapshot) {
|
||||
return { status: "custom" };
|
||||
}
|
||||
|
||||
const updated = `${JSON.stringify({ ...settings, toolSearch: CURRENT_TOOL_SEARCH_DEFAULTS }, null, 2)}\n`;
|
||||
const directory = dirname(settingsPath);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { chmod, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { dirname, join, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
@@ -29,14 +29,15 @@ test("all package extensions load together without global registration conflicts
|
||||
dependencies: Record<string, string>;
|
||||
files: string[];
|
||||
pi: { extensions: string[] };
|
||||
allowScripts: Record<string, boolean>;
|
||||
};
|
||||
assert.ok(
|
||||
packageJson.pi.extensions.includes("./extensions/plugin-init.ts"),
|
||||
"the bundle must expose the project plugin initializer command",
|
||||
);
|
||||
assert.ok(
|
||||
packageJson.pi.extensions.includes("./hippo-memory-pi/index.ts"),
|
||||
"the bundle must load the vendored official Hippo Memory extension",
|
||||
packageJson.pi.extensions.includes("./pi-hermes-memory/src/index.ts"),
|
||||
"the bundle must load the vendored Hermes memory extension",
|
||||
);
|
||||
assert.ok(
|
||||
packageJson.pi.extensions.includes("./pi-minimal-footer/index.ts"),
|
||||
@@ -145,19 +146,37 @@ test("all package extensions load together without global registration conflicts
|
||||
assert.equal(packageJson.dependencies["@ogulcancelik/pi-minimal-footer"], "file:./pi-minimal-footer");
|
||||
assert.equal(packageJson.dependencies["@tintinweb/pi-subagents"], undefined);
|
||||
assert.equal(packageJson.pi.extensions.includes("./extensions/subagents.ts"), false);
|
||||
assert.ok(
|
||||
packageJson.pi.extensions.includes("./pi-hermes-memory/src/index.ts"),
|
||||
"the bundle must load the locally maintained Hermes memory extension",
|
||||
);
|
||||
assert.equal(packageJson.dependencies["pi-hermes-memory"], "file:./pi-hermes-memory");
|
||||
assert.ok(
|
||||
packageJson.files.includes("pi-hermes-memory"),
|
||||
"the packed bundle must include the locally maintained Hermes memory source",
|
||||
);
|
||||
assert.ok(
|
||||
packageJson.pi.extensions.indexOf("./pi-hermes-memory/src/index.ts") <
|
||||
packageJson.pi.extensions.indexOf("./pi-compaction/index.ts"),
|
||||
"the memory extension must register its session_before_compact flush before the compaction authority can cancel the event",
|
||||
);
|
||||
assert.equal(
|
||||
packageJson.pi.extensions.some((entry) => entry.includes("pi-hermes-memory") || entry.includes("the-forge-flow")),
|
||||
packageJson.pi.extensions.some((entry) => entry.includes("hippo-memory-pi") || entry.includes("the-forge-flow")),
|
||||
false,
|
||||
"retired Hermes and third-party Hippo extensions must not be loaded",
|
||||
"retired Hippo and third-party memory extensions must not be loaded",
|
||||
);
|
||||
assert.equal(packageJson.dependencies["@the-forge-flow/hippo-memory-pi"], undefined);
|
||||
assert.equal(packageJson.dependencies["better-sqlite3"], undefined);
|
||||
assert.equal(packageJson.allowScripts["better-sqlite3@12.11.1"], true);
|
||||
assert.equal(
|
||||
packageJson.allowScripts["better-sqlite3@13.0.3"],
|
||||
undefined,
|
||||
"Hermes uses better-sqlite3 13 N-API prebuilds, so no extra install-script approval is required",
|
||||
);
|
||||
assert.equal(packageJson.dependencies["@sinclair/typebox"], undefined);
|
||||
const home = await mkdtemp(join(tmpdir(), "my-pi-extension-load-"));
|
||||
const mockBin = join(home, "bin");
|
||||
await mkdir(mockBin, { recursive: true });
|
||||
const hippo = join(mockBin, "hippo");
|
||||
await writeFile(hippo, "#!/bin/sh\nprintf '%s\\n' 'Hippo Memory status'\n", "utf8");
|
||||
await chmod(hippo, 0o755);
|
||||
const args = ["--no-extensions"];
|
||||
for (const entry of packageJson.pi.extensions) args.push("-e", resolve(repositoryRoot, entry));
|
||||
args.push("--mode", "rpc");
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { chmod, mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import test from "node:test";
|
||||
|
||||
import hippoExtension from "../hippo-memory-pi/index.ts";
|
||||
|
||||
test("official Hippo extension registers five tools and runs lifecycle CLI calls", async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), "my-pi-hippo-extension-"));
|
||||
const bin = join(root, "bin");
|
||||
const project = join(root, "project");
|
||||
const log = join(root, "hippo.log");
|
||||
await mkdir(bin, { recursive: true });
|
||||
await mkdir(join(project, ".hippo"), { recursive: true });
|
||||
const hippo = join(bin, "hippo");
|
||||
await writeFile(
|
||||
hippo,
|
||||
`#!/bin/sh\nprintf '%s\\n' "$*" >> "$HIPPO_TEST_LOG"\ncase "$1" in\n status) printf '%s\\n' 'Hippo status ok' ;;\n context) printf '%s\\n' 'Remembered project context for testing' ;;\n recall) printf '%s\\n' 'Relevant project memory' ;;\n remember) printf '%s\\n' 'Remembered [test]' ;;\n outcome) printf '%s\\n' 'Outcome recorded' ;;\n sleep) printf '%s\\n' 'Sleep complete' ;;\nesac\n`,
|
||||
"utf8",
|
||||
);
|
||||
await chmod(hippo, 0o755);
|
||||
|
||||
const previousPath = process.env.PATH;
|
||||
const previousLog = process.env.HIPPO_TEST_LOG;
|
||||
process.env.PATH = `${bin}:${previousPath ?? ""}`;
|
||||
process.env.HIPPO_TEST_LOG = log;
|
||||
try {
|
||||
const handlers = new Map<string, Array<(event: any, ctx: any) => Promise<any>>>();
|
||||
const tools = new Map<string, any>();
|
||||
hippoExtension({
|
||||
on(name: string, handler: (event: any, ctx: any) => Promise<any>) {
|
||||
const entries = handlers.get(name) ?? [];
|
||||
entries.push(handler);
|
||||
handlers.set(name, entries);
|
||||
},
|
||||
registerTool(tool: any) {
|
||||
tools.set(tool.name, tool);
|
||||
},
|
||||
});
|
||||
|
||||
assert.deepEqual(
|
||||
[...tools.keys()].sort(),
|
||||
["hippo_context", "hippo_outcome", "hippo_recall", "hippo_remember", "hippo_status"],
|
||||
);
|
||||
const ctx = { cwd: project };
|
||||
const startResult = await handlers.get("session_start")?.[0]?.({}, ctx);
|
||||
assert.match(startResult.systemPromptAppend, /Remembered project context/);
|
||||
const rememberResult = await tools.get("hippo_remember").execute("call-1", { text: "A durable lesson" }, undefined, undefined, ctx);
|
||||
assert.match(rememberResult.content[0].text, /Remembered \[test\]/);
|
||||
await handlers.get("tool_result")?.[0]?.(
|
||||
{ isError: true, toolName: "build", content: "Compilation failed because the generated symbol was missing" },
|
||||
ctx,
|
||||
);
|
||||
await handlers.get("session_shutdown")?.[0]?.({}, ctx);
|
||||
|
||||
const calls = await readFile(log, "utf8");
|
||||
assert.match(calls, /context --auto/);
|
||||
assert.match(calls, /remember A durable lesson/);
|
||||
assert.match(calls, /remember Tool 'build' failed:/);
|
||||
assert.match(calls, /sleep/);
|
||||
} finally {
|
||||
process.env.PATH = previousPath;
|
||||
if (previousLog === undefined) delete process.env.HIPPO_TEST_LOG;
|
||||
else process.env.HIPPO_TEST_LOG = previousLog;
|
||||
}
|
||||
});
|
||||
@@ -1,187 +0,0 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { chmod, mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { dirname, join, resolve } from "node:path";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import test from "node:test";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
|
||||
async function executable(path: string, content: string): Promise<void> {
|
||||
await writeFile(path, `#!/bin/sh\n${content}\n`, "utf8");
|
||||
await chmod(path, 0o755);
|
||||
}
|
||||
|
||||
async function fixture(initialHippoVersion?: string) {
|
||||
const root = await mkdtemp(join(tmpdir(), "my-pi-hippo-scripts-"));
|
||||
const home = join(root, "home");
|
||||
const bin = join(root, "bin");
|
||||
const project = join(root, "project");
|
||||
const log = join(root, "commands.log");
|
||||
const versionState = join(root, "hippo-version");
|
||||
await mkdir(home, { recursive: true });
|
||||
await mkdir(bin, { recursive: true });
|
||||
await mkdir(project, { recursive: true });
|
||||
await writeFile(log, "", "utf8");
|
||||
if (initialHippoVersion) await writeFile(versionState, `${initialHippoVersion}\n`, "utf8");
|
||||
|
||||
await executable(join(bin, "pi"), 'printf "pi %s\\n" "$*" >> "$MOCK_LOG"');
|
||||
await executable(
|
||||
join(bin, "npm"),
|
||||
String.raw`printf 'npm %s\n' "$*" >> "$MOCK_LOG"
|
||||
if [ "$1" = "view" ]; then
|
||||
printf '%s\n' '1.33.0'
|
||||
elif [ "$1" = "install" ] && [ "$2" = "-g" ]; then
|
||||
printf '%s\n' '1.33.0' > "$HIPPO_VERSION_STATE"
|
||||
cat > "$MOCK_BIN/hippo" <<'HIPPO'
|
||||
#!/bin/sh
|
||||
if [ "$1" = "--version" ]; then cat "$HIPPO_VERSION_STATE"; exit 0; fi
|
||||
if [ "$1" = "init" ]; then mkdir -p .hippo; printf 'hippo init\n' >> "$MOCK_LOG"; exit 0; fi
|
||||
exit 0
|
||||
HIPPO
|
||||
chmod +x "$MOCK_BIN/hippo"
|
||||
fi`,
|
||||
);
|
||||
if (initialHippoVersion) {
|
||||
await executable(
|
||||
join(bin, "hippo"),
|
||||
String.raw`if [ "$1" = "--version" ]; then cat "$HIPPO_VERSION_STATE"; exit 0; fi
|
||||
if [ "$1" = "init" ]; then mkdir -p .hippo; printf 'hippo init\n' >> "$MOCK_LOG"; exit 0; fi
|
||||
exit 0`,
|
||||
);
|
||||
}
|
||||
|
||||
const env = {
|
||||
...process.env,
|
||||
HOME: home,
|
||||
PATH: `${bin}:/usr/bin:/bin`,
|
||||
MOCK_BIN: bin,
|
||||
MOCK_LOG: log,
|
||||
HIPPO_VERSION_STATE: versionState,
|
||||
PI_PACKAGE_SOURCE: repositoryRoot,
|
||||
ZSH: "",
|
||||
ZSH_CUSTOM: "",
|
||||
ZDOTDIR: home,
|
||||
};
|
||||
return { root, home, bin, project, log, versionState, env };
|
||||
}
|
||||
|
||||
async function logText(path: string): Promise<string> {
|
||||
return readFile(path, "utf8");
|
||||
}
|
||||
|
||||
test("install.sh leaves Hippo absent when installation is declined", async () => {
|
||||
const f = await fixture();
|
||||
const result = spawnSync("sh", [join(repositoryRoot, "install.sh")], {
|
||||
cwd: f.project,
|
||||
env: f.env,
|
||||
input: "n\n".repeat(14),
|
||||
encoding: "utf8",
|
||||
timeout: 30_000,
|
||||
});
|
||||
assert.equal(result.status, 0, result.stderr || result.stdout);
|
||||
assert.doesNotMatch(await logText(f.log), /npm install -g hippo-memory/);
|
||||
assert.match(result.stdout, /已跳过 Hippo Memory CLI/);
|
||||
});
|
||||
|
||||
|
||||
test("install.sh installs the pinned Hippo CLI without choosing an init directory", async () => {
|
||||
const f = await fixture();
|
||||
const input = `y\n${"n\n".repeat(12)}`;
|
||||
const result = spawnSync("sh", [join(repositoryRoot, "install.sh")], {
|
||||
cwd: f.project,
|
||||
env: f.env,
|
||||
input,
|
||||
encoding: "utf8",
|
||||
timeout: 30_000,
|
||||
});
|
||||
assert.equal(result.status, 0, result.stderr || result.stdout);
|
||||
const log = await logText(f.log);
|
||||
assert.match(log, /npm install -g hippo-memory@1\.33\.0/);
|
||||
assert.doesNotMatch(log, /hippo init/);
|
||||
assert.equal(await readFile(f.versionState, "utf8"), "1.33.0\n");
|
||||
assert.match(result.stdout, /未自动运行 hippo init/);
|
||||
});
|
||||
|
||||
test("update.sh leaves an already matching Hippo CLI untouched", async () => {
|
||||
const f = await fixture("1.33.0");
|
||||
const result = spawnSync("sh", [join(repositoryRoot, "update.sh")], {
|
||||
cwd: f.project,
|
||||
env: f.env,
|
||||
encoding: "utf8",
|
||||
timeout: 30_000,
|
||||
});
|
||||
assert.equal(result.status, 0, result.stderr || result.stdout);
|
||||
const log = await logText(f.log);
|
||||
assert.match(log, /npm view hippo-memory@1\.33\.0 version/);
|
||||
assert.doesNotMatch(log, /npm install -g/);
|
||||
assert.match(result.stdout, /已是组合包目标版本/);
|
||||
});
|
||||
|
||||
test("update.sh upgrades an npm-managed older Hippo CLI exactly once", async () => {
|
||||
const f = await fixture("1.32.0");
|
||||
const result = spawnSync("sh", [join(repositoryRoot, "update.sh")], {
|
||||
cwd: f.project,
|
||||
env: f.env,
|
||||
encoding: "utf8",
|
||||
timeout: 30_000,
|
||||
});
|
||||
assert.equal(result.status, 0, result.stderr || result.stdout);
|
||||
const log = await logText(f.log);
|
||||
assert.match(log, /npm list -g --depth=0 hippo-memory/);
|
||||
assert.equal((log.match(/npm install -g hippo-memory@1\.33\.0/g) ?? []).length, 1);
|
||||
assert.equal(await readFile(f.versionState, "utf8"), "1.33.0\n");
|
||||
});
|
||||
|
||||
test("update.sh preserves a Hippo executable that is not npm globally managed", async () => {
|
||||
const f = await fixture("1.32.0");
|
||||
await executable(
|
||||
join(f.bin, "npm"),
|
||||
String.raw`printf 'npm %s\n' "$*" >> "$MOCK_LOG"
|
||||
if [ "$1" = "view" ]; then printf '%s\n' '1.33.0'; exit 0; fi
|
||||
if [ "$1" = "list" ]; then exit 1; fi
|
||||
exit 0`,
|
||||
);
|
||||
const result = spawnSync("sh", [join(repositoryRoot, "update.sh")], {
|
||||
cwd: f.project,
|
||||
env: f.env,
|
||||
encoding: "utf8",
|
||||
timeout: 30_000,
|
||||
});
|
||||
assert.equal(result.status, 1);
|
||||
const log = await logText(f.log);
|
||||
assert.doesNotMatch(log, /npm install -g/);
|
||||
assert.match(result.stderr, /不属于 npm 全局/);
|
||||
});
|
||||
|
||||
|
||||
test("uninstall.sh keeps Hippo by default and removes only after explicit approval", async () => {
|
||||
const keep = await fixture("1.33.0");
|
||||
await mkdir(join(keep.project, ".hippo"), { recursive: true });
|
||||
await writeFile(join(keep.project, ".hippo", "sentinel"), "keep\n", "utf8");
|
||||
const keepResult = spawnSync("sh", [join(repositoryRoot, "uninstall.sh")], {
|
||||
cwd: keep.project,
|
||||
env: keep.env,
|
||||
input: "n\n",
|
||||
encoding: "utf8",
|
||||
timeout: 30_000,
|
||||
});
|
||||
assert.equal(keepResult.status, 0, keepResult.stderr || keepResult.stdout);
|
||||
assert.doesNotMatch(await logText(keep.log), /npm uninstall/);
|
||||
assert.equal(await readFile(join(keep.project, ".hippo", "sentinel"), "utf8"), "keep\n");
|
||||
|
||||
const remove = await fixture("1.33.0");
|
||||
await mkdir(join(remove.project, ".hippo"), { recursive: true });
|
||||
await writeFile(join(remove.project, ".hippo", "sentinel"), "keep\n", "utf8");
|
||||
const removeResult = spawnSync("sh", [join(repositoryRoot, "uninstall.sh")], {
|
||||
cwd: remove.project,
|
||||
env: remove.env,
|
||||
input: "y\n",
|
||||
encoding: "utf8",
|
||||
timeout: 30_000,
|
||||
});
|
||||
assert.equal(removeResult.status, 0, removeResult.stderr || removeResult.stdout);
|
||||
assert.match(await logText(remove.log), /npm uninstall -g hippo-memory/);
|
||||
assert.equal(await readFile(join(remove.project, ".hippo", "sentinel"), "utf8"), "keep\n");
|
||||
});
|
||||
+13
-23
@@ -11,12 +11,12 @@ import pluginInitExtension, {
|
||||
validatePluginInitCwd,
|
||||
} from "../extensions/plugin-init.ts";
|
||||
|
||||
test("initializes missing CodeGraph and Hippo state after preflighting both CLIs", async () => {
|
||||
test("initializes missing CodeGraph state after preflighting the CLI", async () => {
|
||||
const cwd = await mkdtemp(join(tmpdir(), "my-pi-plugin-init-"));
|
||||
const calls: Array<{ command: string; args: string[]; cwd: string }> = [];
|
||||
try {
|
||||
const targets = await findMissingPluginInitializers(cwd);
|
||||
assert.deepEqual(targets.map(({ name }) => name), ["CodeGraph", "Hippo"]);
|
||||
assert.deepEqual(targets.map(({ name }) => name), ["CodeGraph"]);
|
||||
|
||||
await initializeMissingPlugins(cwd, targets, async (command, args, options) => {
|
||||
calls.push({ command, args, cwd: options.cwd });
|
||||
@@ -25,30 +25,25 @@ test("initializes missing CodeGraph and Hippo state after preflighting both CLIs
|
||||
|
||||
assert.deepEqual(calls, [
|
||||
{ command: "codegraph", args: ["--version"], cwd },
|
||||
{ command: "hippo", args: ["--version"], cwd },
|
||||
{ command: "codegraph", args: ["init", cwd], cwd },
|
||||
{ command: "hippo", args: ["init"], cwd },
|
||||
]);
|
||||
} finally {
|
||||
await rm(cwd, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("uses the CLIs' database files as authoritative initialization markers", async () => {
|
||||
test("uses the CLI's database file as the authoritative initialization marker", async () => {
|
||||
const cwd = await mkdtemp(join(tmpdir(), "my-pi-plugin-init-"));
|
||||
const calls: string[] = [];
|
||||
try {
|
||||
await mkdir(join(cwd, ".codegraph"));
|
||||
await mkdir(join(cwd, ".hippo"));
|
||||
assert.deepEqual(
|
||||
(await findMissingPluginInitializers(cwd)).map(({ name }) => name),
|
||||
["CodeGraph", "Hippo"],
|
||||
"bare state directories must not count as initialized",
|
||||
["CodeGraph"],
|
||||
"a bare state directory must not count as initialized",
|
||||
);
|
||||
|
||||
await writeFile(join(cwd, ".codegraph", "codegraph.db"), "");
|
||||
assert.deepEqual((await findMissingPluginInitializers(cwd)).map(({ name }) => name), ["Hippo"]);
|
||||
await writeFile(join(cwd, ".hippo", "hippo.db"), "");
|
||||
const targets = await findMissingPluginInitializers(cwd);
|
||||
assert.deepEqual(targets, []);
|
||||
|
||||
@@ -56,7 +51,7 @@ test("uses the CLIs' database files as authoritative initialization markers", as
|
||||
calls.push(`${command} ${args.join(" ")}`);
|
||||
return { code: 0, stdout: "ok", stderr: "" };
|
||||
});
|
||||
assert.deepEqual(calls, ["codegraph --version", "hippo --version"]);
|
||||
assert.deepEqual(calls, ["codegraph --version"]);
|
||||
} finally {
|
||||
await rm(cwd, { recursive: true, force: true });
|
||||
}
|
||||
@@ -67,7 +62,7 @@ test("rejects unsafe project roots", () => {
|
||||
assert.throws(() => validatePluginInitCwd(homedir()), /用户主目录/);
|
||||
});
|
||||
|
||||
test("preflights both CLIs before creating project state", async () => {
|
||||
test("preflights the CLI before creating project state", async () => {
|
||||
const cwd = await mkdtemp(join(tmpdir(), "my-pi-plugin-init-"));
|
||||
const calls: string[] = [];
|
||||
try {
|
||||
@@ -75,13 +70,11 @@ test("preflights both CLIs before creating project state", async () => {
|
||||
await assert.rejects(
|
||||
initializeMissingPlugins(cwd, targets, async (command, args) => {
|
||||
calls.push(`${command} ${args.join(" ")}`);
|
||||
return command === "hippo"
|
||||
? { code: 1, stdout: "", stderr: "not installed" }
|
||||
: { code: 0, stdout: "ok", stderr: "" };
|
||||
return { code: 1, stdout: "", stderr: "not installed" };
|
||||
}),
|
||||
/Hippo 检测失败:not installed/,
|
||||
/CodeGraph 检测失败:not installed/,
|
||||
);
|
||||
assert.deepEqual(calls, ["codegraph --version", "hippo --version"]);
|
||||
assert.deepEqual(calls, ["codegraph --version"]);
|
||||
} finally {
|
||||
await rm(cwd, { recursive: true, force: true });
|
||||
}
|
||||
@@ -125,9 +118,7 @@ test("plugin_init confirms the target and hot-reloads after initialization", asy
|
||||
assert.match(confirmation, new RegExp(cwd.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")));
|
||||
assert.deepEqual(calls, [
|
||||
"codegraph --version",
|
||||
"hippo --version",
|
||||
`codegraph init ${cwd}`,
|
||||
"hippo init",
|
||||
]);
|
||||
assert.equal(reloaded, true);
|
||||
} finally {
|
||||
@@ -135,14 +126,14 @@ test("plugin_init confirms the target and hot-reloads after initialization", asy
|
||||
}
|
||||
});
|
||||
|
||||
test("plugin_init reports retained partial state and does not reload when a later initializer fails", async () => {
|
||||
test("plugin_init reports the failure and does not reload when initialization fails", async () => {
|
||||
let command: { handler: (args: string, ctx: any) => Promise<void> } | undefined;
|
||||
const pi = {
|
||||
registerCommand(_name: string, definition: typeof command) {
|
||||
command = definition;
|
||||
},
|
||||
exec: async (program: string, args: string[]) => ({
|
||||
code: program === "hippo" && args[0] === "init" ? 1 : 0,
|
||||
code: program === "codegraph" && args[0] === "init" ? 1 : 0,
|
||||
stdout: "",
|
||||
stderr: "mock failure",
|
||||
}),
|
||||
@@ -168,8 +159,7 @@ test("plugin_init reports retained partial state and does not reload when a late
|
||||
reloaded = true;
|
||||
},
|
||||
});
|
||||
assert.match(error, /Hippo 初始化失败:mock failure/);
|
||||
assert.match(error, /已完成的 CodeGraph 初始化会保留,不会自动回滚/);
|
||||
assert.match(error, /CodeGraph 初始化失败:mock failure/);
|
||||
assert.equal(reloaded, false);
|
||||
} finally {
|
||||
await rm(cwd, { recursive: true, force: true });
|
||||
|
||||
@@ -9,6 +9,7 @@ import { fileURLToPath } from "node:url";
|
||||
import {
|
||||
CURRENT_TOOL_SEARCH_DEFAULTS,
|
||||
LEGACY_TOOL_SEARCH_DEFAULTS,
|
||||
PREVIOUS_TOOL_SEARCH_DEFAULTS,
|
||||
migrateToolSearchSettings,
|
||||
} from "../scripts/migrate-tool-search-settings.mjs";
|
||||
|
||||
@@ -89,6 +90,36 @@ test("recognizes current defaults and skips files without a Tool Search section"
|
||||
assert.deepEqual(await migrateToolSearchSettings(absent.settingsPath), { status: "missing-section" });
|
||||
});
|
||||
|
||||
test("migrates the previous versioned default snapshot and pins the Hermes memory tools", async (t) => {
|
||||
const { settingsPath } = await fixture(t);
|
||||
await writeFile(settingsPath, JSON.stringify({ toolSearch: PREVIOUS_TOOL_SEARCH_DEFAULTS }), "utf8");
|
||||
|
||||
const result = await migrateToolSearchSettings(settingsPath);
|
||||
assert.equal(result.status, "migrated");
|
||||
const migrated = JSON.parse(await readFile(settingsPath, "utf8")).toolSearch;
|
||||
assert.deepEqual(migrated, CURRENT_TOOL_SEARCH_DEFAULTS);
|
||||
assert.equal(migrated.bundleDefaultsVersion, 3);
|
||||
for (const name of ["memory_search", "session_search", "memory_add", "memory_replace", "memory_remove", "skill_manage"]) {
|
||||
assert.ok(migrated.alwaysEnabled.includes(name), `${name} must be pinned after migration`);
|
||||
}
|
||||
});
|
||||
|
||||
test("preserves custom settings that keep the previous capacities but pin extra tools", async (t) => {
|
||||
const { root, settingsPath } = await fixture(t);
|
||||
const custom = {
|
||||
toolSearch: {
|
||||
...PREVIOUS_TOOL_SEARCH_DEFAULTS,
|
||||
alwaysEnabled: [...PREVIOUS_TOOL_SEARCH_DEFAULTS.alwaysEnabled, "custom_tool"],
|
||||
},
|
||||
};
|
||||
const original = `${JSON.stringify(custom, null, 2)}\n`;
|
||||
await writeFile(settingsPath, original, "utf8");
|
||||
|
||||
assert.deepEqual(await migrateToolSearchSettings(settingsPath), { status: "custom" });
|
||||
assert.equal(await readFile(settingsPath, "utf8"), original);
|
||||
assert.deepEqual(await readdir(root), ["settings.json"]);
|
||||
});
|
||||
|
||||
test("fails safely for invalid JSON and reports a missing settings file", async (t) => {
|
||||
const invalid = await fixture(t);
|
||||
await writeFile(invalid.settingsPath, "{ invalid\n", "utf8");
|
||||
@@ -130,8 +161,8 @@ async function updateFixture(t: test.TestContext, piExitStatus = 0) {
|
||||
await mkdir(join(updatedBundle, "scripts"), { recursive: true });
|
||||
await copyFile(join(repositoryRoot, "config", "p10k.zsh"), join(updatedBundle, "config", "p10k.zsh"));
|
||||
await copyFile(
|
||||
join(repositoryRoot, "config", "hippo-memory-version"),
|
||||
join(updatedBundle, "config", "hippo-memory-version"),
|
||||
join(repositoryRoot, "config", "pi-hashline-edit.json"),
|
||||
join(updatedBundle, "config", "pi-hashline-edit.json"),
|
||||
);
|
||||
await copyFile(
|
||||
join(repositoryRoot, "scripts", "migrate-tool-search-settings.mjs"),
|
||||
|
||||
@@ -29,8 +29,8 @@ async function fixture(
|
||||
await mkdir(join(target, "config"), { recursive: true });
|
||||
await copyFile(join(repositoryRoot, "config", "p10k.zsh"), join(target, "config", "p10k.zsh"));
|
||||
await copyFile(
|
||||
join(repositoryRoot, "config", "hippo-memory-version"),
|
||||
join(target, "config", "hippo-memory-version"),
|
||||
join(repositoryRoot, "config", "pi-hashline-edit.json"),
|
||||
join(target, "config", "pi-hashline-edit.json"),
|
||||
);
|
||||
await mkdir(bin, { recursive: true });
|
||||
|
||||
|
||||
@@ -3,19 +3,6 @@
|
||||
set -u
|
||||
|
||||
PACKAGE_SOURCE=${PI_PACKAGE_SOURCE:-git:git@bitbucket.org:siakitem/my-pi.git}
|
||||
uninstall_failed=0
|
||||
|
||||
ask_yes_no() {
|
||||
prompt=$1
|
||||
printf '%s [y/N] ' "$prompt"
|
||||
if ! IFS= read -r answer; then
|
||||
answer=n
|
||||
fi
|
||||
case $answer in
|
||||
y|Y|yes|YES|Yes) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
if ! command -v pi >/dev/null 2>&1; then
|
||||
printf '错误: 找不到 pi,无法卸载组合包。请确保 pi 位于 PATH。\n' >&2
|
||||
@@ -28,27 +15,4 @@ if ! pi remove "$PACKAGE_SOURCE"; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if command -v hippo >/dev/null 2>&1; then
|
||||
hippo_version=$(hippo --version 2>/dev/null | sed -n 's/^[^0-9]*\([0-9][0-9.]*\).*$/\1/p' | sed -n '1p')
|
||||
if ask_yes_no "检测到 Hippo Memory CLI ${hippo_version:-未知版本}。是否同时卸载 npm 全局 hippo-memory?项目与全局记忆数据会保留。"; then
|
||||
if ! command -v npm >/dev/null 2>&1; then
|
||||
printf '%s\n' '警告: 找不到 npm,无法卸载 Hippo Memory CLI。' >&2
|
||||
uninstall_failed=1
|
||||
elif ! npm list -g --depth=0 hippo-memory >/dev/null 2>&1; then
|
||||
printf '%s\n' '警告: 当前 hippo 不属于 npm 全局 hippo-memory 安装,已保留。' >&2
|
||||
uninstall_failed=1
|
||||
elif npm uninstall -g hippo-memory; then
|
||||
printf '%s\n' 'Hippo Memory CLI 已卸载;.hippo 与用户级记忆数据均已保留。'
|
||||
else
|
||||
printf '%s\n' '警告: Hippo Memory CLI 卸载失败。' >&2
|
||||
uninstall_failed=1
|
||||
fi
|
||||
else
|
||||
printf '%s\n' 'Hippo Memory CLI 已保留。'
|
||||
fi
|
||||
else
|
||||
printf '%s\n' '未检测到 Hippo Memory CLI,无需额外卸载。'
|
||||
fi
|
||||
|
||||
printf '%s\n' '组合包已移除。CodeGraph、Kotlin LSP、JDT LS、Java 和记忆数据均未卸载,因为它们可能仍被其他项目使用。'
|
||||
exit "$uninstall_failed"
|
||||
|
||||
@@ -13,7 +13,6 @@ fi
|
||||
|
||||
PACKAGE_SOURCE=${PI_PACKAGE_SOURCE:-git:git@bitbucket.org:siakitem/my-pi.git}
|
||||
POWERLEVEL10K_PRESET_FILE=$SCRIPT_DIR/config/p10k.zsh
|
||||
HIPPO_MEMORY_VERSION_FILE=$SCRIPT_DIR/config/hippo-memory-version
|
||||
CODEGRAPH_INSTALL_URL=https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.sh
|
||||
CODEGRAPH_LATEST_RELEASE_URL=https://github.com/colbymchenry/codegraph/releases/latest
|
||||
KITTY_INSTALL_URL=https://sw.kovidgoyal.net/kitty/installer.sh
|
||||
@@ -50,7 +49,6 @@ select_updated_bundle_config() {
|
||||
|
||||
if [ -n "$updated_bundle_dir" ] && [ -f "$updated_bundle_dir/config/p10k.zsh" ]; then
|
||||
POWERLEVEL10K_PRESET_FILE=$updated_bundle_dir/config/p10k.zsh
|
||||
HIPPO_MEMORY_VERSION_FILE=$updated_bundle_dir/config/hippo-memory-version
|
||||
elif [ ! -f "$POWERLEVEL10K_PRESET_FILE" ]; then
|
||||
warn "无法定位升级后的组合包配置,找不到 ${POWERLEVEL10K_PRESET_FILE}。"
|
||||
return 1
|
||||
@@ -122,7 +120,7 @@ migrate_tool_search_settings() {
|
||||
migration_result=$(node "$migration_helper" "$settings_file") || return 1
|
||||
case $migration_result in
|
||||
migrated:*)
|
||||
say "✓ Tool Search 旧版默认配置已迁移到 5 个动态组 / 28 个动态工具。"
|
||||
say "✓ Tool Search 旧版默认配置已迁移到当前组合包默认值。"
|
||||
say " 原配置备份:${migration_result#migrated:}"
|
||||
;;
|
||||
current)
|
||||
@@ -396,62 +394,6 @@ update_codegraph() {
|
||||
rm -f "$installer_file"
|
||||
}
|
||||
|
||||
read_hippo_memory_version() {
|
||||
if [ ! -f "$HIPPO_MEMORY_VERSION_FILE" ]; then
|
||||
warn "找不到 Hippo Memory 版本文件:$HIPPO_MEMORY_VERSION_FILE"
|
||||
return 1
|
||||
fi
|
||||
hippo_target_version=$(sed -n '1p' "$HIPPO_MEMORY_VERSION_FILE")
|
||||
case $hippo_target_version in
|
||||
''|*[!0-9.]*)
|
||||
warn "Hippo Memory 版本文件内容无效:$hippo_target_version"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
printf '%s\n' "$hippo_target_version"
|
||||
}
|
||||
|
||||
installed_hippo_memory_version() {
|
||||
hippo --version 2>/dev/null | sed -n 's/^[^0-9]*\([0-9][0-9.]*\).*$/\1/p' | sed -n '1p'
|
||||
}
|
||||
|
||||
update_hippo_memory() {
|
||||
if ! command -v hippo >/dev/null 2>&1; then
|
||||
say "- Hippo Memory CLI 未安装,升级流程跳过。"
|
||||
return 0
|
||||
fi
|
||||
if ! command -v npm >/dev/null 2>&1; then
|
||||
warn "升级 Hippo Memory CLI 需要 npm。"
|
||||
return 1
|
||||
fi
|
||||
hippo_target_version=$(read_hippo_memory_version) || return 1
|
||||
hippo_published_version=$(npm view "hippo-memory@$hippo_target_version" version 2>/dev/null) || hippo_published_version=
|
||||
if [ "$hippo_published_version" != "$hippo_target_version" ]; then
|
||||
warn "无法确认 npm 上的 Hippo Memory 目标版本 $hippo_target_version,已保留现状。"
|
||||
return 1
|
||||
fi
|
||||
hippo_installed_version=$(installed_hippo_memory_version)
|
||||
if [ -z "$hippo_installed_version" ]; then
|
||||
warn "无法读取 Hippo Memory CLI 本地版本,已保留现状。"
|
||||
return 1
|
||||
fi
|
||||
if [ "$hippo_installed_version" = "$hippo_target_version" ]; then
|
||||
say "✓ Hippo Memory CLI $hippo_installed_version 已是组合包目标版本。"
|
||||
return 0
|
||||
fi
|
||||
if ! npm list -g --depth=0 hippo-memory >/dev/null 2>&1; then
|
||||
warn "检测到 hippo,但它不属于 npm 全局 hippo-memory 安装;已保留现状。"
|
||||
return 1
|
||||
fi
|
||||
say "发现 Hippo Memory CLI ${hippo_target_version}(当前 ${hippo_installed_version}),正在升级。"
|
||||
npm install -g "hippo-memory@$hippo_target_version" || return 1
|
||||
hippo_updated_version=$(installed_hippo_memory_version)
|
||||
if [ "$hippo_updated_version" != "$hippo_target_version" ]; then
|
||||
warn "Hippo Memory CLI 升级后版本校验失败:期望 $hippo_target_version,实际 ${hippo_updated_version:-未知}。"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
update_zsh_environment() {
|
||||
resolve_zsh_paths
|
||||
if [ ! -f "$oh_my_zsh_dir/oh-my-zsh.sh" ]; then
|
||||
@@ -548,7 +490,6 @@ say "组合包升级完成,开始升级已安装的终端环境和机器级 CL
|
||||
update_kitty || record_update_failure "Kitty"
|
||||
update_zsh_environment || record_update_failure "Zsh 环境"
|
||||
update_codegraph || record_update_failure "CodeGraph"
|
||||
update_hippo_memory || record_update_failure "Hippo Memory CLI"
|
||||
update_brew_formula_if_installed "kotlin-lsp" "kotlin-lsp" "kotlin-lsp"
|
||||
update_brew_formula_if_installed "openjdk@21" "Java 21" "java"
|
||||
update_brew_formula_if_installed "jdtls" "jdtls" "jdtls"
|
||||
|
||||
Reference in New Issue
Block a user