同步 latest skill releases
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
"baimiao-table-ocr": "v1",
|
"baimiao-table-ocr": "v1",
|
||||||
"disk-master": "v1",
|
"disk-master": "v1",
|
||||||
"everything-file-search": "v1",
|
"everything-file-search": "v1",
|
||||||
"init-workspace": "v3",
|
"init-workspace": "v4",
|
||||||
"teach-me": "v2",
|
"teach-me": "v2",
|
||||||
"think-twice": "v3.1",
|
"think-twice": "v3.1",
|
||||||
"why-here": "v1"
|
"why-here": "v1"
|
||||||
|
|||||||
@@ -8,23 +8,23 @@ disable-model-invocation: true
|
|||||||
|
|
||||||
先检查环境,再根据用户要求决定只报告、初始化新目录或补全已有工作区。一个工作区可以容纳多个源码仓、`docs/` 本地文档和 `pkgs/` 参考库。环境检查不是初始化授权,也不是启动 worker 的授权。
|
先检查环境,再根据用户要求决定只报告、初始化新目录或补全已有工作区。一个工作区可以容纳多个源码仓、`docs/` 本地文档和 `pkgs/` 参考库。环境检查不是初始化授权,也不是启动 worker 的授权。
|
||||||
|
|
||||||
## Step 1 — 检查 Herdr 与 skills
|
## Step 1 — 确认 Herdr 环境与 skills
|
||||||
|
|
||||||
先检查 `HERDR_ENV` 和 PATH 上的 `herdr`,再查找当前宿主列出或用户指定的 `herdr`、`think-twice`、`why-here` skill,并实际读取文件。记录来源和能否读取;目录存在不等于宿主可用,同名副本以宿主当前选择为准。
|
先检查 `HERDR_ENV` 和 PATH 上的 `herdr`,再查找当前宿主列出或用户指定的 `herdr`、`think-twice`、`why-here` skill,并实际读取文件。记录来源和能否读取;目录存在不等于宿主可用,同名副本以宿主当前选择为准。
|
||||||
|
|
||||||
- `HERDR_ENV` 不为 `1`:不查询或控制 Herdr 会话。可以记录 CLI 存在,并按用户要求为以后生成条件式规则。
|
- `HERDR_ENV` 不为 `1`:不查询或控制 Herdr 会话。可以记录 CLI 存在,并按用户要求为以后生成条件式规则。
|
||||||
- 在 Herdr 内:读取可访问的 Herdr skill 后,按其要求查询 CLI 帮助与当前会话,核验可用性。宿主没有列出 Herdr skill 时,可运行 `herdr --skill` 获取内置文本并读取;记录其来源为 CLI,不声称已安装到所有宿主。
|
- 在 Herdr 内:读取可访问的 Herdr skill。宿主没有列出时,可运行 `herdr --skill` 获取内置文本并读取;记录其来源为 CLI,不声称已安装到所有宿主。
|
||||||
- 缺少 skill:报告缺口,不自动安装,也不生成无法执行的 skill 引用。用户明确允许安装后,重新检查。
|
- 缺少 skill:报告缺口,不自动安装,也不生成无法执行的 skill 引用。用户明确允许安装后,重新检查。
|
||||||
- 主控可读不代表 worker 可读;协作规则要求启动 worker 后重新核验必需 skill。
|
|
||||||
|
|
||||||
**完成条件:** Herdr 环境和三个 skill 的状态明确;“未查”“不可读”“当前可读”分开记录。
|
**完成条件:** 是否处于 Herdr 环境及三个 skill 的可读性明确。
|
||||||
|
|
||||||
## Step 2 — 检查工具、模型与目标目录
|
## Step 2 — 检查协作环境、工具与目标目录
|
||||||
|
|
||||||
读取 [references/environment-checks.md](references/environment-checks.md),按其中的命令和边界运行检查。脚本路径相对本 skill,不相对目标工作区。
|
读取 [references/environment-checks.md](references/environment-checks.md),按其中的命令和边界运行检查。脚本路径相对本 skill,不相对目标工作区。
|
||||||
|
|
||||||
- 有 Python 3.9+ 时使用 `scripts/inspect_environment.py`。默认只检查 PATH、受支持 CLI 的版本/帮助、目标文件存在性;输出 JSON,不写工作区。
|
- 有 Python 3.9+ 时使用 `scripts/inspect_environment.py`。默认检查 PATH、受支持 CLI 的版本/帮助、Herdr 内的 integration 状态、受限 Pi package 列表和目标文件存在性;输出 JSON,不写工作区。扫描做过的检查直接引用结果,不重复执行。
|
||||||
- 将宿主找到的 skill 路径用 `--skill NAME=PATH` 传给脚本。脚本只核验文件可读性,Step 1 的宿主判断仍由 Agent 负责。
|
- 将宿主找到的 skill 路径用 `--skill NAME=PATH` 传给脚本;Shepherdr 的 git/local、项目级或显式加载来源,可用 `--shepherdr-path PACKAGE_DIR` 提供确认的包根目录。脚本只检查文件,不加载扩展。
|
||||||
|
- 在 Herdr 内按其 skill 核验当前会话;结合宿主提供的工具来源,报告 Shepherdr 的安装线索与当前加载状态。不为检查启动 worker。
|
||||||
- 包含 `pi`、`claude`、`opencode`、`grok`;`codex`、`gemini` 只查 PATH。用户要求列表时,按参考说明显式开启 Pi/Grok/OpenCode 模型查询和 OpenCode agent 定义查询。其他宿主接口按需补充,没有接口或无法确认副作用时标记未查。
|
- 包含 `pi`、`claude`、`opencode`、`grok`;`codex`、`gemini` 只查 PATH。用户要求列表时,按参考说明显式开启 Pi/Grok/OpenCode 模型查询和 OpenCode agent 定义查询。其他宿主接口按需补充,没有接口或无法确认副作用时标记未查。
|
||||||
- 区分 agent 定义与运行实例,模型配置与 CLI 列表;这些都不证明真实调用成功。不推断额度,不发测试 prompt、不登录、不启动 worker。
|
- 区分 agent 定义与运行实例,模型配置与 CLI 列表;这些都不证明真实调用成功。不推断额度,不发测试 prompt、不登录、不启动 worker。
|
||||||
- 检查目标的 `AGENTS.md`、`CLAUDE.md` 桥、`CODINGRULES.md`、skills 连接、`*.code-workspace` 和 PATH 上的 `just`。新目录尚不存在时先扫描其现有父目录,并注明这不是目标目录现状。
|
- 检查目标的 `AGENTS.md`、`CLAUDE.md` 桥、`CODINGRULES.md`、skills 连接、`*.code-workspace` 和 PATH 上的 `just`。新目录尚不存在时先扫描其现有父目录,并注明这不是目标目录现状。
|
||||||
@@ -51,7 +51,7 @@ disable-model-invocation: true
|
|||||||
1. 目标目录、工作区名、维护者与用途。
|
1. 目标目录、工作区名、维护者与用途。
|
||||||
2. 源码仓库:已有路径还是待克隆、一个还是多个;待克隆需远端 URL。
|
2. 源码仓库:已有路径还是待克隆、一个还是多个;待克隆需远端 URL。
|
||||||
3. Quick Context:模块归属、维护的上游库、需要防止误判的事项。
|
3. Quick Context:模块归属、维护的上游库、需要防止误判的事项。
|
||||||
4. 是否采用 Herdr 并行协作(当前可用时建议采用,但不自动启用)。只读调研和评审不额外建 worktree,并行修改源码的 worker 用独立 worktree。
|
4. 是否采用 Herdr 并行协作(当前可用时建议采用,但不自动启用)。Pi 任务优先使用已配置的 Shepherdr,其他任务按 `$herdr` 交互;用户的工具选择优先。只读任务不额外建 worktree,并行修改用独立 worktree。
|
||||||
5. 是否生成 `CODINGRULES.md`(默认否),工作区是否 git 跟踪(默认否)。
|
5. 是否生成 `CODINGRULES.md`(默认否),工作区是否 git 跟踪(默认否)。
|
||||||
6. 是否引用用户指定的模型策略文件(默认不生成)。策略表达工具/模型/额度偏好,不把本次扫描清单复制进去。
|
6. 是否引用用户指定的模型策略文件(默认不生成)。策略表达工具/模型/额度偏好,不把本次扫描清单复制进去。
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ disable-model-invocation: true
|
|||||||
按 [references/environment-checks.md](references/environment-checks.md) 的 decisions 格式整理明确选择,用 `scripts/render_agents.py` 输出候选 `AGENTS.md` 到系统临时目录。它只做模板渲染,不读取扫描结果自行决策。
|
按 [references/environment-checks.md](references/environment-checks.md) 的 decisions 格式整理明确选择,用 `scripts/render_agents.py` 输出候选 `AGENTS.md` 到系统临时目录。它只做模板渲染,不读取扫描结果自行决策。
|
||||||
|
|
||||||
- `AGENTS.md`:使用 [assets/AGENTS.template.md](assets/AGENTS.template.md)。新文件核验后写入;已有文件逐节修改,不将完整候选重定向覆盖。重复补全先对照现有章节,避免重复规则。
|
- `AGENTS.md`:使用 [assets/AGENTS.template.md](assets/AGENTS.template.md)。新文件核验后写入;已有文件逐节修改,不将完整候选重定向覆盖。重复补全先对照现有章节,避免重复规则。
|
||||||
- 可选 Herdr 章节来自 [assets/sections/herdr.md](assets/sections/herdr.md),只有采用 Herdr 且可读其 skill 时加入。`think-twice` 用于正式任务书/交付;`why-here` 只在废弃尝试、纠正误解、完成迁移或替换实现后使用。
|
- 可选 Herdr 章节来自 [assets/sections/herdr.md](assets/sections/herdr.md),只有采用 Herdr 且可读其 skill 时加入,保留用户的工具选择。环境故障参考链接指向本次使用的 `init-workspace/SKILL.md`;渲染器填入其实际文件 URI,手工生成时同样填写可读地址。`think-twice` 用于正式任务书/交付;`why-here` 只在废弃尝试、纠正误解、完成迁移或替换实现后使用。
|
||||||
- 模型策略引用必须指向目标工作区实际可读的文件;在本次明确授权创建该文件时可以先生成候选,交付前检查引用。没有文件就省略,不生成空策略文件。采用个人外部策略时,在工作区文件中写清实际来源、读取条件和失效处理。
|
- 模型策略引用必须指向目标工作区实际可读的文件;在本次明确授权创建该文件时可以先生成候选,交付前检查引用。没有文件就省略,不生成空策略文件。采用个人外部策略时,在工作区文件中写清实际来源、读取条件和失效处理。
|
||||||
- `CLAUDE.md`:先尝试符号链接到 `AGENTS.md`;Windows 需要管理员权限或开发者模式。失败时写一行 `@AGENTS.md`。已有可用桥不动。
|
- `CLAUDE.md`:先尝试符号链接到 `AGENTS.md`;Windows 需要管理员权限或开发者模式。失败时写一行 `@AGENTS.md`。已有可用桥不动。
|
||||||
- `CODINGRULES.md`:用户选择生成时使用 [assets/CODINGRULES.template.md](assets/CODINGRULES.template.md),同时保留 AGENTS 指针;未选择时不删已有文件。
|
- `CODINGRULES.md`:用户选择生成时使用 [assets/CODINGRULES.template.md](assets/CODINGRULES.template.md),同时保留 AGENTS 指针;未选择时不删已有文件。
|
||||||
@@ -105,11 +105,12 @@ disable-model-invocation: true
|
|||||||
先确定验收范围:只检查时只验收检查报告;新建时验证本次全部生成资源;更新时验证本次修改及其必要依赖,不将范围外的既有问题变成本轮修复要求。以下文件检查只适用于对应范围。既有问题按更新策略登记;本轮必要依赖未验证时明确标为未验证或阻塞。
|
先确定验收范围:只检查时只验收检查报告;新建时验证本次全部生成资源;更新时验证本次修改及其必要依赖,不将范围外的既有问题变成本轮修复要求。以下文件检查只适用于对应范围。既有问题按更新策略登记;本轮必要依赖未验证时明确标为未验证或阻塞。
|
||||||
|
|
||||||
- [ ] 只检查模式没有创建工作区资源、启动 Agent、登录或发起模型推理
|
- [ ] 只检查模式没有创建工作区资源、启动 Agent、登录或发起模型推理
|
||||||
- [ ] 报告区分宿主可读/文件存在、agent 定义/运行实例、模型列出/调用验证
|
- [ ] 报告区分宿主可读/文件存在、agent 定义/运行实例、模型列出/调用验证;integration、扩展声明/文件/宿主加载/worker 可用分开,受限列表未发现不等于全机未安装
|
||||||
- [ ] 重复使用或已有规则时进入更新策略,仅询问缺口;环境暂时缺失不导致删除既有规则
|
- [ ] 重复使用或已有规则时进入更新策略,仅询问缺口;环境暂时缺失不导致删除既有规则
|
||||||
- [ ] 写入已有文件前得到确认;未知章节和无关用户内容保留,重复补全无重复章节;没有差异时不写文件
|
- [ ] 写入已有文件前得到确认;未知章节和无关用户内容保留,重复补全无重复章节;没有差异时不写文件
|
||||||
- [ ] 本次新增或修改的规则不重复,`AGENTS.md` 与 `CODINGRULES.md` 职责分明,commit 尾注规则放在前者
|
- [ ] 本次新增或修改的规则不重复,`AGENTS.md` 与 `CODINGRULES.md` 职责分明,commit 尾注规则放在前者
|
||||||
- [ ] 本轮涉及的 skill、文档与策略指针带触发条件和动作,且必要目标可读;Herdr 执行前会重新核验环境
|
- [ ] 本轮涉及的 skill、文档与策略指针带触发条件和动作,且必要目标可读,包括协作章节的环境故障参考链接
|
||||||
|
- [ ] 协作规则按任务选择回传方式,没有同时要求插件自动交付和手工重复回报;下发状态不明时不降级重发,迁移不重启或重发活动任务
|
||||||
- [ ] 本轮创建、修改或依赖的 skills 连接和 CLAUDE 桥有效,涉及的 code-workspace 是合法 JSON
|
- [ ] 本轮创建、修改或依赖的 skills 连接和 CLAUDE 桥有效,涉及的 code-workspace 是合法 JSON
|
||||||
- [ ] 新生成内容无占位符、模板注释或未替换示例,候选 `AGENTS.md` 不超过 200 行;更新不为清除既有超限而删除用户内容
|
- [ ] 新生成内容无占位符、模板注释或未替换示例,候选 `AGENTS.md` 不超过 200 行;更新不为清除既有超限而删除用户内容
|
||||||
- [ ] 输出做字段筛选及密钥模式屏蔽,说明未验证能力与未完成事项
|
- [ ] 输出做字段筛选及密钥模式屏蔽,说明未验证能力与未完成事项
|
||||||
|
|||||||
@@ -1,19 +1,26 @@
|
|||||||
## Herdr Collaboration
|
## Herdr Collaboration
|
||||||
- 本工作区采用 Herdr 管理并行 worker。操作前检查 `HERDR_ENV=1`,读取宿主可访问的 `$herdr`;宿主未列出时,在 Herdr 内运行 `herdr --skill` 并读取返回指令。核验 CLI 与当前会话,以当前 skill 和 CLI 帮助为准。不可用时串行推进或请用户进入 Herdr,不从外部操控会话或绕用其他后台方式。
|
|
||||||
- 主控负责拆分、共同接口、任务负责人、跟踪、评审、集成、验收与清理;worker 只完成明确授权的任务。主控自身的子任务可以串行委派。
|
### 共同协作要求
|
||||||
- 只读调研和评审不额外创建 worktree。并行修改源码的 worker 通过 Herdr 的 worktree 功能管理独立工作树与 workspace;建在实际源码仓,不在外层资料目录建树。
|
- 本工作区采用 Herdr 管理并行 worker,具体操作遵循 `$herdr`。遇到环境问题时,参考 [init-workspace 的环境检查]({{init_workspace}});需要重新扫描时,请用户以“只检查环境”调用该 skill。
|
||||||
- 每项任务有唯一负责人;同一文件同时只有一个写入负责人。共享契约、依赖、锁文件和公共配置由主控指定修改者。共同接口未定时先设计、核验,不为填满并发提前实现。
|
- 主控负责拆分、共同接口、唯一任务负责人、跟踪、评审、集成、验收与清理;worker 只完成明确授权的任务。遵守用户的工具选择和宿主委派限制,冲突时报告;主控不重复实现已委派范围。
|
||||||
- 发现上游错误或需要跨范围修改时,回报成因与所需改动,不在调用方吞错或堆兼容分支。
|
- 只读调研和评审不额外创建 worktree。并行修改源码的 worker 通过 Herdr 的 worktree 功能管理独立工作树与 workspace;建在实际源码仓,不在外层资料目录建树。同一文件同时只有一个写入负责人,共享契约、依赖、锁文件和公共配置由主控指定修改者。
|
||||||
- 任务书写明目标、仓库/分支/基线提交、共同接口、允许修改的文件、必读材料、依赖、验收命令及失败情形、停下报告的条件、提交授权和交付接收方。材料必须在 worker 的 cwd 或 worktree 中可读。
|
- 共同接口未定时先设计、核验,不为填满并发提前实现。发现上游错误或需要跨范围修改时,回报成因与所需改动,不在调用方吞错或堆兼容分支。
|
||||||
- 后台创建保持用户焦点;使用返回的 workspace/pane ID 和唯一 Agent 名称。启动后核验身份、模型、cwd、分支、必需 skill 的可访问性和可接收任务状态。只读任务也要明确使用的代码基线。
|
- 任务书写明目标、仓库/分支/基线提交、共同接口、允许修改的文件、必读材料、依赖、验收命令及失败情形、停下报告的条件、提交授权和交付接收方。共同要求通过可读引用传递;worker 的 cwd/worktree 中不可见的工作区规则提供可读副本或必要摘录。
|
||||||
- 每个任务独立使用测试数据、临时目录和端口;共享依赖明确使用约束。worktree 不隔离凭据或外部文件,不是安全沙箱。
|
- 每个任务独立使用测试数据、临时目录和端口,共享依赖明确使用约束。worktree 不隔离凭据或外部文件,不是安全沙箱。
|
||||||
- 双向消息以 `[主控]` 或 `[任务名]` 开头。完成或阻塞时按本节开头的方式获取并读取 Herdr 指令,通过 Herdr 向实际接收方主动回传一次:完成情况、提交(或未提交原因/不适用)、改动、实际检查与结果、限制、需要主控的动作。只在自己的终端打印不算交付。
|
- 交付报告包括完成情况、提交(或未提交原因/不适用)、改动、实际检查与结果、限制和需要主控的动作。`idle` / `done` 或自动完成通知只说明运行状态,不表示验收通过。
|
||||||
- worker 回传后,没有补充任务、修改要求或待澄清事项时,主控不发送“收到”等确认性消息,直接评审或收尾,避免触发无效模型回合。worker 等待明确指令,不主动续聊。修改要求、阻塞解除和任务取消仍须通知相关 Agent;主控仍向用户交付最终结果。
|
- worker 交付后,没有补充任务、修改要求或待澄清事项时,主控不发送“收到”等确认性消息,直接评审或收尾。worker 等待明确指令,不主动续聊;修改要求、阻塞解除和任务取消仍须通知相关 Agent,主控仍向用户交付最终结果。
|
||||||
- 回传不等待主控当前任务结束;发送失败时保留消息并报告,不连续重发或操纵对方按键。`idle` / `done` 只是运行状态,不表示验收通过。
|
|
||||||
|
### 任务交互
|
||||||
|
- Pi 任务优先使用已配置的 Shepherdr `agents`,按工具帮助下发任务、发送消息和读取结果。Shepherdr 任务由 worker 的最终答复交付,主控等待插件通知。
|
||||||
|
- 未使用 Shepherdr 的任务按 `$herdr` 交互;worker 完成或阻塞时,通过 Herdr 向实际主控主动回传一次交付报告。
|
||||||
|
- 任务是否接收不明时,先查原任务状态,不立即降级重发。
|
||||||
|
- worktree、布局、原始终端及其他 CLI agent 由 Herdr 管理。
|
||||||
|
|
||||||
|
### 验收与收尾
|
||||||
- worker 只在授权分支提交;主控按范围检查、独立评审、依赖集成、集成验证、验收的顺序推进。push、安装、发布另按项目授权执行。
|
- worker 只在授权分支提交;主控按范围检查、独立评审、依赖集成、集成验证、验收的顺序推进。push、安装、发布另按项目授权执行。
|
||||||
- 检查命令取自项目指令和实际配置;局部/全量测试、类型检查、真实模型、浏览器和宿主验证分别报告,不能相互替代。测试失败、实现不完整或关键条件未验证时保持未完成或阻塞,不用 skip 消除问题。
|
- 检查命令取自项目指令和实际配置;局部/全量测试、类型检查、真实模型、浏览器和宿主验证分别报告,不能相互替代。测试失败、实现不完整或关键条件未验证时保持未完成或阻塞,不用 skip 消除问题。
|
||||||
- 收尾包括源码集成、Herdr 资源和工作区文档三部分,不以关闭 worker 代替。主控按授权合并分支或应用提交,执行集成验证;Herdr workspace 是终端组织,不代替 Git 合并。
|
- 收尾包括源码集成、Herdr 资源和工作区文档三部分,不以关闭 worker 代替。主控按授权合并分支或应用提交,执行集成验证;Herdr workspace 是终端组织,不代替 Git 合并。
|
||||||
- worker 后续不再需要,且交付已回传、提交已保全、未提交文件已处理后,及时通过 Herdr 关闭本轮创建的 Agent/pane,释放资源,不为等待确认回复保留进程。仍需返工或澄清时可暂留,并记录原因。
|
- worker 后续不再需要,且交付已送达、提交已保全、未提交文件已处理后,及时按当前工具契约关闭本轮创建的 Agent/pane,不为等待确认回复保留进程。仍需返工或澄清时可暂留并记录原因。
|
||||||
- 核验成果已集成或另有可恢复副本、没有活动进程和待处理文件后,按授权移除本轮无用的 worktree、分支和空 Herdr workspace。仍需使用的终端按当前 CLI 支持的方式整理到保留的布局,移动后使用返回的新 ID;不为整齐搬动他人资源。关闭进程不授权删除未集成成果,无法证明可清理时保留并报告。
|
- 核验成果已集成或另有可恢复副本、没有活动进程和待处理文件后,按授权移除本轮无用的 worktree、分支和空 Herdr workspace。仍需使用的终端按当前 CLI 支持的方式整理到保留布局,移动后使用返回的新 ID,不搬动他人资源。关闭进程不授权删除未集成成果,无法证明可清理时保留并报告。
|
||||||
- 同步本轮相关的工作区文档:将有效结论、验证和后续动作写入对应文档,更新任务状态与目录地图,按项目约定及授权归档或删除已失效的临时计划。保留尚有效的决定、风险和阻塞,不借收尾清理无关材料。
|
- 同步本轮相关的工作区文档:写入有效结论、验证和后续动作,更新任务状态与目录地图,按项目约定及授权归档或删除失效临时计划。保留仍有效的决定、风险和阻塞,不借收尾清理无关材料。
|
||||||
- 只清理本轮创建且已无用途的资源;移除共享依赖链接与目录前先核验,不关闭他人的终端。剩余任务和暂留资源登记负责人、保留原因及下一步,恢复时重新查询状态和 ID。
|
- 只清理本轮创建且已无用途的资源;移除共享依赖链接与目录前先核验,不关闭他人的终端。剩余任务和暂留资源登记负责人、保留原因及下一步,恢复时重新查询状态和 ID。
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ python -B "${SKILL_DIR}/scripts/inspect_environment.py" --target /existing/works
|
|||||||
|
|
||||||
默认不保存文件。需要留档时,将 stdout 重定向到系统临时目录下本轮专用目录中的新文件;不要写进 skill 安装目录或直接覆盖工作区文件。所有脚本使用 Python 3.9+ 标准库,不安装依赖。Windows 上使用可用的 `python` / `py -3`,参数路径需加引号。
|
默认不保存文件。需要留档时,将 stdout 重定向到系统临时目录下本轮专用目录中的新文件;不要写进 skill 安装目录或直接覆盖工作区文件。所有脚本使用 Python 3.9+ 标准库,不安装依赖。Windows 上使用可用的 `python` / `py -3`,参数路径需加引号。
|
||||||
|
|
||||||
JSON 顶层字段:`schema_version`、`checked_at`、`target`、`herdr`、`skills`、`tools`、`workspace`、`limits`。
|
JSON 顶层字段:`schema_version`、`checked_at`、`target`、`herdr`、`skills`、`tools`、`shepherdr`、`workspace`、`limits`。
|
||||||
|
|
||||||
- 工具 `found` 只证明 PATH 可定位;`version.status=ok` 证明版本格式识别成功,不证明认证可用。
|
- 工具 `found` 只证明 PATH 可定位;`version.status=ok` 证明版本格式识别成功,不证明认证可用。
|
||||||
- Herdr `candidate` 只证明环境变量与 CLI help 通过。Agent 按 Herdr skill 查询当前会话后,才能另行报告会话可用。
|
- Herdr `candidate` 只证明环境变量与 CLI help 通过。Agent 按 Herdr skill 查询当前会话后,才能另行报告会话可用。
|
||||||
@@ -19,7 +19,32 @@ JSON 顶层字段:`schema_version`、`checked_at`、`target`、`herdr`、`skil
|
|||||||
- 模型 `listed` / `evidence=cli_listed` 不等于调用成功;`call_verified` 始终为 false。
|
- 模型 `listed` / `evidence=cli_listed` 不等于调用成功;`call_verified` 始终为 false。
|
||||||
- `not_checked` 不是空列表或不支持;`unrecognized_output` 不是“没有模型”。`timeout`、`command_failed`、`execution_failed` 和 `output_too_large` 分别保留。
|
- `not_checked` 不是空列表或不支持;`unrecognized_output` 不是“没有模型”。`timeout`、`command_failed`、`execution_failed` 和 `output_too_large` 分别保留。
|
||||||
|
|
||||||
单项失败仍输出其余检查,扫描进程返回 0;输入或脚本执行错误返回 2。成功退出不表示每个工具可用。每条命令默认超时 10 秒,可用 `--timeout` 调整到最多 60 秒。失败命令的输出丢弃;成功 help 的 stderr 也参与解析(OpenCode 将帮助写到该流),但原始文本不入报告,仅提取允许字段。CLI 子进程不是沙箱,工具自身可能写缓存或执行启动逻辑,不承诺系统零副作用。
|
单项失败仍输出其余检查,扫描进程返回 0;输入或脚本执行错误返回 2。成功退出不表示每个工具可用。每条命令默认超时 10 秒,可用 `--timeout` 调整到最多 60 秒。失败命令的输出丢弃;帮助的 stderr 也参与解析(OpenCode 和 Herdr 命令组会写到该流)。Herdr 的裸 `integration` 在退出码为 2 且输出以已知帮助标题开头时按帮助处理,其余非零退出仍是失败。原始文本不入报告,仅提取允许字段。CLI 子进程不是沙箱,工具自身可能写缓存或执行启动逻辑,不承诺系统零副作用。
|
||||||
|
|
||||||
|
## Herdr integration 与 Shepherdr
|
||||||
|
|
||||||
|
默认执行以下检查,不安装、不 reload、不启动 worker:
|
||||||
|
|
||||||
|
- `HERDR_ENV` 不为 `1` 时不运行任何 Herdr CLI;`herdr.pi_integration.status=not_checked`。在 Herdr 内检查 `--help`、`--version`,帮助包含 integration 时查询命令组,确认支持 status 后运行 `herdr integration status`,仅提取 Pi 状态,不输出其他 integration 的路径。
|
||||||
|
- `pi_integration` 区分 `installed`、`not_installed`、`outdated`、`unrecognized_output`、`unsupported_interface` 和命令失败;未识别的输出不推断安装状态。安装侧状态不证明当前会话已加载 integration。
|
||||||
|
- Pi 在 PATH 时运行 `pi list --no-approve`,设置 `PI_OFFLINE=1`、`PI_TELEMETRY=0`、`NO_COLOR=1`,关闭 stdin 并限制超时。只提取用户级 `npm:@howaboua/pi-shepherdr`(含版本)的声明、filtered 标记及安装路径;`filtered` 只说明存在资源筛选,不证明扩展已禁用。
|
||||||
|
- `shepherdr.packages.status` 为 `listed`、`not_found_in_scope` 或解析/命令错误;`items[].files` 另报候选文件状态。没有返回安装路径为 `path_not_reported`,不能当作文件已安装。`not_found_in_scope` 只说明本次用户级 npm 范围未发现声明,不排除项目、git/local、显式加载或未登记的副本。
|
||||||
|
- 候选包只读 `package.json`,核对准确 name、version 和 `pi.extensions` 中精确入口文件存在性;结果为 `files_present`、`missing_manifest`、`missing_entry`、`identity_mismatch`、`invalid_manifest`、`unreadable_or_invalid` 等。含通配符、越界或不支持的路径为 `unsupported_manifest`;精确入口不是文件(包括目录)为 `missing_entry`,交给宿主核验。脱敏路径为 `redacted_path`,不会拿来读文件。包存在不等于已加载。
|
||||||
|
- `shepherdr.node` 查询 Node 版本;结合 `tools` 的 Pi 版本、`herdr.version` 和实际插件版本说明核对前提,不内置永久版本白名单。Shepherdr 0.2.18 要求 Pi ≥ 1.1.0、Node ≥ 22.18、Herdr ≥ 0.9 及 Pi integration;其他版本重新查证。
|
||||||
|
|
||||||
|
宿主提供或用户指定的 git/local、项目级安装可传包根目录,重复参数检查多个明确候选,不扫描全盘:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -B "${SKILL_DIR}/scripts/inspect_environment.py" --target /existing/workspace --shepherdr-path /confirmed/package-root
|
||||||
|
```
|
||||||
|
|
||||||
|
这些文件结果在 `shepherdr.candidates`,不代表来源已配置或获得项目信任。脚本不 import 扩展、不执行其代码、不读取认证文件,也不自行实现 Pi 资源解析规则。
|
||||||
|
|
||||||
|
脚本的 `shepherdr.host_loaded` 与 `worker_loaded` 为 `not_checked`。Agent 根据当前宿主提供的工具来源补充加载证据;没有接口时标为未验证,不另开会话探测。worker 不在此次扫描范围内。Shepherdr profile 的模型和 thinking 独立配置,不继承主控。
|
||||||
|
|
||||||
|
Pi 1.1.0 的裸 `pi list` 可能在项目信任流程加载扩展;`--no-approve` 排除该路径和项目资源。旧版不接受参数或输出不同就报告失败,不自动改成裸命令或 `--approve`。首次加载 Shepherdr 会创建默认 profiles,因此不能为检查而新开加载插件的 Pi。受限查询仍进入 Pi CLI,不保证没有宿主缓存副作用。
|
||||||
|
|
||||||
|
缺少 integration 或扩展时报告缺口,只有用户另行授权才能安装。直接 Herdr 协作不要求安装 Shepherdr。
|
||||||
|
|
||||||
## CLI 与模型、agent 的检查
|
## CLI 与模型、agent 的检查
|
||||||
|
|
||||||
@@ -32,7 +57,7 @@ JSON 顶层字段:`schema_version`、`checked_at`、`target`、`herdr`、`skil
|
|||||||
| OpenCode | 显式 `--opencode-models` 查询 `opencode models --pure`,不传 `--refresh` | 显式 `--opencode-agents` 查询 `opencode agent list --pure`,只提取名称和类型 |
|
| OpenCode | 显式 `--opencode-models` 查询 `opencode models --pure`,不传 `--refresh` | 显式 `--opencode-agents` 查询 `opencode agent list --pure`,只提取名称和类型 |
|
||||||
| Grok Build | 显式 `--grok-models` 查询 `grok models` | `--agent` 是选择参数,不是列表接口;不读取配置全文猜定义 |
|
| Grok Build | 显式 `--grok-models` 查询 `grok models` | `--agent` 是选择参数,不是列表接口;不读取配置全文猜定义 |
|
||||||
|
|
||||||
按用户的列表查询要求显式开启对应选项;默认不查列表。Grok/OpenCode 查询可能访问目录服务或缓存,`--pure` 只排除 OpenCode 外部插件,不保证离线。命令不发推理 prompt、不登录、不启动 worker。已验证输出格式:Grok 1.0.13、OpenCode 1.15.13;格式无法识别时报告未识别,不猜列表。查询报错或超时就报告,不自动改成带认证、刷新或启动功能的命令。
|
按用户的模型或 agent 列表查询要求显式开启对应选项;默认不查这些列表。Grok/OpenCode 查询可能访问目录服务或缓存,`--pure` 只排除 OpenCode 外部插件,不保证离线。命令不发推理 prompt、不登录、不启动 worker。已验证输出格式:Grok 1.0.13、OpenCode 1.15.13;格式无法识别时报告未识别,不猜列表。查询报错或超时就报告,不自动改成带认证、刷新或启动功能的命令。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python -B "${SKILL_DIR}/scripts/inspect_environment.py" --target /existing/workspace --grok-models --opencode-models --opencode-agents --timeout 30
|
python -B "${SKILL_DIR}/scripts/inspect_environment.py" --target /existing/workspace --grok-models --opencode-models --opencode-agents --timeout 30
|
||||||
@@ -70,7 +95,7 @@ python -B "${SKILL_DIR}/scripts/inspect_environment.py" --target /existing/works
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`skills` 中的 true 表示 Agent 已实际读取并确认可访问,不是照抄文件存在性。`herdr` 表示用户选择采用该协作方式,独立于当前是否在 Herdr 中;启用时必须有可读 Herdr skill。`coding_rules` 表示候选需要该指针,同时确认目标文件会存在。
|
`skills` 中的 true 表示 Agent 已实际读取并确认可访问,不是照抄文件存在性。`herdr` 表示用户选择采用该协作方式,独立于当前是否在 Herdr 中;启用时必须有可读 Herdr skill。用户明确的工具偏好由 Agent 在候选中保留。`coding_rules` 表示候选需要该指针,同时确认目标文件会存在。
|
||||||
|
|
||||||
`model_policy` 为 null 或省略时不生成选型章节。非空时是工作区相对 `.md` 路径,例如 `docs/model-policy.md`;不接受绝对路径或 `..`。渲染器不验证目标存在,Agent 在交付前负责核验。策略内容必须来自用户,不由扫描推断额度、偏好或默认 provider。
|
`model_policy` 为 null 或省略时不生成选型章节。非空时是工作区相对 `.md` 路径,例如 `docs/model-policy.md`;不接受绝对路径或 `..`。渲染器不验证目标存在,Agent 在交付前负责核验。策略内容必须来自用户,不由扫描推断额度、偏好或默认 provider。
|
||||||
|
|
||||||
@@ -78,7 +103,7 @@ python -B "${SKILL_DIR}/scripts/inspect_environment.py" --target /existing/works
|
|||||||
python -B "${SKILL_DIR}/scripts/render_agents.py" --decisions /task-temp/decisions.json > /task-temp/AGENTS.candidate.md
|
python -B "${SKILL_DIR}/scripts/render_agents.py" --decisions /task-temp/decisions.json > /task-temp/AGENTS.candidate.md
|
||||||
```
|
```
|
||||||
|
|
||||||
stdout 是 UTF-8 Markdown,错误走 stderr。输出前校验字段、布尔值、占位符和 200 行上限。新工作区验证后保存;已有工作区对照候选逐节修改,保留未知章节,不用此命令覆盖原文件。无 Python 时,Agent 读取基础模板与可选章节,按同一决策填充并人工执行这些检查。
|
stdout 是 UTF-8 Markdown,错误走 stderr。Herdr 章节的环境故障链接自动指向本次执行的 skill 文件 URI;迁移到其他机器时,按目标机器可读的 skill 位置更新。输出前校验字段、布尔值、占位符和 200 行上限。新工作区验证后保存;已有工作区对照候选逐节修改,保留未知章节,不用此命令覆盖原文件。无 Python 时,Agent 读取基础模板与可选章节,按同一决策填充并人工执行这些检查。
|
||||||
|
|
||||||
## 脱敏
|
## 脱敏
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,15 @@
|
|||||||
- 模型列表是本轮事实,不自动写成永久白名单或额度策略。
|
- 模型列表是本轮事实,不自动写成永久白名单或额度策略。
|
||||||
- 整理工作区文档时,只处理本轮涉及的状态、结论、计划和目录地图;仍有用的决策与验证证据保留。归档/删除按工作区约定和用户授权处理,不扫描全仓顺手清理。
|
- 整理工作区文档时,只处理本轮涉及的状态、结论、计划和目录地图;仍有用的决策与验证证据保留。归档/删除按工作区约定和用户授权处理,不扫描全仓顺手清理。
|
||||||
|
|
||||||
|
### 更新 Herdr 协作规则时
|
||||||
|
|
||||||
|
对照当前协作模板逐项处理:
|
||||||
|
|
||||||
|
- 保留任务负责人、代码基线、独立 worktree、评审、验收和成果保全要求。用户的模型、布局、工具选择和禁用功能优先于模板的条件式“优先使用 Shepherdr”。
|
||||||
|
- 将全局“worker 经 Herdr 主动回传”限定到直接 Herdr 任务;Shepherdr 任务改为最终答复由插件交付,不再另发完成报告。交付内容要求不变,运行结束仍不等于验收通过。
|
||||||
|
- 删除被工具帮助覆盖的操作说明和例行环境自检要求;遇到环境问题时,通过模板中的链接参考 `init-workspace`。保留用户仍需的自定义约定。
|
||||||
|
- 优先让活动任务按原方式收尾。更新规则不授权重启、关闭或重发任务;确需接管时另行确认原任务状态、责任方和回传方式。
|
||||||
|
|
||||||
已有文件修改取得用户确认后再执行。没有差异时报告无需更新,到此结束,不写时间戳、空报告或状态文件来制造变化。
|
已有文件修改取得用户确认后再执行。没有差异时报告无需更新,到此结束,不写时间戳、空报告或状态文件来制造变化。
|
||||||
|
|
||||||
## 3. 应用并验证
|
## 3. 应用并验证
|
||||||
@@ -34,4 +43,4 @@
|
|||||||
|
|
||||||
范围外的既有超 200 行、失效引用或连接问题只登记,不自动扩修或强制开启精简访谈;需要处理时另提修改范围。若不可读文件是本次修改依赖的策略,或相关连接无法验证,将受影响部分标为未验证或阻塞,不能登记后仍宣称通过。新生成的候选仍遵守 200 行上限,不为满足模板限制删除既有用户内容。
|
范围外的既有超 200 行、失效引用或连接问题只登记,不自动扩修或强制开启精简访谈;需要处理时另提修改范围。若不可读文件是本次修改依赖的策略,或相关连接无法验证,将受影响部分标为未验证或阻塞,不能登记后仍宣称通过。新生成的候选仍遵守 200 行上限,不为满足模板限制删除既有用户内容。
|
||||||
|
|
||||||
再次按相同要求比较,应该没有需要重复添加的内容。交付实际改动、执行的检查和仍未完成的事项,不声称本轮未检查的整个工作区已健康。
|
再次按相同要求比较,应该没有需要重复添加的内容。涉及协作迁移时,检查同一任务不会同时受到“插件自动交付”和“额外手工回传”的要求;用户自定义章节未变,活动任务没有因规则更新被重新下发。交付实际改动、执行的检查和仍未完成的事项,不声称本轮未检查的整个工作区已健康。
|
||||||
|
|||||||
@@ -8,29 +8,34 @@ import sys
|
|||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
sys.dont_write_bytecode = True
|
sys.dont_write_bytecode = True
|
||||||
from output_safety import Parser, emit, entrypoint, redact
|
from output_safety import Parser, emit, entrypoint, read_json, redact
|
||||||
|
|
||||||
TOOLS = ("pi", "claude", "opencode", "grok", "codex", "gemini")
|
TOOLS = ("pi", "claude", "opencode", "grok", "codex", "gemini")
|
||||||
SKILLS = ("herdr", "think-twice", "why-here")
|
SKILLS = ("herdr", "think-twice", "why-here")
|
||||||
VERSION = re.compile(r"(?<![\w.-])\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?(?![\w.-])")
|
VERSION = re.compile(r"(?<![\w.-])\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?(?![\w.-])")
|
||||||
|
SHEPHERDR = "@howaboua/pi-shepherdr"
|
||||||
|
|
||||||
|
|
||||||
def run_query(executable, arguments, timeout, cwd, env=None):
|
def run_query(executable, arguments, timeout, cwd, env=None):
|
||||||
"""Only callers' fixed argument lists reach this boundary; stdin stays closed."""
|
"""Only callers' fixed argument lists reach this boundary; stdin stays closed."""
|
||||||
|
help_query = arguments in (["--help"], ["integration"])
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
[executable, *arguments], cwd=cwd, env=env,
|
[executable, *arguments], cwd=cwd, env=env,
|
||||||
stdin=subprocess.DEVNULL, stdout=subprocess.PIPE,
|
stdin=subprocess.DEVNULL, stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE if arguments == ["--help"] else subprocess.DEVNULL, timeout=timeout, check=False,
|
stderr=subprocess.PIPE if help_query else subprocess.DEVNULL, timeout=timeout, check=False,
|
||||||
encoding="utf-8", errors="replace",
|
encoding="utf-8", errors="replace",
|
||||||
)
|
)
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
return {"status": "timeout"}, ""
|
return {"status": "timeout"}, ""
|
||||||
except OSError:
|
except OSError:
|
||||||
return {"status": "execution_failed"}, ""
|
return {"status": "execution_failed"}, ""
|
||||||
if result.returncode:
|
text = result.stdout + (result.stderr or "") if help_query else result.stdout
|
||||||
|
# Herdr 0.9 prints bare group help on stderr with syntax exit 2.
|
||||||
|
group_help = (arguments == ["integration"] and result.returncode == 2
|
||||||
|
and redact(text).startswith("herdr integration commands:\n"))
|
||||||
|
if result.returncode and not group_help:
|
||||||
return {"status": "command_failed", "exit_code": result.returncode}, ""
|
return {"status": "command_failed", "exit_code": result.returncode}, ""
|
||||||
text = result.stdout + (result.stderr or "") if arguments == ["--help"] else result.stdout
|
|
||||||
if len(text) > 256_000:
|
if len(text) > 256_000:
|
||||||
return {"status": "output_too_large"}, ""
|
return {"status": "output_too_large"}, ""
|
||||||
return {"status": "ok"}, redact(text)
|
return {"status": "ok"}, redact(text)
|
||||||
@@ -128,20 +133,128 @@ def query_catalog(row, field, arguments, parser, scope, cwd, timeout):
|
|||||||
row[field]["scope"] = scope
|
row[field]["scope"] = scope
|
||||||
|
|
||||||
|
|
||||||
|
def query_version(executable, cwd, timeout, runner=run_query):
|
||||||
|
check, text = runner(executable, ["--version"], timeout, cwd)
|
||||||
|
if check["status"] != "ok":
|
||||||
|
return check
|
||||||
|
match = VERSION.search(re.sub(r"^v(?=\d)", "", text.strip()))
|
||||||
|
return {"status": "ok", "value": match.group()} if match else {"status": "unrecognized_output"}
|
||||||
|
|
||||||
|
|
||||||
|
def parse_pi_integration(text):
|
||||||
|
lines = [line.strip() for line in text.splitlines() if line.startswith("pi:")]
|
||||||
|
if len(lines) != 1:
|
||||||
|
return {"status": "unrecognized_output"}
|
||||||
|
match = re.fullmatch(r"pi: (not installed|installed|outdated)(?: \([^\r\n]+\))?", lines[0])
|
||||||
|
if not match:
|
||||||
|
return {"status": "unrecognized_output"}
|
||||||
|
return {"status": {"not installed": "not_installed", "installed": "installed", "outdated": "outdated"}[match[1]]}
|
||||||
|
|
||||||
|
|
||||||
def inspect_herdr(cwd, timeout, *, environ=None, which=shutil.which, runner=run_query):
|
def inspect_herdr(cwd, timeout, *, environ=None, which=shutil.which, runner=run_query):
|
||||||
environ = os.environ if environ is None else environ
|
environ = os.environ if environ is None else environ
|
||||||
inside = environ.get("HERDR_ENV") == "1"
|
inside = environ.get("HERDR_ENV") == "1"
|
||||||
executable = which("herdr")
|
executable = which("herdr")
|
||||||
row = {"in_session": inside, "cli_found": bool(executable), "status": "not_in_herdr"}
|
row = {"in_session": inside, "cli_found": bool(executable), "status": "not_in_herdr",
|
||||||
|
"version": {"status": "not_checked"}, "pi_integration": {"status": "not_checked"},
|
||||||
|
"session_verified": False}
|
||||||
if not inside:
|
if not inside:
|
||||||
return row # No Herdr CLI calls outside its managed environment.
|
return row # No Herdr CLI calls outside its managed environment.
|
||||||
if not executable:
|
if not executable:
|
||||||
row["status"] = "missing_cli"
|
row["status"] = "missing_cli"
|
||||||
return row
|
return row
|
||||||
check, _ = runner(executable, ["--help"], timeout, cwd)
|
check, help_text = runner(executable, ["--help"], timeout, cwd)
|
||||||
row["help"] = check
|
row["help"] = check
|
||||||
row["status"] = "candidate" if check["status"] == "ok" else "unavailable"
|
row["status"] = "candidate" if check["status"] == "ok" else "unavailable"
|
||||||
row["session_verified"] = False
|
row["version"] = query_version(executable, cwd, timeout, runner)
|
||||||
|
if check["status"] == "ok" and re.search(r"\bherdr integration\b", help_text):
|
||||||
|
# A bare command group is discovery; never probe install with missing arguments.
|
||||||
|
check, help_text = runner(executable, ["integration"], timeout, cwd)
|
||||||
|
if check["status"] != "ok":
|
||||||
|
row["pi_integration"] = check
|
||||||
|
elif re.search(r"\bherdr integration status\b", help_text):
|
||||||
|
check, text = runner(executable, ["integration", "status"], timeout, cwd)
|
||||||
|
row["pi_integration"] = parse_pi_integration(text) if check["status"] == "ok" else check
|
||||||
|
else:
|
||||||
|
row["pi_integration"] = {"status": "unsupported_interface"}
|
||||||
|
return row
|
||||||
|
|
||||||
|
|
||||||
|
def parse_shepherdr_packages(text):
|
||||||
|
"""Parse the restricted user list, not Pi's complete resource resolution policy."""
|
||||||
|
if text.strip() == "No packages installed.":
|
||||||
|
return {"status": "not_found_in_scope", "items": []}
|
||||||
|
lines = [line.rstrip() for line in text.splitlines() if line.strip()]
|
||||||
|
if not lines or lines.pop(0) != "User packages:":
|
||||||
|
return {"status": "unrecognized_output", "items": []}
|
||||||
|
items, current = [], None
|
||||||
|
for line in lines:
|
||||||
|
if re.fullmatch(r" \S.*", line):
|
||||||
|
source = line[2:]
|
||||||
|
filtered = source.endswith(" (filtered)")
|
||||||
|
if filtered:
|
||||||
|
source = source[:-11]
|
||||||
|
current = {"path_seen": False}
|
||||||
|
if re.fullmatch(r"npm:" + re.escape(SHEPHERDR) + r"(?:@[^\s]+)?", source):
|
||||||
|
current = {"source": source, "filtered": filtered, "path_seen": False}
|
||||||
|
items.append(current)
|
||||||
|
elif re.fullmatch(r" \S.*", line) and current is not None and not current["path_seen"]:
|
||||||
|
current["path_seen"] = True
|
||||||
|
current["path"] = line[4:]
|
||||||
|
else:
|
||||||
|
return {"status": "unrecognized_output", "items": []}
|
||||||
|
for item in items:
|
||||||
|
item.pop("path_seen")
|
||||||
|
return {"status": "listed" if items else "not_found_in_scope", "items": items}
|
||||||
|
|
||||||
|
|
||||||
|
def inspect_shepherdr_files(filename):
|
||||||
|
"""Read one candidate package only. Never load its code or follow manifest escapes."""
|
||||||
|
if "[REDACTED]" in str(filename):
|
||||||
|
return {"status": "redacted_path"}
|
||||||
|
try:
|
||||||
|
root = Path(filename).expanduser().resolve()
|
||||||
|
manifest = read_json(root / "package.json")
|
||||||
|
if not isinstance(manifest, dict) or manifest.get("name") != SHEPHERDR:
|
||||||
|
return {"status": "identity_mismatch"}
|
||||||
|
version = manifest.get("version")
|
||||||
|
if not isinstance(version, str) or not VERSION.fullmatch(version):
|
||||||
|
return {"status": "invalid_manifest"}
|
||||||
|
pi = manifest.get("pi")
|
||||||
|
entries = pi.get("extensions") if isinstance(pi, dict) else None
|
||||||
|
row = {"status": "files_present", "path": str(root), "version": version}
|
||||||
|
if not isinstance(entries, list) or not entries:
|
||||||
|
return {**row, "status": "unsupported_manifest"}
|
||||||
|
for entry in entries:
|
||||||
|
if not isinstance(entry, str) or not entry or any(c in entry for c in "*?![]{}\\:"):
|
||||||
|
return {**row, "status": "unsupported_manifest"}
|
||||||
|
target = (root / entry).resolve()
|
||||||
|
if root not in target.parents:
|
||||||
|
return {**row, "status": "unsupported_manifest"}
|
||||||
|
if not target.is_file():
|
||||||
|
return {**row, "status": "missing_entry"}
|
||||||
|
return row
|
||||||
|
except FileNotFoundError:
|
||||||
|
return {"status": "missing_manifest"}
|
||||||
|
except (OSError, ValueError, RuntimeError):
|
||||||
|
return {"status": "unreadable_or_invalid"}
|
||||||
|
|
||||||
|
|
||||||
|
def inspect_shepherdr(pi, paths, cwd, timeout, *, which=shutil.which, runner=run_query):
|
||||||
|
row = {"packages": {"status": "missing_cli", "items": []},
|
||||||
|
"scope": "User npm declaration only; project/git/local/explicit loads need host-supplied paths.",
|
||||||
|
"candidates": [], "host_loaded": "not_checked", "worker_loaded": "not_checked",
|
||||||
|
"node": {"status": "missing_cli"}}
|
||||||
|
node = which("node")
|
||||||
|
if node:
|
||||||
|
row["node"] = query_version(node, cwd, timeout, runner)
|
||||||
|
if pi["status"] == "found":
|
||||||
|
check, text = runner(pi["path"], ["list", "--no-approve"], timeout, cwd,
|
||||||
|
{**os.environ, "PI_OFFLINE": "1", "PI_TELEMETRY": "0", "NO_COLOR": "1"})
|
||||||
|
row["packages"] = parse_shepherdr_packages(text) if check["status"] == "ok" else {**check, "items": []}
|
||||||
|
for item in row["packages"]["items"]:
|
||||||
|
item["files"] = inspect_shepherdr_files(item["path"]) if "path" in item else {"status": "path_not_reported"}
|
||||||
|
row["candidates"] = [inspect_shepherdr_files(path) for path in paths]
|
||||||
return row
|
return row
|
||||||
|
|
||||||
|
|
||||||
@@ -172,6 +285,7 @@ def inspect(args):
|
|||||||
skills = inspect_skills(args.skill)
|
skills = inspect_skills(args.skill)
|
||||||
tools = [inspect_cli(name, cwd, args.timeout) for name in TOOLS]
|
tools = [inspect_cli(name, cwd, args.timeout) for name in TOOLS]
|
||||||
by_name = {row["name"]: row for row in tools}
|
by_name = {row["name"]: row for row in tools}
|
||||||
|
shepherdr = inspect_shepherdr(by_name["pi"], args.shepherdr_path, cwd, args.timeout)
|
||||||
if args.pi_models:
|
if args.pi_models:
|
||||||
flags = ["--offline", "--no-extensions", "--no-skills", "--no-prompt-templates",
|
flags = ["--offline", "--no-extensions", "--no-skills", "--no-prompt-templates",
|
||||||
"--no-themes", "--no-context-files", "--no-approve", "--no-session", "--list-models"]
|
"--no-themes", "--no-context-files", "--no-approve", "--no-session", "--list-models"]
|
||||||
@@ -188,7 +302,7 @@ def inspect(args):
|
|||||||
query_catalog(by_name["opencode"], "agent_definitions", ["agent", "list", "--pure"], parse_opencode_agents,
|
query_catalog(by_name["opencode"], "agent_definitions", ["agent", "list", "--pure"], parse_opencode_agents,
|
||||||
"OpenCode definitions with external plugins disabled; names/modes only, not running instances or launch verification.", cwd, args.timeout)
|
"OpenCode definitions with external plugins disabled; names/modes only, not running instances or launch verification.", cwd, args.timeout)
|
||||||
return {"schema_version": 1, "checked_at": datetime.now(timezone.utc).isoformat(),
|
return {"schema_version": 1, "checked_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"target": cwd, "herdr": herdr, "skills": skills, "tools": tools,
|
"target": cwd, "herdr": herdr, "skills": skills, "tools": tools, "shepherdr": shepherdr,
|
||||||
"workspace": {name: os.path.lexists(Path(cwd) / name) for name in
|
"workspace": {name: os.path.lexists(Path(cwd) / name) for name in
|
||||||
("AGENTS.md", "CLAUDE.md", "CODINGRULES.md", ".agents/skills", ".claude/skills")},
|
("AGENTS.md", "CLAUDE.md", "CODINGRULES.md", ".agents/skills", ".claude/skills")},
|
||||||
"limits": ["No model inference or credential validation performed.",
|
"limits": ["No model inference or credential validation performed.",
|
||||||
@@ -202,6 +316,8 @@ def main():
|
|||||||
parser.add_argument("--target", default=".", help="Existing directory to inspect (new workspace: inspect parent).")
|
parser.add_argument("--target", default=".", help="Existing directory to inspect (new workspace: inspect parent).")
|
||||||
parser.add_argument("--skill", action="append", default=[], metavar="NAME=PATH",
|
parser.add_argument("--skill", action="append", default=[], metavar="NAME=PATH",
|
||||||
help="Host-supplied skill candidate, not proof of host accessibility.")
|
help="Host-supplied skill candidate, not proof of host accessibility.")
|
||||||
|
parser.add_argument("--shepherdr-path", action="append", default=[], metavar="PACKAGE_DIR",
|
||||||
|
help="Host-supplied package root; reads manifest/entry presence without loading code.")
|
||||||
parser.add_argument("--timeout", type=float, default=10)
|
parser.add_argument("--timeout", type=float, default=10)
|
||||||
parser.add_argument("--pi-models", action="store_true", help="Opt into a restricted pi catalog query; not inference.")
|
parser.add_argument("--pi-models", action="store_true", help="Opt into a restricted pi catalog query; not inference.")
|
||||||
parser.add_argument("--grok-models", action="store_true", help="Opt into grok models; may access catalog services/cache, no inference.")
|
parser.add_argument("--grok-models", action="store_true", help="Opt into grok models; may access catalog services/cache, no inference.")
|
||||||
|
|||||||
@@ -54,7 +54,10 @@ def render(config):
|
|||||||
if skills.get("why-here"):
|
if skills.get("why-here"):
|
||||||
quality.append("- 工作涉及废弃尝试、纠正误解、完成迁移或替换实现时,交付前读取并使用 `$why-here` 检查遗留内容;检查本身不授权清理代码。")
|
quality.append("- 工作涉及废弃尝试、纠正误解、完成迁移或替换实现时,交付前读取并使用 `$why-here` 检查遗留内容;检查本身不授权清理代码。")
|
||||||
slots["quality"] = "\n".join(quality)
|
slots["quality"] = "\n".join(quality)
|
||||||
slots["collaboration"] = (ASSETS / "sections/herdr.md").read_text(encoding="utf-8") if config.get("herdr") else ""
|
slots["collaboration"] = ""
|
||||||
|
if config.get("herdr"):
|
||||||
|
slots["collaboration"] = (ASSETS / "sections/herdr.md").read_text(encoding="utf-8").replace(
|
||||||
|
"{{init_workspace}}", (ASSETS.parent / "SKILL.md").resolve().as_uri())
|
||||||
slots["coding"] = ("## Coding Rules\n- 实现、修复或 review 代码前,完整读取 `CODINGRULES.md` 并遵循;宿主已加载时不重复读取。纯文档改动不适用。\n"
|
slots["coding"] = ("## Coding Rules\n- 实现、修复或 review 代码前,完整读取 `CODINGRULES.md` 并遵循;宿主已加载时不重复读取。纯文档改动不适用。\n"
|
||||||
if config.get("coding_rules") else "")
|
if config.get("coding_rules") else "")
|
||||||
slots["models"] = ""
|
slots["models"] = ""
|
||||||
|
|||||||
Reference in New Issue
Block a user