diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e4e6f16..5899684 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,3 +44,7 @@ jobs: run: test -f dist/packages/cli/index.js - name: Smoke test run: node dist/packages/cli/index.js --help + - name: CLI check (hello-world) + run: node dist/packages/cli/index.js check -r examples/hello-world + - name: CLI check (order-service) + run: node dist/packages/cli/index.js check -r examples/order-service diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ab35d72..a77ab6d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -7,7 +7,6 @@ on: jobs: publish: runs-on: ubuntu-latest - environment: npm permissions: contents: read id-token: write diff --git a/.gitignore b/.gitignore index b57e876..0102602 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ !.prettierrc !.node-version !README.md +!CONTRIBUTING.md +!CODE_OF_CONDUCT.md !LICENSE !tsconfig.json !package.json diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..5f65f00 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,83 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at . All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of actions. + +**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..c02fac1 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,156 @@ +# Contributing to SVP Forge + +感谢你对 SVP Forge 的关注!以下指南帮助你高效参与贡献。 + +## Code of Conduct + +请阅读并遵守我们的 [Code of Conduct](./CODE_OF_CONDUCT.md)。 + +## 如何贡献 + +### 报告 Bug + +1. 先搜索 [已有 Issues](https://github.com/SemanticVoxelProtocol/forge/issues) 确认没有重复 +2. 使用 Bug Report 模板创建 Issue +3. 提供:复现步骤、期望行为、实际行为、环境信息(Node 版本、OS) + +### 提议新功能 + +1. 创建 Feature Request Issue 描述需求 +2. 说明:要解决的问题、建议的方案、可能的替代方案 +3. 非平凡的 API 变更建议先在 Issue 中讨论,达成共识后再开发 + +### 提交代码 + +欢迎通过 Pull Request 贡献代码,包括 bug 修复、新功能、文档改进和测试补充。 + +## 开发环境 + +### 前置要求 + +- Node.js >= 22(见 `.node-version`) +- npm + +### 安装与构建 + +```bash +# 克隆仓库 +git clone https://github.com/SemanticVoxelProtocol/forge.git +cd forge + +# 安装依赖 +npm install + +# 构建 +npm run build + +# 运行测试 +npm test + +# 完整检查(TypeScript + ESLint + Prettier) +npm run check +``` + +## 项目结构 + +``` +packages/ + cli/ CLI 命令(forge check, init, prompt, ...) + core/ 核心逻辑(check, hash, store, view, i18n) + skills/ AI 工具适配器和 prompt 生成 +examples/ 示例项目(hello-world, order-service, ...) +tests/e2e/ 端到端测试 +docs/ 文档 +``` + +## Pull Request 流程 + +### 分支策略 + +- `main` — 稳定分支,所有发布从此分支打 tag +- `dev` — 开发分支,日常开发合入此分支 +- 功能分支从 `dev` 创建,命名:`feat/描述`、`fix/描述`、`docs/描述` + +### PR 步骤 + +1. Fork 仓库,从 `dev` 创建功能分支 +2. 每个 PR 只解决一个问题(不要混合多个无关改动) +3. 为新功能和 bug 修复添加测试 +4. 本地运行完整检查: + ```bash + npm run check # tsc --noEmit + eslint + prettier --check + npm test # vitest + ``` +5. PR 标题遵循 Conventional Commits 格式(见下方) +6. 关联 Issue:`Fixes #123` 或 `Closes #123` +7. 开启 "Allow edits from maintainers" + +### PR 检查清单 + +- [ ] 代码通过 `npm run check` +- [ ] 测试通过 `npm test` +- [ ] 新功能/bug 修复包含测试 +- [ ] 文档已更新(如涉及用户可见变更) +- [ ] PR 标题符合 Conventional Commits + +## Commit 规范 + +遵循 [Conventional Commits v1.0.0](https://www.conventionalcommits.org/en/v1.0.0/)。 + +### 格式 + +``` +[optional scope]: + +[optional body] + +[optional footer(s)] +``` + +### 类型 + +| 类型 | 说明 | 示例 | +|------|------|------| +| `feat` | 新功能 | `feat(cli): add view command` | +| `fix` | Bug 修复 | `fix(core): handle empty hash input` | +| `docs` | 仅文档 | `docs: update tutorial` | +| `style` | 格式调整,无逻辑变化 | `style: fix indentation` | +| `refactor` | 重构,非 feat/fix | `refactor(store): simplify read logic` | +| `perf` | 性能优化 | `perf(hash): cache computed values` | +| `test` | 测试相关 | `test(check): add edge case coverage` | +| `build` | 构建系统 | `build: update tsconfig target` | +| `ci` | CI 配置 | `ci: add Node 24 to matrix` | +| `chore` | 杂项维护 | `chore: update dependencies` | + +### 规则 + +- 使用祈使句现在时:`add feature` 而非 `added` 或 `adds` +- 首字母小写,末尾不加句号 +- 标题行不超过 72 字符 +- 破坏性变更在类型后加 `!`:`feat!: remove deprecated API` + +## 编码规范 + +- **TypeScript** — 所有源码必须有类型标注 +- **ESLint** — `npm run lint`(配置见 `eslint.config.ts`) +- **Prettier** — `npm run format`(提交前自动格式化) +- **测试** — 使用 Vitest,bug 修复和新功能必须附带测试 +- 提交前运行 `npm run check` 确保一切正常 + +## 发布流程 + +> 此部分仅面向维护者。 + +发布通过 CI 自动完成: + +1. 确保 `main` 分支 CI 全绿 +2. 更新版本号:`npm version patch|minor|major` +3. 推送 tag:`git push --follow-tags` +4. CI 自动通过 npm Trusted Publishing 发布到 npm + +## 需要帮助? + +- 浏览标记为 [`good first issue`](https://github.com/SemanticVoxelProtocol/forge/labels/good%20first%20issue) 的 Issue +- 在 Issue 或 Discussion 中提问 + +再次感谢你的贡献! diff --git a/README.md b/README.md index 08de2f8..71066d9 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ L1 Code 最终实现 SVP 不自己调 AI API,不造编译器。SVP 是 AI 编码工具(Claude Code、Cursor、Windsurf、Kimi Code、Codex、GitHub Copilot)的**增强层**: -- **工具链**:`svp check`(校验)、store(读写)、hash(变更追踪) +- **工具链**:`forge check`(校验)、store(读写)、hash(变更追踪) - **Skills**:基于五层数据模型生成结构化 context,喂给用户已有的 AI 工具 类似 [OpenSpec](https://github.com/Fission-AI/OpenSpec) 的定位——不造 AI,给 AI 喂更好的上下文。SVP 的能力随 base model 进化自动提升。 @@ -73,7 +73,7 @@ npm run check # tsc + eslint + prettier - [设计理由](docs/design-rationale.md) — 为什么这么设计,每个决策的推理过程 - [代码风格](docs/code-style.md) — 开发规范(命名、测试、git、依赖、版本) - [交互架构](docs/interaction.md) — 逐层渗透模型、虚拟文件树、聚焦视图、编译计划 -- [check 错误码](docs/check-reference.md) — svp check 的所有错误/警告及修复建议 +- [check 错误码](docs/check-reference.md) — forge check 的所有错误/警告及修复建议 ## 目录 @@ -81,7 +81,7 @@ npm run check # tsc + eslint + prettier packages/ ├── core/ 五层数据模型的 TypeScript 类型定义 + 核心函数 ├── skills/ Prompt 生成器(design-l3、compile、recompile 等) -└── cli/ CLI 入口(svp 命令) +└── cli/ CLI 入口(forge 命令) docs/ 设计文档 + 开发规范 examples/ 示例项目(hello-world、order-service) diff --git a/docs/architecture.md b/docs/architecture.md index bab5374..9f66273 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -89,7 +89,7 @@ interface L2CodeBlock { 分界原则:**层间连接关系结构化,block 内部是黑盒。** -结构化(`svp check` 校验): +结构化(`forge check` 校验): - pins 的类型、wire 的引用、层间 block/flow 引用 自然语言(展示给人看就行): @@ -125,7 +125,7 @@ L5 改了 intent 自下而上(仅检测,用户决策): L1 被手改 - → L2 的 contentHash 和 L1 实际哈希不匹配 → svp check 报 CONTENT_DRIFT + → L2 的 contentHash 和 L1 实际哈希不匹配 → forge check 报 CONTENT_DRIFT → 用户决定:接受改动 / 回退 / 更新上层契约 ``` @@ -141,7 +141,7 @@ L4/L3 的可视化编辑器。用节点图(nodes + wires)来编辑 L4 flow svp-blueprint 不是一个"层"——它是数据模型的一种人看面(编辑视图)。未来可以有其他编辑器(CLI、Web IDE、VS Code 插件),底下操作的是同一份数据。 -### svp check +### forge check 校验层间连接关系: - pin 类型匹配 @@ -162,9 +162,9 @@ SVP 不自己调 AI API,而是生成结构化的 context(skills)喂给用 SVP 不锁定任何 AI 提供商。今天用 Claude Code,明天用别的,SVP 数据模型不变。 -这个定位类似 [OpenSpec](https://github.com/Fission-AI/OpenSpec)——不造 AI,给 AI 喂更好的上下文。SVP 的差异在于:五层结构化的契约框架 + `svp check` 的形式化校验。 +这个定位类似 [OpenSpec](https://github.com/Fission-AI/OpenSpec)——不造 AI,给 AI 喂更好的上下文。SVP 的差异在于:五层结构化的契约框架 + `forge check` 的形式化校验。 -Skills 的具体实现形式是 SVP CLI 的虚拟文件树(`svp view`)和编译计划(`svp compile-plan`),详见 [交互架构](interaction.md)。 +Skills 的具体实现形式是 SVP CLI 的虚拟文件树(`forge view`)和编译计划(`forge compile-plan`),详见 [交互架构](interaction.md)。 ### SVP 交互架构 diff --git a/docs/check-reference.md b/docs/check-reference.md index bccde8e..08024f8 100644 --- a/docs/check-reference.md +++ b/docs/check-reference.md @@ -1,6 +1,6 @@ -# svp check 错误码参考 +# forge check 错误码参考 -`svp check` 校验 `.svp/` 目录下所有层的数据一致性。以下是所有可能的错误和警告。 +`forge check` 校验 `.svp/` 目录下所有层的数据一致性。以下是所有可能的错误和警告。 ## 错误(Error) diff --git a/docs/compilation.md b/docs/compilation.md index b19b392..767d323 100644 --- a/docs/compilation.md +++ b/docs/compilation.md @@ -19,7 +19,7 @@ L3Block ──→ AI 编码工具 + SVP skills ──→ L2CodeBlock + L1 源文 SVP 不自己调 AI API。契约盒(validate + constraints + description)作为结构化 context 喂给 AI 工具,AI 工具负责生成代码。 -> **注意区分两种"编译"**:本文档描述的是**格式转换编译**(YAML 节点图 → L3/L4 JSON),是确定性的、不需要 AI 的。变更驱动的**重编译计划**(某层改了 → 计算哪些下层需要重新生成)见 [交互架构](interaction.md) 的 `svp compile-plan` 章节。 +> **注意区分两种"编译"**:本文档描述的是**格式转换编译**(YAML 节点图 → L3/L4 JSON),是确定性的、不需要 AI 的。变更驱动的**重编译计划**(某层改了 → 计算哪些下层需要重新生成)见 [交互架构](interaction.md) 的 `forge compile-plan` 章节。 ## 映射规则 @@ -195,7 +195,7 @@ interface OrderRequest { { name: "request", type: "OrderRequest" } ``` -类型本身不被编译成独立的数据结构——它们就是 TypeScript,被 `svp check` 用来做连线类型匹配。 +类型本身不被编译成独立的数据结构——它们就是 TypeScript,被 `forge check` 用来做连线类型匹配。 ## 编译缓存 diff --git a/docs/design-rationale.md b/docs/design-rationale.md index 3f527e3..c340546 100644 --- a/docs/design-rationale.md +++ b/docs/design-rationale.md @@ -172,11 +172,11 @@ TypeScript interface 在这里的角色不是"实现语言",而是**对 AI 最 ### 结构化的边界:拓扑骨架 vs 黑盒内部 -"AI 不需要结构化"不意味着什么都不结构化。SVP 的工具链(`svp check`、渲染器)需要程序化处理一部分数据。 +"AI 不需要结构化"不意味着什么都不结构化。SVP 的工具链(`forge check`、渲染器)需要程序化处理一部分数据。 区分标准很简单:**层间的连接关系必须结构化,block 内部可以是黑盒。** -需要结构化(`svp check` 要校验): +需要结构化(`forge check` 要校验): - **pins 的类型** — 连线两端类型是否兼容 - **wire 的引用** — from/to 指向的 pin 是否存在 - **层间引用** — L4 step 引用的 L3 block 是否存在、签名是否匹配 @@ -201,7 +201,7 @@ SVP 协议(语言无关) └── 变更传播的机制 SVP 工具链(某种语言实现) - ├── 校验器(svp check) + ├── 校验器(forge check) ├── 编辑器(svp-blueprint 等) ├── Skills(给 AI 编码工具的结构化 context) └── Store(.svp/ 数据读写) diff --git a/docs/design.md b/docs/design.md index c16b0c7..d683de0 100644 --- a/docs/design.md +++ b/docs/design.md @@ -193,7 +193,7 @@ wires: ## 蓝图查看器 -`svp blueprint` 命令是 svp-blueprint 的只读可视化入口——读取 `.svp/` 下已编译的 L3/L4 JSON,生成自包含 HTML 节点图在浏览器中查看。 +`forge blueprint` 命令是 svp-blueprint 的只读可视化入口——读取 `.svp/` 下已编译的 L3/L4 JSON,生成自包含 HTML 节点图在浏览器中查看。 设计要点: @@ -204,7 +204,7 @@ wires: - **三种 L4 变体**:Flow(主要,节点图)、EventGraph(事件处理器列表)、StateMachine(状态节点 + 转换边) - **零依赖自包含**:单个 HTML 文件内联所有 CSS/JS/数据,无需服务器,离线可用 -查看器不做编辑——编辑仍然通过 YAML 文件 + `svp compile-blueprint` 完成。查看器是数据的一种只读渲染视图。 +查看器不做编辑——编辑仍然通过 YAML 文件 + `forge compile-blueprint` 完成。查看器是数据的一种只读渲染视图。 ## 不做什么 diff --git a/docs/interaction.md b/docs/interaction.md index bc03e70..53279af 100644 --- a/docs/interaction.md +++ b/docs/interaction.md @@ -13,16 +13,16 @@ AI 编码工具(Claude Code、Cursor 等)天然围绕文件树 + 文件操 ## 虚拟文件树 -`svp view` 命令把 `.svp/` 下的 JSON 数据实时渲染成 AI 友好的视图。不是真实文件,是按需计算的。 +`forge view` 命令把 `.svp/` 下的 JSON 数据实时渲染成 AI 友好的视图。不是真实文件,是按需计算的。 ``` -svp view l5 # L5 overview -svp view l4 # 所有 flow 的 overview -svp view l4/create-order # 某个 flow 的 detail -svp view l3 # 所有 block 的 overview -svp view l3/validate-order # 某个 block 的 detail -svp view l2 # 所有 code block 的 overview -svp view l1 # 源文件树(映射到真实文件系统) +forge view l5 # L5 overview +forge view l4 # 所有 flow 的 overview +forge view l4/create-order # 某个 flow 的 detail +forge view l3 # 所有 block 的 overview +forge view l3/validate-order # 某个 block 的 detail +forge view l2 # 所有 code block 的 overview +forge view l1 # 源文件树(映射到真实文件系统) ``` ### Overview 视图 @@ -30,7 +30,7 @@ svp view l1 # 源文件树(映射到真实文件系统) 每个实体在 overview 里只占一行——名字 + 签名 + 摘要统计。AI 看到全局拓扑,不被细节淹没。 ``` -# svp view l3 +# forge view l3 L3 Logic Blocks (7 blocks) ────────────────────────── @@ -46,7 +46,7 @@ reserve-inventory (Reservation) → ReserveConfirmation ``` ``` -# svp view l4 +# forge view l4 L4 Logic Chains (2 flows) ───────────────────────── @@ -62,7 +62,7 @@ cancel-order POST /api/orders/:id/cancel 展示该层的完整信息,但不自动展开其他层。用 `↑` `↓` 标注关联层,AI 知道往哪切换但不自动加载。 ``` -# svp view l3/validate-order +# forge view l3/validate-order validate-order ══════════════ @@ -93,7 +93,7 @@ description: ``` ``` -# svp view l4/create-order +# forge view l4/create-order create-order ════════════ @@ -155,9 +155,9 @@ dataFlows: 用户(在 L4):"create-order 流程里,validate 之后加一个限流步骤" 主 Agent(L4 层,上下文极少): - 1. svp view l4/create-order ← 看当前流程 - 2. svp edit l4/create-order ← 在 validate-order 后加 rate-limit 步骤 - 3. svp compile-plan ← SVP 计算变更范围 + 1. forge view l4/create-order ← 看当前流程 + 2. forge edit l4/create-order ← 在 validate-order 后加 rate-limit 步骤 + 3. forge compile-plan ← SVP 计算变更范围 SVP 输出编译计划: - [新建] L3/rate-limit ← 需要定义契约盒 @@ -179,9 +179,9 @@ SVP 输出编译计划: 用户(在 L3):"validate-order 的 email 改成可选" 主 Agent(L3 层): - 1. svp view l3/validate-order ← 看当前契约 - 2. svp edit l3/validate-order ← 删掉 email 的 required - 3. svp compile-plan ← SVP 计算变更范围 + 1. forge view l3/validate-order ← 看当前契约 + 2. forge edit l3/validate-order ← 删掉 email 的 required + 3. forge compile-plan ← SVP 计算变更范围 SVP 输出编译计划: - [更新] L2/validate-order ← sourceHash 不匹配,需要重编译 @@ -202,11 +202,11 @@ SVP 检测到 L1 变化: - L2/validate-order 的 contentHash 和 L1 实际哈希不匹配 - 标记 drift(对账警告) -用户跑 svp check: +用户跑 forge check: WARNING [SOURCE_DRIFT] l2/validate-order: contentHash 不匹配,L1 被手动修改 用户决定: - a. 接受 L1 的改动 → svp accept l2/validate-order → 更新 contentHash + a. 接受 L1 的改动 → forge accept l2/validate-order → 更新 contentHash b. 回退 L1 → 从 L3 重新编译覆盖 c. 同时更新 L3 契约 → 手动修改后重新编译 ``` @@ -245,21 +245,21 @@ L1(代码层): ~200+ 行/block ← 最重,但隔离在 subagent 里 SVP 逐层渗透: 用户说"加个限流" → 主 Agent 只看 L4 拓扑(20 行)→ subagent 各自处理 上下文:每个 agent 最多几十行 - 风险:每层都有 svp check 校验,问题逐层可见 + 风险:每层都有 forge check 校验,问题逐层可见 ``` --- -## 编译计划:svp compile-plan +## 编译计划:forge compile-plan -用户或 AI 修改了某层数据后,`svp compile-plan` 计算需要重编译的范围。 +用户或 AI 修改了某层数据后,`forge compile-plan` 计算需要重编译的范围。 输入:当前层的变更(哪些实体的 contentHash 变了)。 输出:结构化的任务清单,每个任务是一个独立的编译单元。 ``` -# svp compile-plan 的输出示例 +# forge compile-plan 的输出示例 Compile Plan (3 tasks) ────────────────────── @@ -288,12 +288,12 @@ AI 编码工具根据这个计划派发 subagent。无依赖的任务可以并 --- -## 与 svp check 的关系 +## 与 forge check 的关系 -`svp check` 是编译后的验收工具。 +`forge check` 是编译后的验收工具。 ``` -用户改 L4 → AI 逐层编译 → svp check +用户改 L4 → AI 逐层编译 → forge check │ ┌────────────┼────────────┐ ▼ ▼ ▼ @@ -306,7 +306,7 @@ AI 编码工具根据这个计划派发 subagent。无依赖的任务可以并 check 报什么层有问题,用户就去那层看。这是逐层渗透模型的**闭环**: ``` -用户改上层 → AI 向下编译 → svp check 校验 → 有问题回到对应层修 → 再次向下编译 +用户改上层 → AI 向下编译 → forge check 校验 → 有问题回到对应层修 → 再次向下编译 ``` --- diff --git a/docs/node-spec.md b/docs/node-spec.md index bdff352..584d024 100644 --- a/docs/node-spec.md +++ b/docs/node-spec.md @@ -27,7 +27,7 @@ AI 是编译器,不是 parser。`"request.items: array, min 1, max 50"` 这种 ### 结构化的边界 -层间连接关系(pins 类型、wire 引用)需要结构化,`svp check` 要校验。block 内部(validate、constraints、description)是黑盒,工具只需展示,不需解析。 +层间连接关系(pins 类型、wire 引用)需要结构化,`forge check` 要校验。block 内部(validate、constraints、description)是黑盒,工具只需展示,不需解析。 --- @@ -312,7 +312,7 @@ graphs/ ### 加载行为 -- `svp prompt compile/recompile/review` 时自动加载并注入 prompt +- `forge prompt compile/recompile/review` 时自动加载并注入 prompt - subagent 只拉自己需要的 docs,不加载全量 - 不影响 `contentHash` 计算——docs 是编译辅助信息,不是契约的一部分 diff --git a/docs/overview.md b/docs/overview.md index 757f2a6..0b92800 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -175,9 +175,9 @@ SVP 的工具链分两面:面向 AI 的和面向人的。 SVP 的数据模型本身就是为 AI 优化的——TypeScript 类型定义、自然语言契约、结构化的层间引用。AI 编码工具直接消费这些数据。 -- **`svp view`**(虚拟文件树):把 `.svp/` 下的 JSON 数据实时渲染成 AI 友好的聚焦视图。AI 在某一层看到该层全貌,按需切换层级。 -- **`svp compile-plan`**(编译计划):检测变更范围,输出结构化的任务清单——哪些实体需要重编译、每个任务需要的最小上下文、任务间的依赖关系。AI 编码工具根据计划派发 subagent。 -- **`svp check`**(校验):编译后的自动验收——hash 一致性、引用完整性、漂移检测、图结构。AI 跑 check 就知道自己做对了没有。 +- **`forge view`**(虚拟文件树):把 `.svp/` 下的 JSON 数据实时渲染成 AI 友好的聚焦视图。AI 在某一层看到该层全貌,按需切换层级。 +- **`forge compile-plan`**(编译计划):检测变更范围,输出结构化的任务清单——哪些实体需要重编译、每个任务需要的最小上下文、任务间的依赖关系。AI 编码工具根据计划派发 subagent。 +- **`forge check`**(校验):编译后的自动验收——hash 一致性、引用完整性、漂移检测、图结构。AI 跑 check 就知道自己做对了没有。 - **store**(数据读写):读写 `.svp/` 下的五层数据。 - **hash**(变更追踪):计算和比对 contentHash。 @@ -185,10 +185,10 @@ SVP 的数据模型本身就是为 AI 优化的——TypeScript 类型定义、 SVP 的数据是为 AI 对齐的(TypeScript 对象、JSON 持久化)。人看 JSON 不直观。所以 SVP 提供**查看器**——把 AI 对齐的数据渲染成人类对齐的视图。 -查看器**只读不写**。编辑是 AI 的事(通过 `svp view` + `svp edit` 操作数据)。查看器只是让人能看懂 AI 在操作什么。 +查看器**只读不写**。编辑是 AI 的事(通过 `forge view` + `forge edit` 操作数据)。查看器只是让人能看懂 AI 在操作什么。 - **svp-blueprint**(蓝图查看器):L4/L3 的可视化节点图。把 flow 的步骤编排和 block 之间的数据流渲染成节点和连线,让人一眼看到全局拓扑。人在这个视图里理解系统结构,但不直接在图上编辑——要改的话,在对应层级告诉 AI 去改。 -- **CLI 查看器**:`svp status` 展示各层的健康状态(哪些 hash 匹配、哪些有 drift)。文本界面,适合快速检查。 +- **CLI 查看器**:`forge status` 展示各层的健康状态(哪些 hash 匹配、哪些有 drift)。文本界面,适合快速检查。 - **未来可以有更多查看器**:Web dashboard、IDE 插件、移动端概览。底下读的是同一份数据,只是渲染方式不同。 查看器的价值是**让人能观测**。SVP 的核心理念是透明——你得看得见才能判断对不对。查看器就是五个观测窗口的具体实现。 @@ -212,7 +212,7 @@ SVP 不让 AI 自由浏览五层数据。SVP 给 AI **聚焦的视野**——在 为什么?研究表明 LLM 不擅长处理太多上下文。更少但更精准的上下文反而准确率更高。SVP 在每一层只展示该层的信息,AI 集中注意力在一个抽象层级上工作。 -`svp view` 命令提供两级视图: +`forge view` 命令提供两级视图: - **Overview**:一个实体一行——名字、签名、摘要统计。AI 看到全局拓扑。 - **Detail**:展示本层完整信息,但不展开其他层。用导航标记指向关联层,AI 按需切换。 @@ -260,7 +260,7 @@ SVP 不让 AI 自由浏览五层数据。SVP 给 AI **聚焦的视野**——在 ### 编译计划 -用户修改某层后,`svp compile-plan` 计算需要重编译的范围,输出结构化的任务清单。每个任务标注操作类型、原因、需要的最小上下文、依赖关系。AI 编码工具根据这个计划派发 subagent,无依赖的任务并行执行。 +用户修改某层后,`forge compile-plan` 计算需要重编译的范围,输出结构化的任务清单。每个任务标注操作类型、原因、需要的最小上下文、依赖关系。AI 编码工具根据这个计划派发 subagent,无依赖的任务并行执行。 越高层介入,编译深度越深,消耗越大。越低层介入,越精确,越便宜。用户自然会学会在合适的层级操作。 @@ -274,7 +274,7 @@ SVP 不让 AI 自由浏览五层数据。SVP 给 AI **聚焦的视野**——在 **自下而上(对账检测)**:下层被手改 → 下层的 contentHash 和上层记录的不匹配 → 标记 drift 警告。这只是检测机制,不自动修复。用户看到 drift 后自己决定怎么处理。 -`svp check` 是编译后的验收工具,校验四类问题: +`forge check` 是编译后的验收工具,校验四类问题: 1. **Hash 一致性**——contentHash 和实际内容是否匹配 2. **引用完整性**——层间引用的实体是否存在,pin 类型是否兼容 @@ -284,7 +284,7 @@ SVP 不让 AI 自由浏览五层数据。SVP 给 AI **聚焦的视野**——在 check 报什么层有问题,用户就去那层看。这是逐层渗透模型的闭环: ``` -用户改上层 → AI 向下编译 → svp check 校验 → 有问题回到对应层修 → 再次向下编译 +用户改上层 → AI 向下编译 → forge check 校验 → 有问题回到对应层修 → 再次向下编译 ``` --- diff --git a/docs/phase2-plan.md b/docs/phase2-plan.md index 27ae6e9..ea3f52b 100644 --- a/docs/phase2-plan.md +++ b/docs/phase2-plan.md @@ -16,7 +16,7 @@ SVP 核心基础设施已完成(208 tests passing): - **哈希**: `computeHash`, `hashL3`, `hashL4`, `hashL5`, `hashL2` (`packages/core/hash.ts`) - **初始化**: `init()` — 创建 `.svp/` + L5 (`packages/core/init.ts`) - **编译器**: YAML 节点图 → L3/L4 JSON(确定性格式转换)(`packages/compiler/`) -- **CLI**: `svp init/check/compile/compile-plan/view/compile-blueprint` (`packages/cli/`) +- **CLI**: `forge init/check/compile/compile-plan/view/compile-blueprint` (`packages/cli/`) ### 跑不通的环节 @@ -28,7 +28,7 @@ SVP 核心基础设施已完成(208 tests passing): ### 本阶段目标 -让用户在 Claude Code 中通过意图驱动的 slash commands 与 SVP 全层协作。人只做两件事:**看**(svp view / svp check)和**说**(自然语言指令)。AI 负责 L5 到 L1 所有层的创建、编辑和编译。 +让用户在 Claude Code 中通过意图驱动的 slash commands 与 SVP 全层协作。人只做两件事:**看**(forge view / forge check)和**说**(自然语言指令)。AI 负责 L5 到 L1 所有层的创建、编辑和编译。 --- @@ -40,7 +40,7 @@ SVP 不调 AI API,不造编译器。SVP 是 AI 编码工具的**增强层**— ### 2. 透明优先于正确(overview.md §二) -SVP 不追求 AI 不犯错,追求错误可见、可定位、可修复。五层是五个观测窗口。`svp check` 是闭环验收工具。每层完成后展示给用户确认。 +SVP 不追求 AI 不犯错,追求错误可见、可定位、可修复。五层是五个观测窗口。`forge check` 是闭环验收工具。每层完成后展示给用户确认。 ### 3. 逐层渗透模型(interaction.md §逐层渗透) @@ -179,7 +179,7 @@ packages/skills/ **问题**:AI 创建/编辑 L5、L4、L3 的 JSON 时,contentHash 和 revision 需要正确计算。让 AI 在 prompt 中算 SHA-256 不可靠。 -**方案**:`svp rehash` — AI 写完 JSON 后运行,自动修正 hash 和 revision。 +**方案**:`forge rehash` — AI 写完 JSON 后运行,自动修正 hash 和 revision。 ```typescript // packages/skills/rehash.ts — 纯函数,不做 IO @@ -217,11 +217,11 @@ export function rehashL2(cb: L2CodeBlock): L2CodeBlock { ... } **CLI 命令** `packages/cli/commands/rehash.ts`: ```bash -svp rehash # 重算所有层 -svp rehash l5 # 只重算 L5 -svp rehash l4 # 重算所有 L4 -svp rehash l3/validate-order # 重算指定 L3 -svp rehash l2 # 重算所有 L2 +forge rehash # 重算所有层 +forge rehash l5 # 只重算 L5 +forge rehash l4 # 重算所有 L4 +forge rehash l3/validate-order # 重算指定 L3 +forge rehash l2 # 重算所有 L2 ``` 实现: @@ -274,8 +274,8 @@ export function createL2Link(options: LinkOptions): L2CodeBlock { **CLI 命令** `packages/cli/commands/link.ts`: ```bash -svp link --files src/validate-order.ts src/validate-order.test.ts -svp link validate-order --files src/validate-order.ts --language typescript +forge link --files src/validate-order.ts src/validate-order.test.ts +forge link validate-order --files src/validate-order.ts --language typescript ``` 实现: @@ -338,7 +338,7 @@ Prompt 内容指导 AI: - 从用户描述中提取:intent(核心问题+解决方案+成功标准)、constraints(功能/非功能/业务约束)、domains(领域+依赖关系)、integrations(外部系统+类型) - 输出 `.svp/l5.json`,格式符合 `L5Blueprint` schema - 保持简洁(~10行有效信息),只描述"做什么"不描述"怎么做" -- 运行 `svp rehash l5` 修正 hash +- 运行 `forge rehash l5` 修正 hash - **嵌入 JSON schema 示例**(让 AI 知道怎么写): ```json @@ -370,7 +370,7 @@ Prompt 内容: - 定义 steps(每个 step 有 id、action、blockRef)、dataFlows(from "stepId.pinName" → to "stepId.pinName")、trigger - **每个 process step 必须有 blockRef**(指向 L3 block id,可以是尚不存在的) - step.action 类型:process(引用 L3 block)、call(引用 L4 子 flow)、parallel(分支+branches)、wait(汇聚+waitFor) -- 运行 `svp rehash l4` 修正 hash +- 运行 `forge rehash l4` 修正 hash - **嵌入 L4Flow JSON schema 示例** #### `prompts/design-l3.ts` @@ -395,7 +395,7 @@ Prompt 内容: - validate 用自然语言:`"request.items": "array, min 1, max 50"` - constraints 用自然语言断言:`"output.result.errors contains all failed checks, not just first"` - type 引用 TypeScript interface 名称(来自项目 `types/` 目录) -- 运行 `svp rehash l3/` 修正 hash +- 运行 `forge rehash l3/` 修正 hash - **嵌入 L3Block JSON schema 示例** ### 5. 编译 Prompt 模板(L2、L1) @@ -409,11 +409,11 @@ Prompt 内容: 2. 读取 L4 流上下文(已通过 `resolved.l4` 提供) 3. 读取相关类型定义(如果 types/ 目录存在) 4. 生成 L1 源代码:函数签名匹配 L3 pins,实现满足 validate + constraints,内部逻辑参考 description -5. 运行 `svp link --files ` 创建 L2 映射 +5. 运行 `forge link --files ` 创建 L2 映射 #### `prompts/recompile.ts` — 重编译 -在 compile 基础上额外提供旧 L1 代码(`resolved.l1Files`)和 L2 映射。指导保留不变的逻辑,只修改受影响部分。运行 `svp link` 更新 L2。 +在 compile 基础上额外提供旧 L1 代码(`resolved.l1Files`)和 L2 映射。指导保留不变的逻辑,只修改受影响部分。运行 `forge link` 更新 L2。 #### `prompts/review.ts` — 漂移审查 @@ -458,7 +458,7 @@ export const claudeCodeAdapter: CodeCLIAdapter = { ### 7. 意图驱动的 Slash Commands -`svp init --host claude-code` 生成到 `.claude/commands/`。每个是 markdown 文件,内含完整工作流指导。 +`forge init --host claude-code` 生成到 `.claude/commands/`。每个是 markdown 文件,内含完整工作流指导。 | 命令文件 | 用户意图 | AI 做什么 | |---|---|---| @@ -466,10 +466,10 @@ export const claudeCodeAdapter: CodeCLIAdapter = { | `svp-add.md` | "加个 Z 步骤" | 编辑 L4 → 设计 L3 → 编译 L2+L1 | | `svp-change.md` | "把 email 改成可选" | 定位层级 → 修改 → 向下重编译 | | `svp-fix.md` | "修复 check 问题" | 读 check 报告 → 逐个修复 | -| `svp-check.md` | "检查一下" | `svp check` → 结构化报告 | -| `svp-view.md` | "看一下 L3" | `svp view $ARGUMENTS` | +| `svp-check.md` | "检查一下" | `forge check` → 结构化报告 | +| `svp-view.md` | "看一下 L3" | `forge view $ARGUMENTS` | -#### `/svp-build` — 全量设计+编译(最重要的命令) +#### `/forge-build` — 全量设计+编译(最重要的命令) 核心 prompt 结构: @@ -482,42 +482,42 @@ L3 设计和 L2+L1 编译通过 Agent 工具派发 subagent 完成。 你绝不直接读写 L1 源代码——那是 subagent 的事。 ## Step 1: 设计 L5 Blueprint(主 Agent 直接做,~10 行) -- 运行 `svp view l5` 查看当前 blueprint +- 运行 `forge view l5` 查看当前 blueprint - 根据用户描述,编辑 .svp/l5.json(嵌入 L5Blueprint JSON schema 示例) -- 运行 `svp rehash l5` 修正 hash -- 展示 `svp view l5` 给用户确认 +- 运行 `forge rehash l5` 修正 hash +- 展示 `forge view l5` 给用户确认 ## Step 2: 设计 L4 Flows(主 Agent 直接做,~20 行/flow) - 基于 L5 domains,为每个核心流程设计 L4 Flow - 写入 .svp/l4/.json(嵌入 L4Flow JSON schema 示例) - 注意:扇出用 parallel step,汇聚用 wait step -- 运行 `svp rehash l4` -- 展示 `svp view l4` 给用户确认 +- 运行 `forge rehash l4` +- 展示 `forge view l4` 给用户确认 ## Step 3: 设计 L3 Contracts(派发 subagent,每个 ~50 行) 对每个 L4 step 引用的 blockRef: - 用 Agent 工具派发 subagent,prompt 包含: - - 该 step 在 L4 中的位置(用 `svp view l4/` 输出) + - 该 step 在 L4 中的位置(用 `forge view l4/` 输出) - 上游/下游 block 的 pin 信息(如果已创建) - 用户意图描述 - L3Block JSON schema 示例 - subagent 创建 .svp/l3/.json(pins + validate + constraints + description) -- subagent 运行 `svp rehash l3/` +- subagent 运行 `forge rehash l3/` - **无依赖的 block 并行派发** -- 展示 `svp view l3` 给用户确认 +- 展示 `forge view l3` 给用户确认 ## Step 4: 编译 L2+L1(派发 subagent,每个 ~200 行) -- 运行 `svp compile-plan` 获取任务列表(每个任务带 context refs) +- 运行 `forge compile-plan` 获取任务列表(每个任务带 context refs) - 对每个 compile 任务派发 subagent,prompt 只包含: - - 该任务的 L3 契约(`svp view l3/`) + - 该任务的 L3 契约(`forge view l3/`) - 相关类型定义(types/ 目录) - 任务描述和输出要求 -- subagent 生成 L1 源代码 + 运行 `svp link --files ` 创建 L2 +- subagent 生成 L1 源代码 + 运行 `forge link --files ` 创建 L2 - **无依赖的任务并行派发** - 主 Agent 只看完成摘要,不读代码 ## Step 5: 验证 -- 运行 `svp check` 验证一致性 +- 运行 `forge check` 验证一致性 - 如有问题,定位到对应层修复 - 重复直到 check 通过 @@ -526,42 +526,42 @@ L3 设计和 L2+L1 编译通过 Agent 工具派发 subagent 完成。 - 主 Agent 不读 L1 代码——上下文隔离是核心价值 - 每层完成后展示给用户确认 - 做不到就报错,说清哪层什么问题——用户是反向反馈回路 -- 用 `svp rehash` 处理 hash,用 `svp link` 创建 L2 +- 用 `forge rehash` 处理 hash,用 `forge link` 创建 L2 - JSON 中 contentHash 和 revision 写占位值,rehash 会修正 - 尽量并行派发无依赖的 subagent ``` -#### `/svp-change` — 修改需求 +#### `/forge-change` — 修改需求 ``` 你是 SVP 编译器。定位受影响层级,从该层向下重编译。 -Step 1: 定位层级(主 Agent 运行 svp view l5 + svp view l4) +Step 1: 定位层级(主 Agent 运行 forge view l5 + forge view l4) 系统意图变了 → L5 | 流程编排变了 → L4 | 契约规则变了 → L3 | 代码变了 → L1(只报 drift) 越低层介入越精确越便宜 Step 2: 执行修改(L5/L4 主 Agent 做,L3 派发 subagent) - 运行 svp rehash 更新 hash → 展示给用户确认 + 运行 forge rehash 更新 hash → 展示给用户确认 Step 3: 向下重编译 - 运行 svp compile-plan → 按 /svp-build Step 3/4 模式派发 subagent + 运行 forge compile-plan → 按 /forge-build Step 3/4 模式派发 subagent 只处理受影响的实体 ``` -#### `/svp-add` — 添加步骤 +#### `/forge-add` — 添加步骤 ``` 定位 flow → 编辑 L4 加新 step → 设计新 L3 → 编译新 L2+L1 -同 /svp-build Step 2-5,但只处理新增的 block +同 /forge-build Step 2-5,但只处理新增的 block ``` -#### `/svp-fix` — 修复 check 问题 +#### `/forge-fix` — 修复 check 问题 ``` -运行 svp check --json → 按 issue code 分类修复: - HASH_MISMATCH → svp rehash +运行 forge check --json → 按 issue code 分类修复: + HASH_MISMATCH → forge rehash MISSING_BLOCK_REF → 创建 L3 或修改 L4 - SOURCE_DRIFT → svp compile-plan 重编译 + SOURCE_DRIFT → forge compile-plan 重编译 CONTENT_DRIFT → 展示给用户决定 ORPHAN_STEP / NEXT_CYCLE → 展示给用户修复 ``` @@ -575,7 +575,7 @@ export function generateClaudeMdSection(projectName: string): string 生成内容包括: - 五层模型说明 + 层间关系图 - `.svp/` 目录结构 -- 可用命令列表(svp view/check/compile-plan/rehash/link/compile) +- 可用命令列表(forge view/check/compile-plan/rehash/link/compile) - 逐层渗透规则(三条规则) - 上下文隔离原则(主 Agent 不读 L1) - JSON 编辑规则(contentHash 写占位值,rehash 修正) @@ -591,18 +591,18 @@ export interface SlashCommandTemplate { export function generateSlashCommands(): readonly SlashCommandTemplate[] ``` -### 10. 增强 `svp init --host` +### 10. 增强 `forge init --host` 修改 `packages/core/init.ts` — `InitOptions` 加 `host?: "claude-code"` 修改 `packages/cli/commands/init.ts` — `--host` 选项 当 `--host claude-code` 时额外生成: -1. `.claude/commands/svp-build.md` -2. `.claude/commands/svp-add.md` -3. `.claude/commands/svp-change.md` -4. `.claude/commands/svp-fix.md` -5. `.claude/commands/svp-check.md` -6. `.claude/commands/svp-view.md` +1. `.claude/commands/forge-build.md` +2. `.claude/commands/forge-add.md` +3. `.claude/commands/forge-change.md` +4. `.claude/commands/forge-fix.md` +5. `.claude/commands/forge-check.md` +6. `.claude/commands/forge-view.md` 7. 追加 SVP section 到 `CLAUDE.md`(已存在则追加,否则创建) --- @@ -628,13 +628,13 @@ export function generateSlashCommands(): readonly SlashCommandTemplate[] 9. 实现 `adapters/claude-code.ts` + 测试 10. 实现 `templates/slash-commands.ts`(6 个命令模板) 11. 实现 `templates/claude-md.ts` -12. 增强 `svp init --host claude-code`(修改 init.ts + init CLI) +12. 增强 `forge init --host claude-code`(修改 init.ts + init CLI) ### Phase D: 验证 13. 单元测试全覆盖(rehash, link, prompt-builder, adapter) -14. 集成测试(svp rehash/link CLI, svp init --host) -15. 端到端:init → /svp-build → check 全绿 +14. 集成测试(forge rehash/link CLI, forge init --host) +15. 端到端:init → /forge-build → check 全绿 16. 回归:`npm test`(208+ tests 通过)+ `npm run check`(tsc + eslint + prettier) --- @@ -665,8 +665,8 @@ export function generateSlashCommands(): readonly SlashCommandTemplate[] | `packages/skills/templates/slash-commands.ts` | 6 个 slash command 模板 | | `packages/skills/templates/claude-md.ts` | CLAUDE.md SVP section | | `packages/skills/adapters/claude-code.ts` | CodeCLIAdapter 实现 | -| `packages/cli/commands/rehash.ts` | svp rehash CLI 命令 | -| `packages/cli/commands/link.ts` | svp link CLI 命令 | +| `packages/cli/commands/rehash.ts` | forge rehash CLI 命令 | +| `packages/cli/commands/link.ts` | forge link CLI 命令 | | `packages/skills/__tests__/rehash.test.ts` | rehash 测试 | | `packages/skills/__tests__/link.test.ts` | link 测试 | | `packages/skills/__tests__/prompt-builder.test.ts` | prompt-builder 测试 | @@ -694,14 +694,14 @@ export function generateSlashCommands(): readonly SlashCommandTemplate[] - **adapter**: `claudeCodeAdapter.createSkillRegistry()` 返回 4 个 skill(compile/recompile/review/update-ref),每个 execute() 返回 status="needs-review" + prompt in notes ### 集成测试 -- `svp rehash`:写入错误 hash → rehash → `svp check` 无 HASH_MISMATCH -- `svp link`:有 L3 无 L2 → link → `svp check` 无 MISSING_L2 -- `svp init --host claude-code`:检查 .claude/commands/ 有 6 个 md 文件 + CLAUDE.md 有 SVP section +- `forge rehash`:写入错误 hash → rehash → `forge check` 无 HASH_MISMATCH +- `forge link`:有 L3 无 L2 → link → `forge check` 无 MISSING_L2 +- `forge init --host claude-code`:检查 .claude/commands/ 有 6 个 md 文件 + CLAUDE.md 有 SVP section ### 端到端 -1. `svp init --name hello --host claude-code` -2. 在 Claude Code 中 `/svp-build 一个简单的 hello world HTTP 服务` -3. `svp check` → 全绿 +1. `forge init --name hello --host claude-code` +2. 在 Claude Code 中 `/forge-build 一个简单的 hello world HTTP 服务` +3. `forge check` → 全绿 ### 回归 - `npm test` — 所有 208+ 现有测试通过 diff --git a/docs/project-introduction.md b/docs/project-introduction.md index 03d7f2f..b2de25b 100644 --- a/docs/project-introduction.md +++ b/docs/project-introduction.md @@ -240,7 +240,7 @@ L1 就是文件系统中的实际源代码。SVP 不直接管理 L1,而是通 ## 五、核心引擎:check、compile-plan、hash -### 5.1 svp check — 层间一致性校验 +### 5.1 forge check — 层间一致性校验 `packages/core/check.ts` 实现了 SVP 最关键的功能——跨层一致性校验。check 是一个纯函数,接收所有层的数据,输出问题报告: @@ -275,7 +275,7 @@ function check(input: CheckInput, language?: string): CheckReport 每个问题都有 severity(error/warning)、layer、entityId、code、message。错误信息通过 i18n 系统支持英文和中文。 -### 5.2 svp compile-plan — 智能任务规划 +### 5.2 forge compile-plan — 智能任务规划 `packages/core/compile-plan.ts` 基于 check 的漂移检测,自动生成结构化的编译任务清单: @@ -303,93 +303,93 @@ compile-plan 还做去重——同一个 target 可能被多个检测器命中 SVP 的变更追踪基于内容哈希。每个层的数据对象(去掉 `contentHash`、`revision`、`sourceHash` 等元数据字段)计算出一个哈希值。对象先 JSON 序列化(key 排序确保确定性),然后用 SHA-256 截取前 16 位十六进制。 -`packages/core/hash.ts` 提供了 `computeHash`、`hashL3`、`hashL4`、`hashL5`、`hashL2` 等函数。rehash 后 `svp check` 就能通过 hash 一致性检查。 +`packages/core/hash.ts` 提供了 `computeHash`、`hashL3`、`hashL4`、`hashL5`、`hashL2` 等函数。rehash 后 `forge check` 就能通过 hash 一致性检查。 --- -## 六、CLI 工具链:svp 命令 +## 六、CLI 工具链:forge 命令 `packages/cli/index.ts` 使用 Commander.js 构建了一个完整的 CLI,包含 9 个顶层命令: -### 6.1 svp init +### 6.1 forge init 初始化 `.svp/` 目录结构 + 写入初始 L5 blueprint: ```bash -svp init --name my-app --intent "做什么" --host +forge init --name my-app --intent "做什么" --host ``` - 创建 `.svp/l2/`、`.svp/l3/`、`.svp/l4/` 目录 - 生成初始 L5 blueprint(自动检测系统语言) - 如果指定 `--host `(支持 `claude-code`、`cursor`、`windsurf`、`kimi-code`、`codex`、`github-copilot`),会在对应工具目录生成 skill 文件并追加上下文配置 -### 6.2 svp check +### 6.2 forge check 运行跨层一致性校验: ```bash -svp check --json +forge check --json ``` 加载所有层数据,调用 `check()` 纯函数,输出问题报告。支持 `--json` 结构化输出。 -### 6.3 svp view +### 6.3 forge view 查看层数据的人类友好文本视图: ```bash -svp view l5 # L5 蓝图概览 -svp view l4 # 所有 L4 流程概览 -svp view l4 order-flow # L4 流程详情 -svp view l3 # 所有 L3 块概览 -svp view l3 greet # L3 块详情 -svp view l2 # 所有 L2 代码块 +forge view l5 # L5 蓝图概览 +forge view l4 # 所有 L4 流程概览 +forge view l4 order-flow # L4 流程详情 +forge view l3 # 所有 L3 块概览 +forge view l3 greet # L3 块详情 +forge view l2 # 所有 L2 代码块 ``` Overview 模式把每个实体压缩成一行,Detail 模式展示完整信息。视图由 `packages/core/view.ts` 的纯函数渲染,支持 i18n。 -### 6.4 svp compile-plan +### 6.4 forge compile-plan 生成编译任务清单: ```bash -svp compile-plan --json +forge compile-plan --json ``` 输出哪些 L3 block 需要编译、重编译、审查或修复引用。 -### 6.5 svp prompt +### 6.5 forge prompt 生成上下文感知的 AI 提示词——这是 SVP 与 AI 工具交互的核心接口。包含 7 个子命令: **任务类(操作已有数据):** ```bash -svp prompt compile # L3 → L1 首次编译 -svp prompt recompile # L3 变更后重编译 -svp prompt review # L1 手改后审查漂移 -svp prompt update-ref # 修复断裂的 L4 引用 +forge prompt compile # L3 → L1 首次编译 +forge prompt recompile # L3 变更后重编译 +forge prompt review # L1 手改后审查漂移 +forge prompt update-ref # 修复断裂的 L4 引用 ``` **设计类(创建新层数据):** ```bash -svp prompt design-l5 --intent "描述系统意图" -svp prompt design-l4 --intent "描述流程" --kind flow|event-graph|state-machine -svp prompt design-l3 --flow --step --intent "描述单元" +forge prompt design-l5 --intent "描述系统意图" +forge prompt design-l4 --intent "描述流程" --kind flow|event-graph|state-machine +forge prompt design-l3 --flow --step --intent "描述单元" ``` 每个 prompt 子命令的流程是:加载 `.svp/` 状态 → 构造合成 CompileTask → resolve 上下文 → buildPrompt 构建结构化提示 → renderPrompt 渲染为 markdown → stdout 输出。 -### 6.6 svp rehash +### 6.6 forge rehash 重算哈希值: ```bash -svp rehash l3 # 重算所有 L3 的 contentHash -svp rehash l3/greet # 重算指定 L3 -svp rehash l4 # 重算所有 L4 -svp rehash l5 # 重算 L5 +forge rehash l3 # 重算所有 L3 的 contentHash +forge rehash l3/greet # 重算指定 L3 +forge rehash l4 # 重算所有 L4 +forge rehash l5 # 重算 L5 ``` -### 6.7 svp link +### 6.7 forge link 创建/更新 L2 映射: ```bash -svp link greet --files src/greet.ts +forge link greet --files src/greet.ts ``` -### 6.8 svp compile-blueprint +### 6.8 forge compile-blueprint 编译 svp-blueprint 节点格式(YAML → JSON)。 -### 6.9 svp blueprint +### 6.9 forge blueprint 生成自包含的 HTML 可视化节点图查看器: ```bash -svp blueprint # 当前目录,打开浏览器 -svp blueprint -r examples/order-service # 指定项目 -svp blueprint -o blueprint.html # 输出到文件 +forge blueprint # 当前目录,打开浏览器 +forge blueprint -r examples/order-service # 指定项目 +forge blueprint -o blueprint.html # 输出到文件 ``` --- @@ -443,8 +443,8 @@ interface StructuredPrompt { 通用规则强制执行 SVP 的核心不变量: - 严格向下只写——不修改上层 -- 使用 `svp rehash` 修复 contentHash -- 使用 `svp link` 创建 L2 映射 +- 使用 `forge rehash` 修复 contentHash +- 使用 `forge link` 创建 L2 映射 - 保持实现最小化——满足契约,仅此而已 ### 7.3 Design Prompts @@ -505,7 +505,7 @@ interface StructuredPrompt { 当前支持英文和中文两种语言,覆盖了 check 错误消息、compile-plan 原因描述、view 渲染标签、CLI 提示信息、viewer UI 文本等所有用户可见的字符串。 -这意味着:如果你的 L5 设置了 `language: "zh"`,整个工具链——从 `svp check` 的错误报告到 `svp view` 的数据展示再到 AI prompt 中的语言指令——都会切换为中文。 +这意味着:如果你的 L5 设置了 `language: "zh"`,整个工具链——从 `forge check` 的错误报告到 `forge view` 的数据展示再到 AI prompt 中的语言指令——都会切换为中文。 --- @@ -581,7 +581,7 @@ nodes/my-node/ 推荐的文档结构包含:Intent(设计意图)、Edge Cases(边界情况)、Error Strategy(错误处理策略)、Integration Notes(集成约定)、Examples(输入输出示例)。 -`svp prompt compile/recompile/review` 时会自动加载并注入 prompt,让 AI 在编译时能获得更多上下文。 +`forge prompt compile/recompile/review` 时会自动加载并注入 prompt,让 AI 在编译时能获得更多上下文。 --- @@ -627,50 +627,50 @@ interface ArtifactVersion { ### 阶段一:设计意图(人) ```bash -svp init --name my-app --intent "做什么" --host -svp prompt design-l5 --intent "详细描述系统" +forge init --name my-app --intent "做什么" --host +forge prompt design-l5 --intent "详细描述系统" # AI 生成 L5 → 人审查 → 写入 .svp/l5.json -svp rehash l5 +forge rehash l5 ``` ### 阶段二:设计流程(人) ```bash -svp prompt design-l4 --intent "描述流程" --kind flow +forge prompt design-l4 --intent "描述流程" --kind flow # AI 生成 L4 → 人审查 → 写入 .svp/l4/ -svp rehash l4 +forge rehash l4 ``` ### 阶段三:设计契约(人 + AI) ```bash -svp prompt design-l3 validate-order --flow order-flow --step 0 --intent "校验订单" +forge prompt design-l3 validate-order --flow order-flow --step 0 --intent "校验订单" # AI 生成 L3 → 人审查 → 写入 .svp/l3/ -svp rehash l3 -svp check # 确保一致性 +forge rehash l3 +forge check # 确保一致性 ``` ### 阶段四:编译代码(AI) ```bash -svp compile-plan # 查看待编译任务 -svp prompt compile greet # 生成 compile prompt +forge compile-plan # 查看待编译任务 +forge prompt compile greet # 生成 compile prompt # AI 生成 L1 代码 -svp link greet --files src/greet.ts -svp rehash l2 -svp check # 全部通过 ✓ +forge link greet --files src/greet.ts +forge rehash l2 +forge check # 全部通过 ✓ ``` ### 阶段五:变更维护 当 L3 契约变更时: ```bash -svp rehash l3/validate-order -svp compile-plan # 自动检测到 recompile 任务 -svp prompt recompile validate-order +forge rehash l3/validate-order +forge compile-plan # 自动检测到 recompile 任务 +forge prompt recompile validate-order # AI 更新代码 ``` 当 L1 代码被手动修改时: ```bash -svp check # 报告 CONTENT_DRIFT -svp prompt review validate-order +forge check # 报告 CONTENT_DRIFT +forge prompt review validate-order # AI 分析漂移,建议如何处理 ``` diff --git a/docs/tutorial.md b/docs/tutorial.md index 53302b9..195e3bf 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -40,12 +40,12 @@ npm install npm run build ``` -构建后 CLI 入口在 `dist/packages/cli/index.js`,可以通过 `npx svp` 或 `node dist/packages/cli/index.js` 运行。 +构建后 CLI 入口在 `dist/packages/cli/index.js`,可以通过 `npx @svporg/forge` 或 `node dist/packages/cli/index.js` 运行。 ### 验证安装 ```bash -npx svp --help +npx @svporg/forge --help ``` 你应该能看到所有可用命令的列表。 @@ -102,8 +102,8 @@ L5 ─聚合→ L4 ─聚合→ L3 ←1:1配对→ L2 ─聚合→ L1 ### 4.1 初始化项目 ```bash -mkdir hello-svp && cd hello-svp -npx svp init --name hello-svp --intent "接收名字,生成大写问候语" +mkdir hello-forge && cd hello-forge +npx @svporg/forge init --name hello-forge --intent "接收名字,生成大写问候语" ``` 这会创建 `.svp/l5.json`(空的蓝图框架)。 @@ -113,7 +113,7 @@ npx svp init --name hello-svp --intent "接收名字,生成大写问候语" 让 AI 帮你设计系统级蓝图: ```bash -npx svp prompt design-l5 --intent "接收名字,生成问候语,转成大写输出" +npx @svporg/forge prompt design-l5 --intent "接收名字,生成问候语,转成大写输出" ``` 这会输出一段结构化的 prompt。把它发给 AI(Claude Code 等),AI 会生成 L5 蓝图的 JSON 内容,写入 `.svp/l5.json`。 @@ -121,7 +121,7 @@ npx svp prompt design-l5 --intent "接收名字,生成问候语,转成大写 写完后刷新哈希: ```bash -npx svp rehash l5 +npx @svporg/forge rehash l5 ``` ### 4.3 设计 L4 流程 @@ -129,7 +129,7 @@ npx svp rehash l5 设计处理流程——两个步骤串联: ```bash -npx svp prompt design-l4 --intent "greet → uppercase 串行管道" --kind flow +npx @svporg/forge prompt design-l4 --intent "greet → uppercase 串行管道" --kind flow ``` AI 会生成 L4 flow 文件。它的结构看起来像这样: @@ -151,7 +151,7 @@ AI 会生成 L4 flow 文件。它的结构看起来像这样: `steps` 定义了步骤和顺序,`dataFlows` 定义了步骤之间的数据传递。每个 step 通过 `blockRef` 引用一个 L3 逻辑块。 ```bash -npx svp rehash l4 +npx @svporg/forge rehash l4 ``` ### 4.4 设计 L3 逻辑块 @@ -159,7 +159,7 @@ npx svp rehash l4 为每个步骤设计契约。以 `greet` 块为例: ```bash -npx svp prompt design-l3 greet --flow hello-world --step 0 --intent "接收名字,生成问候消息" +npx @svporg/forge prompt design-l3 greet --flow hello-world --step 0 --intent "接收名字,生成问候消息" ``` AI 会生成 L3 block —— 一个**契约盒**: @@ -190,7 +190,7 @@ AI 会生成 L3 block —— 一个**契约盒**: 同样为 `uppercase` 设计 L3,然后: ```bash -npx svp rehash l3 +npx @svporg/forge rehash l3 ``` ### 4.5 校验 @@ -198,7 +198,7 @@ npx svp rehash l3 到这里,你已经定义了意图层 (L5+L4) 和契约层 (L3)。运行校验: ```bash -npx svp check +npx @svporg/forge check ``` 它会检查: @@ -213,13 +213,13 @@ npx svp check 生成编译任务列表: ```bash -npx svp compile-plan +npx @svporg/forge compile-plan ``` 输出会告诉你哪些 L3 block 需要编译(生成 L1 代码)。然后为每个任务生成 prompt: ```bash -npx svp prompt compile greet +npx @svporg/forge prompt compile greet ``` 这段 prompt 包含了 AI 需要的全部上下文:L3 契约、pin 类型、校验规则、约束条件。把它发给 AI,AI 会生成源代码文件(比如 `src/greet.ts`)。 @@ -229,9 +229,9 @@ npx svp prompt compile greet AI 生成代码后,用 `link` 命令建立 L3 ↔ L1 的映射关系: ```bash -npx svp link greet --files src/greet.ts -npx svp link uppercase --files src/uppercase.ts -npx svp rehash l2 +npx @svporg/forge link greet --files src/greet.ts +npx @svporg/forge link uppercase --files src/uppercase.ts +npx @svporg/forge rehash l2 ``` 这会创建 L2 代码块,记录每个 L3 block 对应哪些源文件。 @@ -239,7 +239,7 @@ npx svp rehash l2 ### 4.8 最终校验 ```bash -npx svp check +npx @svporg/forge check ``` 全部通过——你的项目从意图到代码,每一层都完整、一致、可追踪。 @@ -250,47 +250,47 @@ npx svp check | 命令 | 用途 | 示例 | |------|------|------| -| `svp init` | 初始化项目 | `svp init --name my-app --intent "..." --host ` | -| `svp check` | 跨层一致性校验 | `svp check --json` | -| `svp view` | 查看层数据 | `svp view l3 greet` | -| `svp compile-plan` | 生成编译任务 | `svp compile-plan --json` | -| `svp prompt` | 生成 AI prompt | `svp prompt compile greet` | -| `svp link` | 创建/更新 L2 映射 | `svp link greet --files src/greet.ts` | -| `svp rehash` | 重算哈希 | `svp rehash l3/greet` | -| `svp compile-blueprint` | YAML → JSON 编译 | `svp compile-blueprint` | -| `svp blueprint` | 生成可视化节点图 | `svp blueprint -r . -o blueprint.html` | - -### svp view 详解 +| `forge init` | 初始化项目 | `forge init --name my-app --intent "..." --host ` | +| `forge check` | 跨层一致性校验 | `forge check --json` | +| `forge view` | 查看层数据 | `forge view l3 greet` | +| `forge compile-plan` | 生成编译任务 | `forge compile-plan --json` | +| `forge prompt` | 生成 AI prompt | `forge prompt compile greet` | +| `forge link` | 创建/更新 L2 映射 | `forge link greet --files src/greet.ts` | +| `forge rehash` | 重算哈希 | `forge rehash l3/greet` | +| `forge compile-blueprint` | YAML → JSON 编译 | `forge compile-blueprint` | +| `forge blueprint` | 生成可视化节点图 | `forge blueprint -r . -o blueprint.html` | + +### forge view 详解 ```bash -svp view l5 # L5 蓝图概览 -svp view l4 # 所有 L4 流程概览(每个流程一行) -svp view l4 order-flow # L4 流程详情(步骤图、数据流、引用关系) -svp view l3 # 所有 L3 块概览(签名、校验规则数、约束数) -svp view l3 greet # L3 块详情(完整 pins、规则、约束、描述) -svp view l2 # 所有 L2 代码块(语言、文件、哈希状态) -svp view l2 greet # L2 详情(文件列表、哈希状态、配对 L3) +forge view l5 # L5 蓝图概览 +forge view l4 # 所有 L4 流程概览(每个流程一行) +forge view l4 order-flow # L4 流程详情(步骤图、数据流、引用关系) +forge view l3 # 所有 L3 块概览(签名、校验规则数、约束数) +forge view l3 greet # L3 块详情(完整 pins、规则、约束、描述) +forge view l2 # 所有 L2 代码块(语言、文件、哈希状态) +forge view l2 greet # L2 详情(文件列表、哈希状态、配对 L3) ``` Overview 把每个实体压缩成一行,供 AI 快速浏览全局拓扑;Detail 展示完整信息但不展开其他层。 -### svp prompt 详解 +### forge prompt 详解 **设计类**(创建新层数据): ```bash -svp prompt design-l5 --intent "描述系统意图" -svp prompt design-l4 --intent "描述流程" [--kind flow|event-graph|state-machine] -svp prompt design-l3 --flow --step --intent "描述单元" +forge prompt design-l5 --intent "描述系统意图" +forge prompt design-l4 --intent "描述流程" [--kind flow|event-graph|state-machine] +forge prompt design-l3 --flow --step --intent "描述单元" ``` **任务类**(已有数据的编译/维护): ```bash -svp prompt compile # L3 → L1 首次编译 -svp prompt recompile # L3 变更后重编译 -svp prompt review # L1 手改后审查漂移 -svp prompt update-ref # 修复断裂的 L4 引用 +forge prompt compile # L3 → L1 首次编译 +forge prompt recompile # L3 变更后重编译 +forge prompt review # L1 手改后审查漂移 +forge prompt update-ref # 修复断裂的 L4 引用 ``` --- @@ -302,49 +302,49 @@ svp prompt update-ref # 修复断裂的 L4 引用 ### 第一步:设计意图 ```bash -svp init --name order-service --intent "电商订单创建:校验→库存→处理→支付" -svp prompt design-l5 --intent "订单管理系统,支持创建订单、校验、库存检查、支付" +forge init --name order-service --intent "电商订单创建:校验→库存→处理→支付" +forge prompt design-l5 --intent "订单管理系统,支持创建订单、校验、库存检查、支付" # AI 生成 L5 → 写入 .svp/l5.json -svp rehash l5 +forge rehash l5 ``` ### 第二步:设计流程 ```bash -svp prompt design-l4 --intent "订单创建流程:validate → check-inventory → process → pay" --kind flow +forge prompt design-l4 --intent "订单创建流程:validate → check-inventory → process → pay" --kind flow # AI 生成 L4 flow → 写入 .svp/l4/order-flow.json -svp rehash l4 +forge rehash l4 ``` ### 第三步:设计每个 block 的契约 ```bash -svp prompt design-l3 validate-order --flow order-flow --step 0 --intent "校验订单请求字段" -svp prompt design-l3 check-inventory --flow order-flow --step 1 --intent "检查库存是否充足" -svp prompt design-l3 process-order --flow order-flow --step 2 --intent "处理订单,生成订单号" -svp prompt design-l3 process-payment --flow order-flow --step 3 --intent "调用支付网关" +forge prompt design-l3 validate-order --flow order-flow --step 0 --intent "校验订单请求字段" +forge prompt design-l3 check-inventory --flow order-flow --step 1 --intent "检查库存是否充足" +forge prompt design-l3 process-order --flow order-flow --step 2 --intent "处理订单,生成订单号" +forge prompt design-l3 process-payment --flow order-flow --step 3 --intent "调用支付网关" # AI 分别生成 4 个 L3 block -svp rehash l3 -svp check +forge rehash l3 +forge check ``` ### 第四步:编译代码 ```bash -svp compile-plan +forge compile-plan # 输出 4 个 compile 任务 -svp prompt compile validate-order +forge prompt compile validate-order # AI 生成 src/validate-order.ts -svp link validate-order --files src/validate-order.ts +forge link validate-order --files src/validate-order.ts -svp prompt compile check-inventory +forge prompt compile check-inventory # AI 生成 src/check-inventory.ts -svp link check-inventory --files src/check-inventory.ts +forge link check-inventory --files src/check-inventory.ts # ... 对每个 block 重复 -svp rehash l2 -svp check # 全部通过 ✓ +forge rehash l2 +forge check # 全部通过 ✓ ``` ### 关键洞察 @@ -370,18 +370,18 @@ AI 只负责从契约编译到代码,绝不回头修改上层意图。如果 A ```bash # 1. 编辑 .svp/l3/validate-order.json,添加新的 validate 规则 # 2. 重算哈希 -svp rehash l3/validate-order +forge rehash l3/validate-order # 3. 检测影响范围 -svp compile-plan +forge compile-plan # 输出:recompile validate-order(因为 L2 的 sourceHash 与 L3 的 contentHash 不匹配了) # 4. 重编译 -svp prompt recompile validate-order +forge prompt recompile validate-order # AI 更新 src/validate-order.ts -svp link validate-order --files src/validate-order.ts -svp rehash l2 -svp check +forge link validate-order --files src/validate-order.ts +forge rehash l2 +forge check ``` ### 场景 B:手改 L1 代码(自下而上) @@ -389,19 +389,19 @@ svp check 开发者直接修改了 `src/validate-order.ts`(比如优化了性能): ```bash -svp check +forge check # 报告:CONTENT_DRIFT — L2 的 signatureHash 与 L1 实际导出签名不匹配 ``` 这时你有三个选择: -1. **接受变更** — 用 `svp link` 更新 L2 的哈希 -2. **回退代码** — 用 `svp prompt recompile` 让 AI 重新生成 +1. **接受变更** — 用 `forge link` 更新 L2 的哈希 +2. **回退代码** — 用 `forge prompt recompile` 让 AI 重新生成 3. **更新契约** — 如果代码变更反映了新需求,同步修改 L3 ```bash # 让 AI 帮你分析漂移 -svp prompt review validate-order +forge prompt review validate-order # AI 输出:哪些签名变了、是否兼容 L3 契约、建议怎么处理 ``` @@ -410,13 +410,13 @@ svp prompt review validate-order 如果你在流程中插入一个新步骤,`compile-plan` 会检测到新的 `blockRef` 没有对应的 L3 block,输出 `update-ref` 任务: ```bash -svp compile-plan +forge compile-plan # 输出:update-ref order-flow(因为新步骤引用了不存在的 L3 block) -svp prompt update-ref order-flow +forge prompt update-ref order-flow # AI 生成缺失的 L3 block -svp rehash l3 -svp check +forge rehash l3 +forge check ``` --- @@ -426,7 +426,7 @@ svp check SVP 支持 6 种 AI 编码工具。初始化时用 `--host` 指定你的工具,SVP 会自动生成对应的 skill 文件和上下文配置: ```bash -svp init --name my-app --host +forge init --name my-app --host ``` ### 支持的工具 @@ -448,15 +448,15 @@ svp init --name my-app --host ``` 你:设计一个用户注册流程 -AI:(调用 svp prompt design-l4,生成流程,调用 svp rehash) +AI:(调用 forge prompt design-l4,生成流程,调用 forge rehash) 已创建 L4 flow: user-registration,包含 3 个步骤... 你:编译所有 block -AI:(调用 svp compile-plan,对每个任务调用 svp prompt compile,生成代码,调用 svp link) - 已编译 3 个 block,所有文件已链接,svp check 通过 ✓ +AI:(调用 forge compile-plan,对每个任务调用 forge prompt compile,生成代码,调用 forge link) + 已编译 3 个 block,所有文件已链接,forge check 通过 ✓ 你:给 validate-email 加上域名黑名单校验 -AI:(更新 L3 的 validate 规则,调用 svp rehash,然后 svp prompt recompile) +AI:(更新 L3 的 validate 规则,调用 forge rehash,然后 forge prompt recompile) 已更新 L3 契约并重编译 validate-email ✓ ``` @@ -466,38 +466,38 @@ AI:(更新 L3 的 validate 规则,调用 svp rehash,然后 svp prompt re ### Q: SVP 支持哪些编程语言? -SVP 是语言无关的协议。L3 契约中的 `type` 字段引用 TypeScript interface(因为 AI 对 TypeScript 类型理解最好),但 L1 生成的代码可以是任何语言。用 `svp link --language python` 指定目标语言。 +SVP 是语言无关的协议。L3 契约中的 `type` 字段引用 TypeScript interface(因为 AI 对 TypeScript 类型理解最好),但 L1 生成的代码可以是任何语言。用 `forge link --language python` 指定目标语言。 ### Q: contentHash 是什么? -每个层的数据模型都包含 `contentHash`——内容的哈希值。编辑数据后用 `svp rehash` 重算。它是变更追踪的基础:上层哈希变了 → 下层记录的不匹配 → 触发重编译。 +每个层的数据模型都包含 `contentHash`——内容的哈希值。编辑数据后用 `forge rehash` 重算。它是变更追踪的基础:上层哈希变了 → 下层记录的不匹配 → 触发重编译。 ### Q: 为什么 validate 和 constraints 用自然语言? 因为 AI 直接理解自然语言。`"array, min 1, max 50"` 比结构化规则对象更简洁,AI 理解得一样准确。而且像 `"not just first"` 这样的微妙语义,结构化表示会丢失。 -### Q: svp check 报错了怎么办? +### Q: forge check 报错了怎么办? -`svp check` 会告诉你具体是哪一层的什么问题。常见问题: +`forge check` 会告诉你具体是哪一层的什么问题。常见问题: | 错误类型 | 含义 | 修复 | |----------|------|------| -| HASH_MISMATCH | contentHash 和实际内容不一致 | `svp rehash ` | +| HASH_MISMATCH | contentHash 和实际内容不一致 | `forge rehash ` | | MISSING_BLOCK_REF | L4 step 引用的 L3 block 不存在 | 创建缺失的 L3 或修复引用 | -| CONTENT_DRIFT | L1 代码与 L3 契约不同步 | `svp prompt review` 分析后决定 | +| CONTENT_DRIFT | L1 代码与 L3 契约不同步 | `forge prompt review` 分析后决定 | | ORPHAN_STEP | L4 中有不可达的步骤 | 检查步骤链接 | ### Q: 我可以跳过某些层吗? -可以。如果你只想用 L3 → L1 的编译能力,不需要定义 L5 和 L4。直接创建 L3 block,然后 `svp prompt compile`。不过完整使用五层能让你在更高层级观测和管理系统。 +可以。如果你只想用 L3 → L1 的编译能力,不需要定义 L5 和 L4。直接创建 L3 block,然后 `forge prompt compile`。不过完整使用五层能让你在更高层级观测和管理系统。 ### Q: compile-blueprint 是做什么的? -如果你用 svp-blueprint 可视化编辑器(节点图方式编辑 L3/L4),编辑器会把数据保存为 `nodes/*.yaml` 和 `graphs/*.yaml`。`svp compile-blueprint` 把这些 YAML 编译成 `.svp/` 下的标准 JSON 格式。 +如果你用 svp-blueprint 可视化编辑器(节点图方式编辑 L3/L4),编辑器会把数据保存为 `nodes/*.yaml` 和 `graphs/*.yaml`。`forge compile-blueprint` 把这些 YAML 编译成 `.svp/` 下的标准 JSON 格式。 -### Q: svp blueprint 是做什么的? +### Q: forge blueprint 是做什么的? -`svp blueprint` 读取 `.svp/` 下的 L3/L4 数据,生成一个自包含的 HTML 文件并在浏览器中打开。这个查看器用节点图渲染流程的步骤编排和数据流,支持平移、缩放、点击查看 L3 契约详情。暗色主题 + 网格背景,pin 按类型自动着色,节点可展开查看完整的 validate 和 constraints。无需安装任何依赖,单个 HTML 文件离线可用。 +`forge blueprint` 读取 `.svp/` 下的 L3/L4 数据,生成一个自包含的 HTML 文件并在浏览器中打开。这个查看器用节点图渲染流程的步骤编排和数据流,支持平移、缩放、点击查看 L3 契约详情。暗色主题 + 网格背景,pin 按类型自动着色,节点可展开查看完整的 validate 和 constraints。无需安装任何依赖,单个 HTML 文件离线可用。 --- diff --git a/package-lock.json b/package-lock.json index 9076513..c4aac55 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,19 @@ { - "name": "svp", + "name": "@svporg/forge", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "svp", + "name": "@svporg/forge", "version": "0.1.0", + "license": "MIT", "dependencies": { "commander": "^14.0.3", "yaml": "^2.8.2" }, "bin": { - "svp": "dist/packages/cli/index.js" + "forge": "dist/packages/cli/index.js" }, "devDependencies": { "@eslint/js": "^10.0.1", diff --git a/packages/cli/commands/check.test.ts b/packages/cli/commands/check.test.ts index 0b6ea41..9980c3b 100644 --- a/packages/cli/commands/check.test.ts +++ b/packages/cli/commands/check.test.ts @@ -1,4 +1,4 @@ -// svp check CLI 命令的集成测试 +// forge check CLI 命令的集成测试 // 创建临时 .svp/ 目录,写入测试数据,调用 check 命令逻辑 import { mkdir, readFile, rm, writeFile } from "node:fs/promises"; @@ -100,7 +100,7 @@ function makeL2(l3: L3Block, overrides: Partial = {}): L2CodeBlock // ── 集成测试 ── -describe("svp check CLI integration", () => { +describe("forge check CLI integration", () => { let testRoot: string; beforeEach(async () => { diff --git a/packages/cli/commands/check.ts b/packages/cli/commands/check.ts index 3d4b449..e6e4988 100644 --- a/packages/cli/commands/check.ts +++ b/packages/cli/commands/check.ts @@ -1,4 +1,4 @@ -// svp check — 层间一致性校验命令 +// forge check — 层间一致性校验命令 // 读取 .svp/ 目录下的所有数据,调用 core.check(),格式化输出结果 import { check } from "../../core/index.js"; @@ -12,7 +12,7 @@ function formatIssue(issue: CheckIssue): string { return ` ${icon} [${issue.code}] ${issue.layer}/${issue.entityId}: ${issue.message}`; } -/** 注册 svp check 子命令 */ +/** 注册 forge check 子命令 */ export function registerCheck(program: Command): void { program .command("check") @@ -33,7 +33,7 @@ export function registerCheck(program: Command): void { if (options.json) { console.log(JSON.stringify({ issues: [], summary: { errors: 0, warnings: 0 } })); } else { - console.log("No .svp/ data found. Run `svp init` to create a project."); + console.log("No .svp/ data found. Run `forge init` to create a project."); } return; } @@ -55,7 +55,7 @@ export function registerCheck(program: Command): void { .filter(Boolean) .join(" "); - console.log(`svp check — loaded: ${loaded}`); + console.log(`forge check — loaded: ${loaded}`); console.log(); if (report.issues.length === 0) { diff --git a/packages/cli/commands/compile-plan.ts b/packages/cli/commands/compile-plan.ts index 308ec34..de9ce12 100644 --- a/packages/cli/commands/compile-plan.ts +++ b/packages/cli/commands/compile-plan.ts @@ -1,4 +1,4 @@ -// svp compile-plan — 变更检测 + 重编译任务生成的 CLI 命令 +// forge compile-plan — 变更检测 + 重编译任务生成的 CLI 命令 import { compilePlan } from "../../core/index.js"; import { loadCheckInput } from "../load.js"; @@ -31,7 +31,7 @@ function formatTask(task: CompileTask, index: number): string { return lines.join("\n"); } -/** 注册 svp compile-plan 子命令 */ +/** 注册 forge compile-plan 子命令 */ export function registerCompilePlan(program: Command): void { program .command("compile-plan") @@ -56,7 +56,7 @@ export function registerCompilePlan(program: Command): void { }), ); } else { - console.log("No .svp/ data found. Run `svp init` to create a project."); + console.log("No .svp/ data found. Run `forge init` to create a project."); } return; } @@ -69,7 +69,7 @@ export function registerCompilePlan(program: Command): void { } // 人类可读输出 - console.log(`svp compile-plan — scanned ${String(entityCount)} entities`); + console.log(`forge compile-plan — scanned ${String(entityCount)} entities`); console.log(); if (plan.tasks.length === 0) { diff --git a/packages/cli/commands/fix.ts b/packages/cli/commands/fix.ts index 7d48fc4..a46b20f 100644 --- a/packages/cli/commands/fix.ts +++ b/packages/cli/commands/fix.ts @@ -1,4 +1,4 @@ -// svp fix — 自动检测一致性问题并批量生成修复提示词 +// forge fix — 自动检测一致性问题并批量生成修复提示词 // check → compilePlan → resolve → buildPrompt → stdout import { compilePlan } from "../../core/compile-plan.js"; @@ -11,61 +11,70 @@ import type { CompileTask, TaskAction } from "../../core/compile-plan.js"; import type { SkillInput } from "../../core/skill.js"; import type { Command } from "commander"; -/** 注册 svp fix 命令 */ +/** 注册 forge fix 命令 */ export function registerFix(program: Command): void { program .command("fix") .description("Auto-detect issues and generate fix prompts for all compile tasks") .option("-r, --root ", "Project root directory", ".") .option("--severity ", "Minimum severity: error (default) or warning", "error") - .option("--action ", "Only generate prompts for this action (compile/recompile/update-ref/review)") + .option( + "--action ", + "Only generate prompts for this action (compile/recompile/update-ref/review)", + ) .option("--dry-run", "Print compile-plan summary only, do not generate prompts") - .action(async (options: { root: string; severity: string; action?: string; dryRun?: boolean }) => { - const root = options.root; + .action( + async (options: { root: string; severity: string; action?: string; dryRun?: boolean }) => { + const root = options.root; - let input; - try { - input = await loadCheckInput(root); - } catch { - console.error(`Error: cannot load .svp/ data from "${root}". Run \`svp init\` first.`); - process.exitCode = 1; - return; - } + let input; + try { + input = await loadCheckInput(root); + } catch { + console.error(`Error: cannot load .svp/ data from "${root}". Run \`forge init\` first.`); + process.exitCode = 1; + return; + } - const language = getLanguage(input.l5); - const plan = compilePlan(input, language); + const language = getLanguage(input.l5); + const plan = compilePlan(input, language); - // Filter by action if specified - let tasks = plan.tasks as CompileTask[]; - if (options.action !== undefined) { - const action = options.action as TaskAction; - tasks = tasks.filter((t) => t.action === action); - } + // Filter by action if specified + let tasks = plan.tasks as CompileTask[]; + if (options.action !== undefined) { + const action = options.action as TaskAction; + tasks = tasks.filter((t) => t.action === action); + } - if (tasks.length === 0) { - console.log("All checks passed — no fix tasks generated."); - return; - } + if (tasks.length === 0) { + console.log("All checks passed — no fix tasks generated."); + return; + } - // Dry-run: print summary only - if (options.dryRun === true) { - console.log(`Compile plan: ${tasks.length} task(s)`); - for (const [i, task] of tasks.entries()) { - console.log(` [${i + 1}] ${task.action} · ${task.targetLayer}/${task.targetId} · ${task.issueCode}`); + // Dry-run: print summary only + if (options.dryRun === true) { + console.log(`Compile plan: ${String(tasks.length)} task(s)`); + for (const [i, task] of tasks.entries()) { + console.log( + ` [${String(i + 1)}] ${task.action} · ${task.targetLayer}/${task.targetId} · ${task.issueCode}`, + ); + } + return; } - return; - } - // Generate prompts - const resolver = createResolver(root); - for (const [i, task] of tasks.entries()) { - if (i > 0) console.log("\n---\n"); - console.log(`## [${i + 1}/${tasks.length}] ${task.action} · ${task.targetLayer}/${task.targetId} · ${task.issueCode}\n`); + // Generate prompts + const resolver = createResolver(root); + for (const [i, task] of tasks.entries()) { + if (i > 0) console.log("\n---\n"); + console.log( + `## [${String(i + 1)}/${String(tasks.length)}] ${task.action} · ${task.targetLayer}/${task.targetId} · ${task.issueCode}\n`, + ); - const resolved = await resolver.resolve(task, input); - const skillInput: SkillInput = { task, resolved, config: DEFAULT_SKILL_CONFIG }; - const structured = buildPrompt(skillInput); - console.log(renderPrompt(structured)); - } - }); + const resolved = await resolver.resolve(task, input); + const skillInput: SkillInput = { task, resolved, config: DEFAULT_SKILL_CONFIG }; + const structured = buildPrompt(skillInput); + console.log(renderPrompt(structured)); + } + }, + ); } diff --git a/packages/cli/commands/init.test.ts b/packages/cli/commands/init.test.ts index 464afc2..a6448f6 100644 --- a/packages/cli/commands/init.test.ts +++ b/packages/cli/commands/init.test.ts @@ -1,4 +1,4 @@ -// svp init CLI 命令的集成测试 +// forge init CLI 命令的集成测试 import { mkdir, readFile, rm, stat } from "node:fs/promises"; import { tmpdir } from "node:os"; @@ -67,7 +67,7 @@ async function fileExists(p: string): Promise { // ── Tests ── -describe("svp init", () => { +describe("forge init", () => { let testRoot: string; beforeEach(async () => { @@ -457,12 +457,7 @@ describe("svp init", () => { }); it("i18n shows correct host name for cursor", async () => { - const { stdout, exitCode } = await runInit(testRoot, [ - "--name", - "My App", - "--host", - "cursor", - ]); + const { stdout, exitCode } = await runInit(testRoot, ["--name", "My App", "--host", "cursor"]); expect(exitCode).toBe(0); expect(stdout).toContain("Cursor"); diff --git a/packages/cli/commands/init.ts b/packages/cli/commands/init.ts index 99e234b..e95812b 100644 --- a/packages/cli/commands/init.ts +++ b/packages/cli/commands/init.ts @@ -1,9 +1,9 @@ -// svp init — 初始化 .svp/ 目录结构的 CLI 命令 +// forge init — 初始化 .svp/ 目录结构的 CLI 命令 import { mkdir, readFile, writeFile } from "node:fs/promises"; import path from "node:path"; +import { t } from "../../core/i18n.js"; import { init } from "../../core/init.js"; -import { t, getLanguage } from "../../core/i18n.js"; import { getAdapter, getAllAdapterIds, detectHosts } from "../../skills/adapters/index.js"; import type { HostId, HostAdapter } from "../../skills/adapters/index.js"; import type { Command } from "commander"; @@ -14,7 +14,7 @@ function isValidHost(host: string): host is HostId { return (VALID_HOSTS as readonly string[]).includes(host); } -/** 注册 svp init 子命令 */ +/** 注册 forge init 子命令 */ export function registerInit(program: Command): void { program .command("init") @@ -38,14 +38,7 @@ export function registerInit(program: Command): void { // Resolve host: explicit flag > auto-detect let hostId: HostId | undefined; - if (options.host != null) { - if (!isValidHost(options.host)) { - console.error(`Unknown host: ${options.host}. Valid hosts: ${VALID_HOSTS.join(", ")}`); - process.exitCode = 1; - return; - } - hostId = options.host; - } else { + if (options.host === undefined) { // Auto-detect from project directory markers const detected = await detectHosts(options.root); if (detected.length === 1) { @@ -55,6 +48,13 @@ export function registerInit(program: Command): void { `Multiple hosts detected: ${detected.join(", ")}. Use --host to specify one.`, ); } + } else { + if (!isValidHost(options.host)) { + console.error(`Unknown host: ${options.host}. Valid hosts: ${VALID_HOSTS.join(", ")}`); + process.exitCode = 1; + return; + } + hostId = options.host; } const result = await init(options.root, { @@ -68,7 +68,7 @@ export function registerInit(program: Command): void { if (!result.created) { console.log(t(lang, "cli.init.alreadyExists")); // Still generate host files if resolved - if (hostId != null) { + if (hostId !== undefined) { await generateHostFiles(options.root, getAdapter(hostId), options.name, lang); } return; @@ -90,12 +90,12 @@ export function registerInit(program: Command): void { console.log(" └── l2/ (code blocks)"); // Host-specific integration - if (hostId != null) { + if (hostId === undefined) { console.log(); - await generateHostFiles(options.root, getAdapter(hostId), options.name, lang); + console.log(t(lang, "cli.init.nextEdit")); } else { console.log(); - console.log(t(lang, "cli.init.nextEdit")); + await generateHostFiles(options.root, getAdapter(hostId), options.name, lang); } }, ); diff --git a/packages/cli/commands/link.test.ts b/packages/cli/commands/link.test.ts index a882b13..d312516 100644 --- a/packages/cli/commands/link.test.ts +++ b/packages/cli/commands/link.test.ts @@ -1,4 +1,4 @@ -// svp link CLI 命令的集成测试 +// forge link CLI 命令的集成测试 import { mkdir, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; @@ -89,7 +89,7 @@ async function runLink( // ── Tests ── -describe("svp link", () => { +describe("forge link", () => { let testRoot: string; beforeEach(async () => { diff --git a/packages/cli/commands/link.ts b/packages/cli/commands/link.ts index 3c805e7..9451283 100644 --- a/packages/cli/commands/link.ts +++ b/packages/cli/commands/link.ts @@ -1,11 +1,11 @@ -// svp link — 创建 L2CodeBlock(L3 和 L1 之间的桥接层) +// forge link — 创建 L2CodeBlock(L3 和 L1 之间的桥接层) // AI 生成 L1 源代码后运行,创建/更新 L2 映射 import { readL2, readL3, writeL2 } from "../../core/index.js"; import { createL2Link, relinkL2 } from "../../skills/index.js"; import type { Command } from "commander"; -/** 注册 svp link 子命令 */ +/** 注册 forge link 子命令 */ export function registerLink(program: Command): void { program .command("link") diff --git a/packages/cli/commands/prompt.test.ts b/packages/cli/commands/prompt.test.ts index 8c298d8..ad65df8 100644 --- a/packages/cli/commands/prompt.test.ts +++ b/packages/cli/commands/prompt.test.ts @@ -1,4 +1,4 @@ -// svp prompt CLI 命令的集成测试 +// forge prompt CLI 命令的集成测试 // 创建临时 .svp/ 目录,写入测试数据,验证 prompt 输出 import { mkdir, rm } from "node:fs/promises"; @@ -126,7 +126,7 @@ async function runPrompt( // ── Tests ── -describe("svp prompt", () => { +describe("forge prompt", () => { let testRoot: string; beforeEach(async () => { @@ -163,7 +163,7 @@ describe("svp prompt", () => { expect(stdout).toContain("validate-order"); expect(stdout).toContain("L3 Contract"); expect(stdout).toContain("Output Spec"); - expect(stdout).toContain("svp link"); + expect(stdout).toContain("forge link"); }); it("includes L4 flow context when block is referenced", async () => { diff --git a/packages/cli/commands/prompt.ts b/packages/cli/commands/prompt.ts index 9e7ffed..1edaa58 100644 --- a/packages/cli/commands/prompt.ts +++ b/packages/cli/commands/prompt.ts @@ -1,9 +1,10 @@ -// svp prompt — 生成上下文感知的 AI 提示词 +// forge prompt — 生成上下文感知的 AI 提示词 // 7 个子命令:compile, recompile, review, update-ref, design-l5, design-l4, design-l3 // 读 .svp/ 状态 → 解析上下文 → 调用 prompt builder → stdout 输出 markdown -import { extractBlockRefs, findBlockContext, getL4Kind } from "../../core/l4.js"; +import { getDefaultComplexity } from "../../core/compile-plan.js"; import { getLanguage } from "../../core/i18n.js"; +import { extractBlockRefs, findBlockContext, getL4Kind } from "../../core/l4.js"; import { DEFAULT_SKILL_CONFIG } from "../../core/skill.js"; import { buildPrompt, renderPrompt } from "../../skills/prompt-builder.js"; import { buildDesignL3Prompt } from "../../skills/prompts/design-l3.js"; @@ -13,14 +14,13 @@ import { buildDesignL4Prompt } from "../../skills/prompts/design-l4.js"; import { buildDesignL5Prompt } from "../../skills/prompts/design-l5.js"; import { loadCheckInput } from "../load.js"; import { createResolver } from "../resolve.js"; -import { getDefaultComplexity } from "../../core/compile-plan.js"; import type { CompileTask, ContextRef, TaskAction } from "../../core/compile-plan.js"; import type { L3Block } from "../../core/l3.js"; -import type { L4Artifact, L4Flow } from "../../core/l4.js"; +import type { L4Flow } from "../../core/l4.js"; import type { SkillInput } from "../../core/skill.js"; import type { Command } from "commander"; -/** 注册 svp prompt 子命令组 */ +/** 注册 forge prompt 子命令组 */ export function registerPrompt(program: Command): void { const prompt = program .command("prompt") @@ -87,7 +87,7 @@ function registerTaskPrompt(parent: Command, action: TaskAction, config: TaskPro try { input = await loadCheckInput(root, { computeSignatures: action === "review" }); } catch { - console.error(`Error: cannot load .svp/ data from "${root}". Run \`svp init\` first.`); + console.error(`Error: cannot load .svp/ data from "${root}". Run \`forge init\` first.`); process.exitCode = 1; return; } @@ -166,7 +166,7 @@ function registerUpdateRef(parent: Command): void { try { input = await loadCheckInput(root); } catch { - console.error(`Error: cannot load .svp/ data from "${root}". Run \`svp init\` first.`); + console.error(`Error: cannot load .svp/ data from "${root}". Run \`forge init\` first.`); process.exitCode = 1; return; } @@ -265,14 +265,14 @@ function registerDesignL4(parent: Command): void { try { input = await loadCheckInput(root); } catch { - console.error(`Error: cannot load .svp/ data from "${root}". Run \`svp init\` first.`); + console.error(`Error: cannot load .svp/ data from "${root}". Run \`forge init\` first.`); process.exitCode = 1; return; } if (input.l5 === undefined) { console.error( - "Error: L5 blueprint not found. Design L5 first with `svp prompt design-l5`.", + "Error: L5 blueprint not found. Design L5 first with `forge prompt design-l5`.", ); process.exitCode = 1; return; @@ -333,7 +333,7 @@ function registerDesignL3(parent: Command): void { try { input = await loadCheckInput(root); } catch { - console.error(`Error: cannot load .svp/ data from "${root}". Run \`svp init\` first.`); + console.error(`Error: cannot load .svp/ data from "${root}". Run \`forge init\` first.`); process.exitCode = 1; return; } @@ -366,7 +366,8 @@ function registerDesignL3(parent: Command): void { }; const prevBlock = stepIndex > 0 ? findBlock(stepIndex - 1) : undefined; - const nextBlock = stepIndex < flow.steps.length - 1 ? findBlock(stepIndex + 1) : undefined; + const nextBlock = + stepIndex < flow.steps.length - 1 ? findBlock(stepIndex + 1) : undefined; const existingBlock = input.l3Blocks.find((b) => b.id === blockId); const prompt = buildDesignL3Prompt({ @@ -391,12 +392,14 @@ function registerDesignL3(parent: Command): void { } // Resolve neighbor L3 blocks from blockContext - const prevBlock = blockContext.prevBlockRef !== undefined - ? input.l3Blocks.find((b) => b.id === blockContext.prevBlockRef) - : undefined; - const nextBlock = blockContext.nextBlockRef !== undefined - ? input.l3Blocks.find((b) => b.id === blockContext.nextBlockRef) - : undefined; + const prevBlock = + blockContext.prevBlockRef === undefined + ? undefined + : input.l3Blocks.find((b) => b.id === blockContext.prevBlockRef); + const nextBlock = + blockContext.nextBlockRef === undefined + ? undefined + : input.l3Blocks.find((b) => b.id === blockContext.nextBlockRef); const existingBlock = input.l3Blocks.find((b) => b.id === blockId); const prompt = buildDesignL3Prompt({ diff --git a/packages/cli/commands/rehash.test.ts b/packages/cli/commands/rehash.test.ts index 9418342..334630b 100644 --- a/packages/cli/commands/rehash.test.ts +++ b/packages/cli/commands/rehash.test.ts @@ -1,4 +1,4 @@ -// svp rehash CLI 命令的集成测试 +// forge rehash CLI 命令的集成测试 import { mkdir, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; @@ -156,7 +156,7 @@ async function runRehash( // ── Tests ── -describe("svp rehash", () => { +describe("forge rehash", () => { let testRoot: string; beforeEach(async () => { diff --git a/packages/cli/commands/rehash.ts b/packages/cli/commands/rehash.ts index d3738fc..4a21da0 100644 --- a/packages/cli/commands/rehash.ts +++ b/packages/cli/commands/rehash.ts @@ -1,4 +1,4 @@ -// svp rehash — 重算 contentHash + 更新 revision +// forge rehash — 重算 contentHash + 更新 revision // AI 写完 JSON 后运行,自动修正 hash import { @@ -25,7 +25,7 @@ function formatResult(result: RehashResult): string { return ` OK ${result.layer}/${result.id}: ${oldShort} -> ${newShort}`; } -/** 注册 svp rehash 子命令 */ +/** 注册 forge rehash 子命令 */ export function registerRehash(program: Command): void { program .command("rehash") @@ -102,7 +102,7 @@ export function registerRehash(program: Command): void { const changed = results.filter((r) => r.changed); console.log( - `svp rehash — ${String(results.length)} artifact(s) checked, ${String(changed.length)} updated`, + `forge rehash — ${String(results.length)} artifact(s) checked, ${String(changed.length)} updated`, ); console.log(); for (const r of results) { diff --git a/packages/cli/commands/view.ts b/packages/cli/commands/view.ts index 3e02843..aea4114 100644 --- a/packages/cli/commands/view.ts +++ b/packages/cli/commands/view.ts @@ -1,4 +1,4 @@ -// svp view — 虚拟文件树渲染命令 +// forge view — 虚拟文件树渲染命令 // 读取 .svp/ 目录下的数据,渲染为 AI 友好的文本视图 import { @@ -53,14 +53,14 @@ async function loadAll(root: string): Promise<{ return { l5, l4Flows, l3Blocks, l2Blocks }; } -/** 注册 svp view 子命令 */ +/** 注册 forge view 子命令 */ export function registerView(program: Command): void { const view = program .command("view") .description("Render virtual file tree views of .svp/ data") .option("-r, --root ", "Project root directory", "."); - // svp view l5 + // forge view l5 view .command("l5") .description("L5 Blueprint overview") @@ -68,13 +68,13 @@ export function registerView(program: Command): void { const root = view.opts<{ root: string }>().root; const l5 = (await readL5(root)) ?? undefined; if (l5 === undefined) { - console.log("No L5 blueprint found. Run `svp init` to create one."); + console.log("No L5 blueprint found. Run `forge init` to create one."); return; } console.log(viewL5Overview(l5)); }); - // svp view l4 + // forge view l4 view .command("l4 [id]") .description("L4 Logic Chains — overview or detail for a specific flow") @@ -107,7 +107,7 @@ export function registerView(program: Command): void { console.log(viewL4Detail(flow, l3Blocks, l5)); }); - // svp view l3 + // forge view l3 view .command("l3 [id]") .description("L3 Logic Blocks — overview or detail for a specific block") @@ -140,7 +140,7 @@ export function registerView(program: Command): void { console.log(viewL3Detail(block, l4Flows, l2Blocks)); }); - // svp view l2 + // forge view l2 view .command("l2 [id]") .description("L2 Code Blocks — overview or detail for a specific block") diff --git a/packages/cli/index.ts b/packages/cli/index.ts index 69a941a..e4d3811 100644 --- a/packages/cli/index.ts +++ b/packages/cli/index.ts @@ -1,10 +1,10 @@ #!/usr/bin/env node -// svp CLI — SVP 工具链的命令行入口 +// forge CLI — SVP 工具链的命令行入口 import { Command } from "commander"; import { registerCheck } from "./commands/check.js"; -import { registerFix } from "./commands/fix.js"; import { registerCompilePlan } from "./commands/compile-plan.js"; +import { registerFix } from "./commands/fix.js"; import { registerInit } from "./commands/init.js"; import { registerLink } from "./commands/link.js"; import { registerPrompt } from "./commands/prompt.js"; @@ -14,7 +14,7 @@ import { registerView } from "./commands/view.js"; /** 创建并配置 CLI 程序 */ export function createCLI(): Command { const program = new Command() - .name("svp") + .name("forge") .description("SVP — Semantic Voxel Protocol toolchain") .version("0.1.0"); diff --git a/packages/core/check.ts b/packages/core/check.ts index de11a51..84c3ceb 100644 --- a/packages/core/check.ts +++ b/packages/core/check.ts @@ -1,4 +1,4 @@ -// svp check — 层间一致性校验 +// forge check — 层间一致性校验 // 检查 hash 一致性、引用完整性、漂移检测、图结构合法性 import { computeHash } from "./hash.js"; @@ -78,7 +78,10 @@ function checkHashConsistency(input: CheckInput, lang: string): CheckIssue[] { layer: "l5", entityId: input.l5.id, code: "HASH_MISMATCH", - message: t(lang, "check.hashMismatch.l5", { stored: input.l5.contentHash, computed: expected }), + message: t(lang, "check.hashMismatch.l5", { + stored: input.l5.contentHash, + computed: expected, + }), }); } @@ -102,7 +105,11 @@ function checkHashConsistency(input: CheckInput, lang: string): CheckIssue[] { layer: "l4", entityId: l4.id, code: "HASH_MISMATCH", - message: t(lang, "check.hashMismatch.l4", { name: l4.name, stored: l4.contentHash, computed: expected }), + message: t(lang, "check.hashMismatch.l4", { + name: l4.name, + stored: l4.contentHash, + computed: expected, + }), }); } } @@ -116,7 +123,11 @@ function checkHashConsistency(input: CheckInput, lang: string): CheckIssue[] { layer: "l3", entityId: block.id, code: "HASH_MISMATCH", - message: t(lang, "check.hashMismatch.l3", { name: block.name, stored: block.contentHash, computed: expected }), + message: t(lang, "check.hashMismatch.l3", { + name: block.name, + stored: block.contentHash, + computed: expected, + }), }); } } @@ -130,7 +141,11 @@ function checkHashConsistency(input: CheckInput, lang: string): CheckIssue[] { layer: "l2", entityId: cb.id, code: "HASH_MISMATCH", - message: t(lang, "check.hashMismatch.l2", { id: cb.id, stored: cb.contentHash, computed: expected }), + message: t(lang, "check.hashMismatch.l2", { + id: cb.id, + stored: cb.contentHash, + computed: expected, + }), }); } } @@ -199,7 +214,11 @@ function checkFlowRefs( layer: "l4", entityId: flow.id, code: "MISSING_BLOCK_REF", - message: t(lang, "check.missingBlockRef.l4FlowStep", { flowName: flow.name, stepId: step.id, blockRef: step.blockRef }), + message: t(lang, "check.missingBlockRef.l4FlowStep", { + flowName: flow.name, + stepId: step.id, + blockRef: step.blockRef, + }), }); } @@ -209,7 +228,11 @@ function checkFlowRefs( layer: "l4", entityId: flow.id, code: "MISSING_FLOW_REF", - message: t(lang, "check.missingFlowRef", { flowName: flow.name, stepId: step.id, flowRef: step.flowRef }), + message: t(lang, "check.missingFlowRef", { + flowName: flow.name, + stepId: step.id, + flowRef: step.flowRef, + }), }); } @@ -267,7 +290,12 @@ function checkEventGraphRefs( layer: "l4", entityId: eg.id, code: "MISSING_BLOCK_REF", - message: t(lang, "check.missingBlockRef.l4EventGraphStep", { egName: eg.name, handlerId: handler.id, stepId: step.id, blockRef: step.blockRef }), + message: t(lang, "check.missingBlockRef.l4EventGraphStep", { + egName: eg.name, + handlerId: handler.id, + stepId: step.id, + blockRef: step.blockRef, + }), }); } @@ -287,7 +315,17 @@ function checkEventGraphRefs( issues, lang, ); - checkEventGraphDataFlowEndpoint(df.to, "to", eg, handler, stepIds, l3Ids, l3Blocks, issues, lang); + checkEventGraphDataFlowEndpoint( + df.to, + "to", + eg, + handler, + stepIds, + l3Ids, + l3Blocks, + issues, + lang, + ); } } @@ -306,7 +344,11 @@ function checkStateMachineRefs(sm: L4StateMachine, l3Ids: Set, lang: str layer: "l4", entityId: sm.id, code: "MISSING_BLOCK_REF", - message: t(lang, "check.missingBlockRef.l4SmOnEntry", { smName: sm.name, stateName, blockRef: config.onEntry.blockRef }), + message: t(lang, "check.missingBlockRef.l4SmOnEntry", { + smName: sm.name, + stateName, + blockRef: config.onEntry.blockRef, + }), }); } if (config.onExit?.blockRef !== undefined && !l3Ids.has(config.onExit.blockRef)) { @@ -315,7 +357,11 @@ function checkStateMachineRefs(sm: L4StateMachine, l3Ids: Set, lang: str layer: "l4", entityId: sm.id, code: "MISSING_BLOCK_REF", - message: t(lang, "check.missingBlockRef.l4SmOnExit", { smName: sm.name, stateName, blockRef: config.onExit.blockRef }), + message: t(lang, "check.missingBlockRef.l4SmOnExit", { + smName: sm.name, + stateName, + blockRef: config.onExit.blockRef, + }), }); } } @@ -328,7 +374,12 @@ function checkStateMachineRefs(sm: L4StateMachine, l3Ids: Set, lang: str layer: "l4", entityId: sm.id, code: "MISSING_BLOCK_REF", - message: t(lang, "check.missingBlockRef.l4SmGuard", { smName: sm.name, from: tr.from, to: tr.to, guard: tr.guard }), + message: t(lang, "check.missingBlockRef.l4SmGuard", { + smName: sm.name, + from: tr.from, + to: tr.to, + guard: tr.guard, + }), }); } } @@ -351,7 +402,11 @@ function checkStepInternalRefs( layer: "l4", entityId: parent.id, code: "MISSING_STEP_REF", - message: t(lang, "check.missingStepRef.next", { parentName: parent.name, stepId: step.id, next: step.next }), + message: t(lang, "check.missingStepRef.next", { + parentName: parent.name, + stepId: step.id, + next: step.next, + }), }); } @@ -363,7 +418,11 @@ function checkStepInternalRefs( layer: "l4", entityId: parent.id, code: "MISSING_STEP_REF", - message: t(lang, "check.missingStepRef.branch", { parentName: parent.name, stepId: step.id, branchId }), + message: t(lang, "check.missingStepRef.branch", { + parentName: parent.name, + stepId: step.id, + branchId, + }), }); } } @@ -377,7 +436,11 @@ function checkStepInternalRefs( layer: "l4", entityId: parent.id, code: "MISSING_STEP_REF", - message: t(lang, "check.missingStepRef.wait", { parentName: parent.name, stepId: step.id, waitId }), + message: t(lang, "check.missingStepRef.wait", { + parentName: parent.name, + stepId: step.id, + waitId, + }), }); } } @@ -437,7 +500,13 @@ function checkDataFlowEndpoint( layer: "l4", entityId: flowId, code: "MISSING_PIN", - message: t(lang, "check.missingPin", { flowName, direction, endpoint, pinName, blockRef: step.blockRef }), + message: t(lang, "check.missingPin", { + flowName, + direction, + endpoint, + pinName, + blockRef: step.blockRef, + }), }); } } @@ -463,7 +532,12 @@ function checkEventGraphDataFlowEndpoint( layer: "l4", entityId: eg.id, code: "INVALID_DATAFLOW_FORMAT", - message: t(lang, "check.invalidDataFlowFormat.eventGraph", { egName: eg.name, handlerId: handler.id, direction, endpoint }), + message: t(lang, "check.invalidDataFlowFormat.eventGraph", { + egName: eg.name, + handlerId: handler.id, + direction, + endpoint, + }), }); return; } @@ -479,7 +553,12 @@ function checkEventGraphDataFlowEndpoint( layer: "l4", entityId: eg.id, code: "MISSING_STATE_REF", - message: t(lang, "check.missingStateRef", { egName: eg.name, handlerId: handler.id, direction, field }), + message: t(lang, "check.missingStateRef", { + egName: eg.name, + handlerId: handler.id, + direction, + field, + }), }); } return; @@ -522,7 +601,12 @@ function checkDrift(input: CheckInput, lang: string): CheckIssue[] { layer: "l2", entityId: cb.id, code: "SOURCE_DRIFT", - message: t(lang, "check.sourceDrift", { id: cb.id, sourceHash: cb.sourceHash, blockRef: cb.blockRef, l3Hash }), + message: t(lang, "check.sourceDrift", { + id: cb.id, + sourceHash: cb.sourceHash, + blockRef: cb.blockRef, + l3Hash, + }), }); } @@ -651,7 +735,10 @@ function checkStateMachineStructure(sm: L4StateMachine, lang: string): CheckIssu layer: "l4", entityId: sm.id, code: "INVALID_INITIAL_STATE", - message: t(lang, "check.invalidInitialState", { smName: sm.name, initialState: sm.initialState }), + message: t(lang, "check.invalidInitialState", { + smName: sm.name, + initialState: sm.initialState, + }), }); } @@ -700,7 +787,11 @@ function checkStateMachineStructure(sm: L4StateMachine, lang: string): CheckIssu layer: "l4", entityId: sm.id, code: "UNREACHABLE_STATE", - message: t(lang, "check.unreachableState", { smName: sm.name, stateName, initialState: sm.initialState }), + message: t(lang, "check.unreachableState", { + smName: sm.name, + stateName, + initialState: sm.initialState, + }), }); } } diff --git a/packages/core/compile-plan.test.ts b/packages/core/compile-plan.test.ts index fee12b1..7f41080 100644 --- a/packages/core/compile-plan.test.ts +++ b/packages/core/compile-plan.test.ts @@ -683,15 +683,12 @@ describe("compilePlan — language parameter", () => { const l3 = makeL3("validate", "Validate"); const l4 = makeL4("flow-a", ["validate"]); - const plan = compilePlan( - { l4Flows: [l4], l3Blocks: [l3], l2Blocks: [] }, - "zh", - ); + const plan = compilePlan({ l4Flows: [l4], l3Blocks: [l3], l2Blocks: [] }, "zh"); expect(plan.summary.compile).toBe(1); const task = plan.tasks[0]; // Chinese locale: reason should contain Chinese characters - expect(task.reason).toMatch(/[\u4e00-\u9fff]/); + expect(task.reason).toMatch(/[\u4E00-\u9FFF]/); }); }); diff --git a/packages/core/compile-plan.ts b/packages/core/compile-plan.ts index a349a91..a18f077 100644 --- a/packages/core/compile-plan.ts +++ b/packages/core/compile-plan.ts @@ -1,4 +1,4 @@ -// svp compile-plan — 变更检测 + 重编译任务生成 +// forge compile-plan — 变更检测 + 重编译任务生成 // 基于 check 的漂移检测,生成结构化的任务清单给 AI subagent // 纯函数,不做 IO @@ -45,12 +45,14 @@ export interface CompilePlan { /** Default complexity for a given task action */ export function getDefaultComplexity(action: TaskAction): Complexity { switch (action) { - case "update-ref": + case "update-ref": { return "light"; + } case "compile": case "recompile": - case "review": + case "review": { return "standard"; + } } } @@ -198,9 +200,15 @@ function detectBrokenRefs(input: CheckInput, lang: string): CompileTask[] { const flow = input.l4Flows.find((f) => f.id === issue.entityId); if (flow === undefined) continue; - const context: ContextRef[] = [{ layer: "l4", id: flow.id, label: t(lang, "compilePlan.label.l4Flow", { name: flow.name }) }]; + const context: ContextRef[] = [ + { layer: "l4", id: flow.id, label: t(lang, "compilePlan.label.l4Flow", { name: flow.name }) }, + ]; if (input.l5 !== undefined) { - context.push({ layer: "l5", id: input.l5.id, label: t(lang, "compilePlan.label.l5Blueprint") }); + context.push({ + layer: "l5", + id: input.l5.id, + label: t(lang, "compilePlan.label.l5Blueprint"), + }); } tasks.push({ @@ -226,7 +234,9 @@ function detectBrokenRefs(input: CheckInput, lang: string): CompileTask[] { targetId: issue.entityId, reason: t(lang, "compilePlan.reason.missingL2BlockRef"), issueCode: "MISSING_BLOCK_REF", - context: [{ layer: "l2", id: issue.entityId, label: t(lang, "compilePlan.label.orphanedL2") }], + context: [ + { layer: "l2", id: issue.entityId, label: t(lang, "compilePlan.label.orphanedL2") }, + ], complexity: "standard", }); } @@ -238,7 +248,11 @@ function detectBrokenRefs(input: CheckInput, lang: string): CompileTask[] { function buildL3Context(block: L3Block, input: CheckInput, lang: string): ContextRef[] { const context: ContextRef[] = [ - { layer: "l3", id: block.id, label: t(lang, "compilePlan.label.l3Contract", { name: block.name }) }, + { + layer: "l3", + id: block.id, + label: t(lang, "compilePlan.label.l3Contract", { name: block.name }), + }, ]; // 找到引用此 block 的 L4 artifact diff --git a/packages/core/i18n.test.ts b/packages/core/i18n.test.ts index 6ec4834..dbb6faa 100644 --- a/packages/core/i18n.test.ts +++ b/packages/core/i18n.test.ts @@ -68,7 +68,7 @@ describe("t()", () => { describe("getLanguage()", () => { it("returns 'en' when no L5 provided", () => { - expect(getLanguage(undefined)).toBe("en"); + expect(getLanguage()).toBe("en"); }); it("returns L5.language when present", () => { @@ -77,7 +77,7 @@ describe("getLanguage()", () => { }); it("returns 'en' when L5.language is undefined", () => { - const l5 = makeL5(undefined); + const l5 = makeL5(); expect(getLanguage(l5)).toBe("en"); }); }); diff --git a/packages/core/i18n.ts b/packages/core/i18n.ts index 825d810..77a22e4 100644 --- a/packages/core/i18n.ts +++ b/packages/core/i18n.ts @@ -10,7 +10,8 @@ type MessageParams = Record; /** 查找翻译,英文兜底,支持 {param} 插值 */ export function t(lang: string, key: string, params?: MessageParams): string { const catalog = messages[lang] ?? messages.en; - let template = catalog[key] ?? messages.en[key] ?? key; + let template = + (catalog[key] as string | undefined) ?? (messages.en[key] as string | undefined) ?? key; if (params !== undefined) { for (const [k, v] of Object.entries(params)) { template = template.replaceAll(`{${k}}`, String(v)); @@ -21,13 +22,13 @@ export function t(lang: string, key: string, params?: MessageParams): string { /** 检测系统语言(取 LANG/LC_ALL 前两位,如 "zh_CN.UTF-8" → "zh") */ export function detectSystemLanguage(): string { - const raw = (typeof process !== "undefined" && (process.env.LC_ALL || process.env.LANG)) || ""; - const match = raw.match(/^([a-z]{2})/i); + const raw = typeof process === "undefined" ? "" : (process.env.LC_ALL ?? process.env.LANG ?? ""); + const match = /^([a-z]{2})/i.exec(raw); return match ? match[1].toLowerCase() : "en"; } /** 从 L5 提取语言,缺省检测系统语言 */ -export function getLanguage(l5?: Pick | undefined): string { +export function getLanguage(l5?: Pick): string { return l5?.language ?? detectSystemLanguage(); } @@ -58,14 +59,12 @@ export function languageDirective(lang: string): string { const messages: Record> = { en: { // ── check.* ── - "check.hashMismatch.l5": - "L5 contentHash mismatch: stored={stored}, computed={computed}", + "check.hashMismatch.l5": "L5 contentHash mismatch: stored={stored}, computed={computed}", "check.hashMismatch.l4": 'L4 "{name}" contentHash mismatch: stored={stored}, computed={computed}', "check.hashMismatch.l3": 'L3 "{name}" contentHash mismatch: stored={stored}, computed={computed}', - "check.hashMismatch.l2": - 'L2 "{id}" contentHash mismatch: stored={stored}, computed={computed}', + "check.hashMismatch.l2": 'L2 "{id}" contentHash mismatch: stored={stored}, computed={computed}', "check.missingBlockRef.l4FlowStep": 'L4 "{flowName}" step "{stepId}" references non-existent L3 block "{blockRef}"', "check.missingFlowRef": @@ -78,8 +77,7 @@ const messages: Record> = { 'L4 "{smName}" state "{stateName}" onExit references non-existent L3 block "{blockRef}"', "check.missingBlockRef.l4SmGuard": 'L4 "{smName}" transition "{from}" → "{to}" guard references non-existent L3 block "{guard}"', - "check.missingBlockRef.l2": - 'L2 "{id}" references non-existent L3 block "{blockRef}"', + "check.missingBlockRef.l2": 'L2 "{id}" references non-existent L3 block "{blockRef}"', "check.missingStepRef.next": 'L4 "{parentName}" step "{stepId}" next references non-existent step "{next}"', "check.missingStepRef.branch": @@ -102,22 +100,17 @@ const messages: Record> = { 'L2 "{id}" signatureHash mismatch: L1 exported signatures have changed since last sync', "check.selfReferencingFlow": 'L4 "{flowName}" step "{stepId}" calls itself (recursive flow reference)', - "check.duplicateEvent": - 'L4 "{egName}" has duplicate event handler for "{event}"', - "check.emptyState": - 'L4 "{egName}" event-graph has no state declarations', - "check.invalidInitialState": - 'L4 "{smName}" initialState "{initialState}" not found in states', + "check.duplicateEvent": 'L4 "{egName}" has duplicate event handler for "{event}"', + "check.emptyState": 'L4 "{egName}" event-graph has no state declarations', + "check.invalidInitialState": 'L4 "{smName}" initialState "{initialState}" not found in states', "check.invalidTransition.from": 'L4 "{smName}" transition from "{from}" references non-existent state', "check.invalidTransition.to": 'L4 "{smName}" transition to "{to}" references non-existent state', "check.unreachableState": 'L4 "{smName}" state "{stateName}" is not reachable from initialState "{initialState}"', - "check.nextCycle": - 'L4 "{entityName}" has a cycle in next chain involving step "{current}"', - "check.orphanStep": - 'L4 "{entityName}" step "{stepId}" is not reachable from the first step', + "check.nextCycle": 'L4 "{entityName}" has a cycle in next chain involving step "{current}"', + "check.orphanStep": 'L4 "{entityName}" step "{stepId}" is not reachable from the first step', "check.missingLanguage": "L5 blueprint has no language field — consider adding language preference", @@ -179,45 +172,35 @@ const messages: Record> = { // ── cli.* ── "cli.init.alreadyExists": - ".svp/ directory already exists. Use `svp check` to validate or `svp view` to inspect.", + ".svp/ directory already exists. Use `forge check` to validate or `forge view` to inspect.", "cli.init.initialized": "Initialized .svp/ in {root}", "cli.init.dirStructure": "Directory structure:", - "cli.init.nextEdit": - "Next: edit .svp/l5.json to add domains, constraints, and integrations.", - "cli.init.nextSvp": "Next: use /svp to start the interactive SVP workflow.", - "cli.init.slashCommands": - "{host}: {count} skill files → {skillDir}/", + "cli.init.nextEdit": "Next: edit .svp/l5.json to add domains, constraints, and integrations.", + "cli.init.nextSvp": "Next: use /forge to start the interactive SVP workflow.", + "cli.init.slashCommands": "{host}: {count} skill files → {skillDir}/", "cli.init.claudeMdSection": "{host}: SVP section → {contextFile}", - "cli.init.claudeMdSkipped": - "{host}: {contextFile} already contains SVP section (skipped)", - "cli.error.loadFailed": - 'Error: cannot load .svp/ data from "{root}". Run `svp init` first.', + "cli.init.claudeMdSkipped": "{host}: {contextFile} already contains SVP section (skipped)", + "cli.error.loadFailed": 'Error: cannot load .svp/ data from "{root}". Run `forge init` first.', "cli.error.l3NotFound": 'Error: L3 block "{id}" not found in .svp/l3/', "cli.error.l4NotFound": 'Error: L4 flow "{id}" not found in .svp/l4/', "cli.error.l5NotFound": - "Error: L5 blueprint not found. Design L5 first with `svp prompt design-l5`.", + "Error: L5 blueprint not found. Design L5 first with `forge prompt design-l5`.", "cli.error.invalidKind": 'Error: invalid --kind "{kind}". Must be flow, event-graph, or state-machine.', - "cli.error.stepOutOfRange": - "Error: step index {step} is out of range (0-{max})", + "cli.error.stepOutOfRange": "Error: step index {step} is out of range (0-{max})", "cli.error.blockNotFound": 'Error: block "{blockId}" not found in L4 artifact "{flow}" (kind: {kind})', }, zh: { // ── check.* ── - "check.hashMismatch.l5": - "L5 contentHash 不匹配:存储值={stored},计算值={computed}", - "check.hashMismatch.l4": - 'L4 "{name}" contentHash 不匹配:存储值={stored},计算值={computed}', - "check.hashMismatch.l3": - 'L3 "{name}" contentHash 不匹配:存储值={stored},计算值={computed}', - "check.hashMismatch.l2": - 'L2 "{id}" contentHash 不匹配:存储值={stored},计算值={computed}', + "check.hashMismatch.l5": "L5 contentHash 不匹配:存储值={stored},计算值={computed}", + "check.hashMismatch.l4": 'L4 "{name}" contentHash 不匹配:存储值={stored},计算值={computed}', + "check.hashMismatch.l3": 'L3 "{name}" contentHash 不匹配:存储值={stored},计算值={computed}', + "check.hashMismatch.l2": 'L2 "{id}" contentHash 不匹配:存储值={stored},计算值={computed}', "check.missingBlockRef.l4FlowStep": 'L4 "{flowName}" 步骤 "{stepId}" 引用了不存在的 L3 block "{blockRef}"', - "check.missingFlowRef": - 'L4 "{flowName}" 步骤 "{stepId}" 引用了不存在的 L4 flow "{flowRef}"', + "check.missingFlowRef": 'L4 "{flowName}" 步骤 "{stepId}" 引用了不存在的 L4 flow "{flowRef}"', "check.missingBlockRef.l4EventGraphStep": 'L4 "{egName}" 处理器 "{handlerId}" 步骤 "{stepId}" 引用了不存在的 L3 block "{blockRef}"', "check.missingBlockRef.l4SmOnEntry": @@ -226,8 +209,7 @@ const messages: Record> = { 'L4 "{smName}" 状态 "{stateName}" 的 onExit 引用了不存在的 L3 block "{blockRef}"', "check.missingBlockRef.l4SmGuard": 'L4 "{smName}" 转换 "{from}" → "{to}" 的 guard 引用了不存在的 L3 block "{guard}"', - "check.missingBlockRef.l2": - 'L2 "{id}" 引用了不存在的 L3 block "{blockRef}"', + "check.missingBlockRef.l2": 'L2 "{id}" 引用了不存在的 L3 block "{blockRef}"', "check.missingStepRef.next": 'L4 "{parentName}" 步骤 "{stepId}" 的 next 引用了不存在的步骤 "{next}"', "check.missingStepRef.branch": @@ -246,38 +228,24 @@ const messages: Record> = { 'L4 "{egName}" 处理器 "{handlerId}" dataFlow {direction} 引用了未声明的 state key "{field}"', "check.sourceDrift": 'L2 "{id}" 的 sourceHash ({sourceHash}) 与 L3 "{blockRef}" 的 contentHash ({l3Hash}) 不匹配:L3 自上次编译以来已变更', - "check.contentDrift": - 'L2 "{id}" signatureHash 不匹配:L1 导出签名自上次同步以来已变更', - "check.selfReferencingFlow": - 'L4 "{flowName}" 步骤 "{stepId}" 调用了自身(递归 flow 引用)', - "check.duplicateEvent": - 'L4 "{egName}" 存在重复的事件处理器 "{event}"', - "check.emptyState": - 'L4 "{egName}" event-graph 没有 state 声明', - "check.invalidInitialState": - 'L4 "{smName}" 的 initialState "{initialState}" 不在 states 中', - "check.invalidTransition.from": - 'L4 "{smName}" 转换的 from "{from}" 引用了不存在的状态', - "check.invalidTransition.to": - 'L4 "{smName}" 转换的 to "{to}" 引用了不存在的状态', + "check.contentDrift": 'L2 "{id}" signatureHash 不匹配:L1 导出签名自上次同步以来已变更', + "check.selfReferencingFlow": 'L4 "{flowName}" 步骤 "{stepId}" 调用了自身(递归 flow 引用)', + "check.duplicateEvent": 'L4 "{egName}" 存在重复的事件处理器 "{event}"', + "check.emptyState": 'L4 "{egName}" event-graph 没有 state 声明', + "check.invalidInitialState": 'L4 "{smName}" 的 initialState "{initialState}" 不在 states 中', + "check.invalidTransition.from": 'L4 "{smName}" 转换的 from "{from}" 引用了不存在的状态', + "check.invalidTransition.to": 'L4 "{smName}" 转换的 to "{to}" 引用了不存在的状态', "check.unreachableState": 'L4 "{smName}" 状态 "{stateName}" 从 initialState "{initialState}" 不可达', - "check.nextCycle": - 'L4 "{entityName}" 在 next 链中存在环,涉及步骤 "{current}"', - "check.orphanStep": - 'L4 "{entityName}" 步骤 "{stepId}" 从第一个步骤不可达', - "check.missingLanguage": - "L5 blueprint 未设置 language 字段 — 建议添加语言偏好", + "check.nextCycle": 'L4 "{entityName}" 在 next 链中存在环,涉及步骤 "{current}"', + "check.orphanStep": 'L4 "{entityName}" 步骤 "{stepId}" 从第一个步骤不可达', + "check.missingLanguage": "L5 blueprint 未设置 language 字段 — 建议添加语言偏好", // ── compilePlan.* ── - "compilePlan.reason.missingL2": - 'L3 block "{name}" 没有对应的 L2 code block — 需要初始编译', - "compilePlan.reason.sourceDrift": - "L3 契约自上次编译以来已变更 — L2 代码已过时", - "compilePlan.reason.contentDrift": - "L1 导出签名已变更 — 请审查 L3 契约是否仍与代码匹配", - "compilePlan.reason.missingBlockRef": - "Flow 引用了缺失的 L3 block — 需要更新步骤或重建 L3", + "compilePlan.reason.missingL2": 'L3 block "{name}" 没有对应的 L2 code block — 需要初始编译', + "compilePlan.reason.sourceDrift": "L3 契约自上次编译以来已变更 — L2 代码已过时", + "compilePlan.reason.contentDrift": "L1 导出签名已变更 — 请审查 L3 契约是否仍与代码匹配", + "compilePlan.reason.missingBlockRef": "Flow 引用了缺失的 L3 block — 需要更新步骤或重建 L3", "compilePlan.reason.missingL2BlockRef": "L2 code block 引用了缺失的 L3 block — 孤立代码需要审查", "compilePlan.label.l3Contract": 'L3 契约 "{name}"', @@ -326,28 +294,22 @@ const messages: Record> = { "view.common.l5Ref": "L5", // ── cli.* ── - "cli.init.alreadyExists": - ".svp/ 目录已存在。使用 `svp check` 校验或 `svp view` 查看。", + "cli.init.alreadyExists": ".svp/ 目录已存在。使用 `forge check` 校验或 `forge view` 查看。", "cli.init.initialized": "已在 {root} 初始化 .svp/", "cli.init.dirStructure": "目录结构:", - "cli.init.nextEdit": - "下一步:编辑 .svp/l5.json 添加领域、约束和集成。", - "cli.init.nextSvp": "下一步:使用 /svp 启动交互式 SVP 工作流。", - "cli.init.slashCommands": - "{host}: {count} 个 skill 文件 → {skillDir}/", + "cli.init.nextEdit": "下一步:编辑 .svp/l5.json 添加领域、约束和集成。", + "cli.init.nextSvp": "下一步:使用 /forge 启动交互式 SVP 工作流。", + "cli.init.slashCommands": "{host}: {count} 个 skill 文件 → {skillDir}/", "cli.init.claudeMdSection": "{host}: SVP 部分 → {contextFile}", - "cli.init.claudeMdSkipped": - "{host}: {contextFile} 已包含 SVP 部分(已跳过)", - "cli.error.loadFailed": - '错误:无法从 "{root}" 加载 .svp/ 数据。请先运行 `svp init`。', + "cli.init.claudeMdSkipped": "{host}: {contextFile} 已包含 SVP 部分(已跳过)", + "cli.error.loadFailed": '错误:无法从 "{root}" 加载 .svp/ 数据。请先运行 `forge init`。', "cli.error.l3NotFound": '错误:在 .svp/l3/ 中未找到 L3 block "{id}"', "cli.error.l4NotFound": '错误:在 .svp/l4/ 中未找到 L4 flow "{id}"', "cli.error.l5NotFound": - "错误:未找到 L5 blueprint。请先使用 `svp prompt design-l5` 设计 L5。", + "错误:未找到 L5 blueprint。请先使用 `forge prompt design-l5` 设计 L5。", "cli.error.invalidKind": '错误:无效的 --kind "{kind}"。必须为 flow、event-graph 或 state-machine。', - "cli.error.stepOutOfRange": - "错误:步骤索引 {step} 超出范围 (0-{max})", + "cli.error.stepOutOfRange": "错误:步骤索引 {step} 超出范围 (0-{max})", "cli.error.blockNotFound": '错误:在 L4 制品 "{flow}" (类型: {kind}) 中未找到 block "{blockId}"', }, diff --git a/packages/core/index.ts b/packages/core/index.ts index 3570d4c..ae898f5 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -44,7 +44,13 @@ export { } from "./store.js"; export type { CheckIssue, CheckReport, CheckInput, IssueSeverity } from "./check.js"; export { check } from "./check.js"; -export type { CompileTask, CompilePlan, ContextRef, TaskAction, Complexity } from "./compile-plan.js"; +export type { + CompileTask, + CompilePlan, + ContextRef, + TaskAction, + Complexity, +} from "./compile-plan.js"; export { getDefaultComplexity } from "./compile-plan.js"; export type { ExportedSymbol, diff --git a/packages/core/l3.ts b/packages/core/l3.ts index 08c4581..9d7768c 100644 --- a/packages/core/l3.ts +++ b/packages/core/l3.ts @@ -7,7 +7,7 @@ export interface L3Block { readonly id: string; readonly name: string; - // 拓扑骨架(结构化,svp check 校验类型匹配和引用有效性) + // 拓扑骨架(结构化,forge check 校验类型匹配和引用有效性) readonly input: readonly Pin[]; readonly output: readonly Pin[]; diff --git a/packages/core/view.ts b/packages/core/view.ts index 6c812a5..c60c8d3 100644 --- a/packages/core/view.ts +++ b/packages/core/view.ts @@ -1,4 +1,4 @@ -// svp view — 虚拟文件树渲染 +// forge view — 虚拟文件树渲染 // 把五层数据模型渲染为 AI 友好的文本视图 // 纯函数,不做 IO,方便测试和复用 @@ -37,7 +37,10 @@ export function viewL5Overview(l5: L5Blueprint, language = "en"): string { } if (l5.integrations.length > 0) { - lines.push("", `${t(lang, "view.l5.integrations", { count: String(l5.integrations.length) })}:`); + lines.push( + "", + `${t(lang, "view.l5.integrations", { count: String(l5.integrations.length) })}:`, + ); for (const i of l5.integrations) { lines.push(` ${i.name} [${i.type}]`); } @@ -50,7 +53,10 @@ export function viewL5Overview(l5: L5Blueprint, language = "en"): string { export function viewL4Overview(flows: readonly L4Artifact[], language = "en"): string { const lang = language; - const lines: string[] = [t(lang, "view.l4.title", { count: String(flows.length) }), "─".repeat(30)]; + const lines: string[] = [ + t(lang, "view.l4.title", { count: String(flows.length) }), + "─".repeat(30), + ]; for (const l4 of flows) { const kind = getL4Kind(l4); @@ -100,7 +106,7 @@ export function viewL4Detail( // ── Flow overview/detail ── -function viewFlowOverviewLines(flow: L4Flow, lang: string): string[] { +function viewFlowOverviewLines(flow: L4Flow, _lang: string): string[] { const lines: string[] = []; const trigger = formatTrigger(flow); lines.push(`${flow.id} [flow]${trigger}`); @@ -117,8 +123,17 @@ function viewFlowOverviewLines(flow: L4Flow, lang: string): string[] { return lines; } -function viewFlowDetail(flow: L4Flow, l3Blocks: readonly L3Block[], l5?: L5Blueprint, lang = "en"): string { - const lines: string[] = [flow.id, "═".repeat(Math.max(flow.id.length, 12)), t(lang, "view.l4.flow.kind")]; +function viewFlowDetail( + flow: L4Flow, + l3Blocks: readonly L3Block[], + l5?: L5Blueprint, + lang = "en", +): string { + const lines: string[] = [ + flow.id, + "═".repeat(Math.max(flow.id.length, 12)), + t(lang, "view.l4.flow.kind"), + ]; const trigger = formatTrigger(flow); if (trigger.length > 0) { @@ -163,7 +178,7 @@ function viewFlowDetail(flow: L4Flow, l3Blocks: readonly L3Block[], l5?: L5Bluep // ── EventGraph overview/detail ── -function viewEventGraphOverviewLines(eg: L4EventGraph, lang: string): string[] { +function viewEventGraphOverviewLines(eg: L4EventGraph, _lang: string): string[] { const lines: string[] = []; const stateCount = Object.keys(eg.state).length; const handlerCount = eg.handlers.length; @@ -185,19 +200,29 @@ function viewEventGraphDetail( l5?: L5Blueprint, lang = "en", ): string { - const lines: string[] = [eg.id, "═".repeat(Math.max(eg.id.length, 12)), t(lang, "view.l4.eventGraph.kind")]; + const lines: string[] = [ + eg.id, + "═".repeat(Math.max(eg.id.length, 12)), + t(lang, "view.l4.eventGraph.kind"), + ]; // state declarations const stateEntries = Object.entries(eg.state); if (stateEntries.length > 0) { - lines.push("", `${t(lang, "view.l4.eventGraph.state", { count: String(stateEntries.length) })}:`); + lines.push( + "", + `${t(lang, "view.l4.eventGraph.state", { count: String(stateEntries.length) })}:`, + ); for (const [key, field] of stateEntries) { lines.push(` ${key}: ${field.type} — ${field.description}`); } } // handlers - lines.push("", `${t(lang, "view.l4.eventGraph.handlers", { count: String(eg.handlers.length) })}:`); + lines.push( + "", + `${t(lang, "view.l4.eventGraph.handlers", { count: String(eg.handlers.length) })}:`, + ); for (const handler of eg.handlers) { lines.push(` [${handler.id}] on "${handler.event}" (${String(handler.steps.length)} steps)`); for (const step of handler.steps) { @@ -240,7 +265,7 @@ function viewEventGraphDetail( // ── StateMachine overview/detail ── -function viewStateMachineOverviewLines(sm: L4StateMachine, lang: string): string[] { +function viewStateMachineOverviewLines(sm: L4StateMachine, _lang: string): string[] { const stateCount = Object.keys(sm.states).length; const transCount = sm.transitions.length; return [ @@ -264,7 +289,10 @@ function viewStateMachineDetail( // states const stateEntries = Object.entries(sm.states); - lines.push("", `${t(lang, "view.l4.stateMachine.states", { count: String(stateEntries.length) })}:`); + lines.push( + "", + `${t(lang, "view.l4.stateMachine.states", { count: String(stateEntries.length) })}:`, + ); for (const [name, config] of stateEntries) { const parts: string[] = [name]; if (config.onEntry !== undefined) parts.push(`onEntry → ${config.onEntry.blockRef}`); @@ -273,7 +301,10 @@ function viewStateMachineDetail( } // transitions - lines.push("", `${t(lang, "view.l4.stateMachine.transitions", { count: String(sm.transitions.length) })}:`); + lines.push( + "", + `${t(lang, "view.l4.stateMachine.transitions", { count: String(sm.transitions.length) })}:`, + ); for (const tr of sm.transitions) { const guard = tr.guard === undefined ? "" : ` [guard: ${tr.guard}]`; lines.push(` ${tr.from} → ${tr.to} on "${tr.event}"${guard}`); @@ -313,7 +344,10 @@ function viewStateMachineDetail( export function viewL3Overview(blocks: readonly L3Block[], language = "en"): string { const lang = language; - const lines: string[] = [t(lang, "view.l3.title", { count: String(blocks.length) }), "─".repeat(30)]; + const lines: string[] = [ + t(lang, "view.l3.title", { count: String(blocks.length) }), + "─".repeat(30), + ]; const maxIdLength = Math.max(...blocks.map((b) => b.id.length), 0); @@ -392,7 +426,10 @@ export function viewL3Detail( const l2 = l2Blocks.find((cb) => cb.blockRef === block.id); if (l2 !== undefined) { - const synced = l2.sourceHash === block.contentHash ? t(lang, "view.common.synced") : t(lang, "view.common.drift"); + const synced = + l2.sourceHash === block.contentHash + ? t(lang, "view.common.synced") + : t(lang, "view.common.drift"); lines.push(`↓ L2: ${l2.id} [${synced}]`); } @@ -407,13 +444,19 @@ export function viewL2Overview( language = "en", ): string { const lang = language; - const lines: string[] = [t(lang, "view.l2.title", { count: String(l2Blocks.length) }), "─".repeat(30)]; + const lines: string[] = [ + t(lang, "view.l2.title", { count: String(l2Blocks.length) }), + "─".repeat(30), + ]; const l3Map = new Map(l3Blocks.map((b) => [b.id, b])); for (const cb of l2Blocks) { const l3 = l3Map.get(cb.blockRef); - const synced = cb.sourceHash === l3?.contentHash ? t(lang, "view.common.syncedShort") : t(lang, "view.common.driftShort"); + const synced = + cb.sourceHash === l3?.contentHash + ? t(lang, "view.common.syncedShort") + : t(lang, "view.common.driftShort"); const files = cb.files.length === 1 ? cb.files[0] : `${String(cb.files.length)} files`; lines.push(`${cb.id} [${cb.language}] ${files} (${synced})`); } @@ -421,10 +464,17 @@ export function viewL2Overview( return lines.join("\n"); } -export function viewL2Detail(cb: L2CodeBlock, l3Blocks: readonly L3Block[], language = "en"): string { +export function viewL2Detail( + cb: L2CodeBlock, + l3Blocks: readonly L3Block[], + language = "en", +): string { const lang = language; const l3 = l3Blocks.find((b) => b.id === cb.blockRef); - const synced = cb.sourceHash === l3?.contentHash ? t(lang, "view.common.synced") : t(lang, "view.common.drift"); + const synced = + cb.sourceHash === l3?.contentHash + ? t(lang, "view.common.synced") + : t(lang, "view.common.drift"); const lines: string[] = [ cb.id, diff --git a/packages/skills/__tests__/adapters.test.ts b/packages/skills/__tests__/adapters.test.ts index e989f0c..eee7dd3 100644 --- a/packages/skills/__tests__/adapters.test.ts +++ b/packages/skills/__tests__/adapters.test.ts @@ -4,8 +4,8 @@ import { mkdir, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import path from "node:path"; import { describe, it, expect, beforeEach, afterEach } from "vitest"; -import { getAdapter, getAllAdapterIds } from "../adapters/index.js"; import { detectHost, detectHosts } from "../adapters/detect.js"; +import { getAdapter, getAllAdapterIds } from "../adapters/index.js"; import type { HostId } from "../adapters/types.js"; // ── Registry tests ── @@ -41,7 +41,7 @@ describe("adapter interface conformance", () => { const allIds = getAllAdapterIds(); for (const hostId of allIds) { - describe(`${hostId}`, () => { + describe(hostId, () => { const adapter = getAdapter(hostId); it("produces non-empty skill files (en)", () => { @@ -103,24 +103,24 @@ describe("adapter interface conformance", () => { // ── Workflow content consistency tests ── +/** Extract the workflow section (everything after the last "---" separator) */ +function extractWorkflow(content: string): string { + const parts = content.split("\n---\n"); + return parts.at(-1)!.trim(); +} + describe("adapter workflow content consistency", () => { const allIds = getAllAdapterIds(); - /** Extract the workflow section (everything after the last "---" separator) */ - function extractWorkflow(content: string): string { - const parts = content.split("\n---\n"); - return parts[parts.length - 1]!.trim(); - } - for (const lang of ["en", "zh"] as const) { it(`all adapters share identical workflow content (${lang})`, () => { - const reference = getAdapter(allIds[0]!).generateSkillFiles(lang)[0]!.content; + const reference = getAdapter(allIds[0]).generateSkillFiles(lang)[0].content; const refWorkflow = extractWorkflow(reference); for (const id of allIds.slice(1)) { - const content = getAdapter(id).generateSkillFiles(lang)[0]!.content; + const content = getAdapter(id).generateSkillFiles(lang)[0].content; const workflow = extractWorkflow(content); - expect(workflow, `${id} (${lang}) workflow differs from ${allIds[0]!}`).toBe(refWorkflow); + expect(workflow, `${id} (${lang}) workflow differs from ${allIds[0]}`).toBe(refWorkflow); } }); } diff --git a/packages/skills/__tests__/design-prompts.test.ts b/packages/skills/__tests__/design-prompts.test.ts index 6acb6ba..cfc7370 100644 --- a/packages/skills/__tests__/design-prompts.test.ts +++ b/packages/skills/__tests__/design-prompts.test.ts @@ -1,11 +1,11 @@ import { describe, expect, it } from "vitest"; +import { findBlockContext } from "../../core/l4.js"; import { buildDesignL3Prompt } from "../prompts/design-l3.js"; import { buildDesignL4EventGraphPrompt } from "../prompts/design-l4-event-graph.js"; import { buildDesignL4StateMachinePrompt } from "../prompts/design-l4-state-machine.js"; import { buildDesignL4Prompt } from "../prompts/design-l4.js"; import type { L3Block } from "../../core/l3.js"; import type { L4EventGraph, L4Flow, L4StateMachine } from "../../core/l4.js"; -import { findBlockContext } from "../../core/l4.js"; import type { L5Blueprint } from "../../core/l5.js"; import type { DesignL3Input } from "../prompts/design-l3.js"; import type { DesignL4EventGraphInput } from "../prompts/design-l4-event-graph.js"; diff --git a/packages/skills/__tests__/prompt-builder.test.ts b/packages/skills/__tests__/prompt-builder.test.ts index 308134f..c43e88d 100644 --- a/packages/skills/__tests__/prompt-builder.test.ts +++ b/packages/skills/__tests__/prompt-builder.test.ts @@ -100,7 +100,7 @@ describe("buildPrompt", () => { expect(prompt.task).toContain("compile"); expect(prompt.input).toContain("validate-order"); expect(prompt.input).toContain("OrderRequest"); - expect(prompt.outputSpec).toContain("svp link"); + expect(prompt.outputSpec).toContain("forge link"); expect(prompt.rules).toContain("downward only"); }); diff --git a/packages/skills/adapters/claude-code.ts b/packages/skills/adapters/claude-code.ts index d56add2..920cd8e 100644 --- a/packages/skills/adapters/claude-code.ts +++ b/packages/skills/adapters/claude-code.ts @@ -1,12 +1,12 @@ // adapters/claude-code — Claude Code host adapter -import type { HostAdapter, SkillFile } from "./types.js"; import { buildSkillFileContent, generateContextBody, defaultSlashCommands, DEFAULT_CONTEXT_MARKER, } from "./shared.js"; +import type { HostAdapter, SkillFile } from "./types.js"; export const claudeCodeAdapter: HostAdapter = { id: "claude-code", diff --git a/packages/skills/adapters/codex.ts b/packages/skills/adapters/codex.ts index 19cb310..01e45bf 100644 --- a/packages/skills/adapters/codex.ts +++ b/packages/skills/adapters/codex.ts @@ -1,12 +1,12 @@ // adapters/codex — Codex CLI host adapter -import type { HostAdapter, SkillFile } from "./types.js"; import { buildSkillFileContent, generateContextBody, defaultSlashCommands, DEFAULT_CONTEXT_MARKER, } from "./shared.js"; +import type { HostAdapter, SkillFile } from "./types.js"; export const codexAdapter: HostAdapter = { id: "codex", @@ -19,7 +19,9 @@ export const codexAdapter: HostAdapter = { generateSkillFiles(language = "en"): readonly SkillFile[] { const modelTierLine = "heavy=o3 | standard=o4-mini | light=o4-mini"; - return [{ relativePath: "svp/SKILL.md", content: buildSkillFileContent(language, modelTierLine) }]; + return [ + { relativePath: "svp/SKILL.md", content: buildSkillFileContent(language, modelTierLine) }, + ]; }, contextFilePath() { diff --git a/packages/skills/adapters/cursor.ts b/packages/skills/adapters/cursor.ts index 7bfdfaa..617469f 100644 --- a/packages/skills/adapters/cursor.ts +++ b/packages/skills/adapters/cursor.ts @@ -1,6 +1,5 @@ // adapters/cursor — Cursor host adapter -import type { HostAdapter, SkillFile } from "./types.js"; import { buildSkillFileContent, genericModelTierLine, @@ -8,6 +7,7 @@ import { genericContextOptions, DEFAULT_CONTEXT_MARKER, } from "./shared.js"; +import type { HostAdapter, SkillFile } from "./types.js"; export const cursorAdapter: HostAdapter = { id: "cursor", @@ -18,7 +18,12 @@ export const cursorAdapter: HostAdapter = { }, generateSkillFiles(language = "en"): readonly SkillFile[] { - return [{ relativePath: "svp.md", content: buildSkillFileContent(language, genericModelTierLine(language)) }]; + return [ + { + relativePath: "svp.md", + content: buildSkillFileContent(language, genericModelTierLine(language)), + }, + ]; }, contextFilePath() { @@ -39,6 +44,8 @@ export const cursorAdapter: HostAdapter = { "", ].join("\n"); - return frontmatter + generateContextBody(projectName, language, genericContextOptions(language)); + return ( + frontmatter + generateContextBody(projectName, language, genericContextOptions(language)) + ); }, }; diff --git a/packages/skills/adapters/detect.ts b/packages/skills/adapters/detect.ts index f0c61e8..65ca734 100644 --- a/packages/skills/adapters/detect.ts +++ b/packages/skills/adapters/detect.ts @@ -6,7 +6,7 @@ import type { HostId } from "./types.js"; /** Marker paths mapped to their host IDs (checked in order). * `kind` determines whether to check for a directory or a file. */ -const HOST_MARKERS: readonly { marker: string; kind: "dir" | "file"; host: HostId }[] = [ +const HOST_MARKERS: ReadonlyArray<{ marker: string; kind: "dir" | "file"; host: HostId }> = [ { marker: ".claude", kind: "dir", host: "claude-code" }, { marker: ".cursor", kind: "dir", host: "cursor" }, { marker: ".windsurf", kind: "dir", host: "windsurf" }, diff --git a/packages/skills/adapters/github-copilot.ts b/packages/skills/adapters/github-copilot.ts index 21616dc..73c9df9 100644 --- a/packages/skills/adapters/github-copilot.ts +++ b/packages/skills/adapters/github-copilot.ts @@ -1,6 +1,5 @@ // adapters/github-copilot — GitHub Copilot host adapter -import type { HostAdapter, SkillFile } from "./types.js"; import { buildSkillFileContent, genericModelTierLine, @@ -8,6 +7,7 @@ import { genericContextOptions, DEFAULT_CONTEXT_MARKER, } from "./shared.js"; +import type { HostAdapter, SkillFile } from "./types.js"; export const githubCopilotAdapter: HostAdapter = { id: "github-copilot", @@ -18,7 +18,12 @@ export const githubCopilotAdapter: HostAdapter = { }, generateSkillFiles(language = "en"): readonly SkillFile[] { - return [{ relativePath: "svp.prompt.md", content: buildSkillFileContent(language, genericModelTierLine(language)) }]; + return [ + { + relativePath: "svp.prompt.md", + content: buildSkillFileContent(language, genericModelTierLine(language)), + }, + ]; }, contextFilePath() { diff --git a/packages/skills/adapters/index.ts b/packages/skills/adapters/index.ts index 95afee8..9f8ff06 100644 --- a/packages/skills/adapters/index.ts +++ b/packages/skills/adapters/index.ts @@ -3,13 +3,13 @@ export type { HostId, HostAdapter, SkillFile } from "./types.js"; export { detectHost, detectHosts } from "./detect.js"; -import type { HostId, HostAdapter } from "./types.js"; import { claudeCodeAdapter } from "./claude-code.js"; -import { kimiCodeAdapter } from "./kimi-code.js"; import { codexAdapter } from "./codex.js"; import { cursorAdapter } from "./cursor.js"; -import { windsurfAdapter } from "./windsurf.js"; import { githubCopilotAdapter } from "./github-copilot.js"; +import { kimiCodeAdapter } from "./kimi-code.js"; +import { windsurfAdapter } from "./windsurf.js"; +import type { HostId, HostAdapter } from "./types.js"; const adapters: Record = { "claude-code": claudeCodeAdapter, @@ -21,8 +21,8 @@ const adapters: Record = { }; export function getAdapter(host: HostId): HostAdapter { - const adapter = adapters[host]; - if (!adapter) { + const adapter = (adapters as Record)[host]; + if (adapter === undefined) { throw new Error(`Unknown host: ${host}`); } return adapter; diff --git a/packages/skills/adapters/kimi-code.ts b/packages/skills/adapters/kimi-code.ts index caa1d8c..2b05d97 100644 --- a/packages/skills/adapters/kimi-code.ts +++ b/packages/skills/adapters/kimi-code.ts @@ -1,6 +1,5 @@ // adapters/kimi-code — Kimi Code host adapter -import type { HostAdapter, SkillFile } from "./types.js"; import { buildSkillFileContent, genericModelTierLine, @@ -8,6 +7,7 @@ import { genericContextOptions, DEFAULT_CONTEXT_MARKER, } from "./shared.js"; +import type { HostAdapter, SkillFile } from "./types.js"; export const kimiCodeAdapter: HostAdapter = { id: "kimi-code", @@ -29,7 +29,12 @@ export const kimiCodeAdapter: HostAdapter = { "", ].join("\n"); - return [{ relativePath: "svp/SKILL.md", content: buildSkillFileContent(language, genericModelTierLine(language), frontmatter) }]; + return [ + { + relativePath: "svp/SKILL.md", + content: buildSkillFileContent(language, genericModelTierLine(language), frontmatter), + }, + ]; }, contextFilePath() { @@ -41,6 +46,10 @@ export const kimiCodeAdapter: HostAdapter = { }, generateContextSection(projectName: string, language = "en"): string { - return generateContextBody(projectName, language, genericContextOptions(language, "/skill:svp")); + return generateContextBody( + projectName, + language, + genericContextOptions(language, "/skill:svp"), + ); }, }; diff --git a/packages/skills/adapters/shared.ts b/packages/skills/adapters/shared.ts index bfdab6c..93d502d 100644 --- a/packages/skills/adapters/shared.ts +++ b/packages/skills/adapters/shared.ts @@ -16,14 +16,14 @@ export function getProtocolSection(language: string, modelTierLine: string): str if (language === "zh") { return `## 协议(一次性声明) -**Subagent 派发**:运行 \`svp prompt \` 获取提示词 → 读取 prompt 头部 complexity 字段 → 派发 subagent → 后续跑 toolchain 命令。 +**Subagent 派发**:运行 \`forge prompt \` 获取提示词 → 读取 prompt 头部 complexity 字段 → 派发 subagent → 后续跑 toolchain 命令。 **Complexity → 模型等级**:${modelTierLine} **通用规则**: - 严格单向向下,不回头修改上层 - 主 Agent 不读 L1 代码——上下文隔离是核心价值 -- JSON 中 contentHash 和 revision 写占位值,\`svp rehash\` 会修正 +- JSON 中 contentHash 和 revision 写占位值,\`forge rehash\` 会修正 - 尽量并行派发无依赖的 subagent - 做不到就报错,说清哪层什么问题——用户是反向反馈回路 - 如果 nodes//docs.md 存在,compile/recompile prompt 会自动包含文档内容`; @@ -31,14 +31,14 @@ export function getProtocolSection(language: string, modelTierLine: string): str return `## Protocol (one-time declaration) -**Subagent dispatch**: Run \`svp prompt \` to get the prompt → read the complexity field in the prompt header → dispatch subagent → then run toolchain commands. +**Subagent dispatch**: Run \`forge prompt \` to get the prompt → read the complexity field in the prompt header → dispatch subagent → then run toolchain commands. **Complexity → Model tier**: ${modelTierLine} **General rules**: - Strictly top-down only, never modify upper layers - Main Agent does not read L1 code — context isolation is the core value -- Write placeholder values for contentHash and revision in JSON; \`svp rehash\` will fix them +- Write placeholder values for contentHash and revision in JSON; \`forge rehash\` will fix them - Dispatch independent subagents in parallel when possible - Report errors when unable to proceed, clearly stating which layer and what the issue is — the user is the reverse feedback loop - If nodes//docs.md exists, compile/recompile prompts will automatically include its content`; @@ -69,7 +69,7 @@ export function buildSkillFileContent( "", getWorkflowContent(language), ].join("\n"); - return frontmatter ? frontmatter + body : body; + return frontmatter !== undefined && frontmatter.length > 0 ? frontmatter + body : body; } // ── Shared defaults (used by most adapters) ── @@ -94,7 +94,7 @@ export function genericModelTierLine(language: string): string { : "heavy=strongest | standard=balanced | light=fastest"; } -export function defaultSlashCommands(language: string, command = "/svp"): SlashCommandEntry[] { +export function defaultSlashCommands(language: string, command = "/forge"): SlashCommandEntry[] { return [ { command, @@ -106,7 +106,7 @@ export function defaultSlashCommands(language: string, command = "/svp"): SlashC ]; } -export function genericContextOptions(language: string, command = "/svp"): ContextOptions { +export function genericContextOptions(language: string, command = "/forge"): ContextOptions { return { modelTierRows: language === "zh" ? GENERIC_MODEL_TIERS_ZH : GENERIC_MODEL_TIERS, slashCommands: defaultSlashCommands(language, command), @@ -135,7 +135,11 @@ export interface ContextOptions { readonly slashCommands: readonly SlashCommandEntry[]; } -export function generateContextBody(_projectName: string, language: string, opts: ContextOptions): string { +export function generateContextBody( + _projectName: string, + language: string, + opts: ContextOptions, +): string { if (language === "zh") { return contextBodyZh(opts); } @@ -146,9 +150,9 @@ export function generateContextBody(_projectName: string, language: string, opts const workflowZh = `## Step 0: 诊断路由 -- 运行 \`svp check --json\`(忽略错误)+ \`svp view l5\` + 检查 .svp/ 是否存在 +- 运行 \`forge check --json\`(忽略错误)+ \`forge view l5\` + 检查 .svp/ 是否存在 - 根据结果判断: - - **无 .svp/**:告知用户先运行 \`svp init\`,停止 + - **无 .svp/**:告知用户先运行 \`forge init\`,停止 - **空项目**(无 L4/L3)→ 进入 **Build** - **有数据** → 问用户选择模式: (a) Build — 从零构建 @@ -162,46 +166,46 @@ const workflowZh = `## Step 0: 诊断路由 ## Build(从零构建整个系统) ### Step 1: [AI] 设计 L5 Blueprint -- 运行 \`svp prompt design-l5 --intent "<用户意图>"\` +- 运行 \`forge prompt design-l5 --intent "<用户意图>"\` - 将 stdout 输出派发给 subagent(读取 complexity 选择模型等级) - Subagent 输出 L5 JSON → 写入 .svp/l5.json -- [Toolchain] 运行 \`svp rehash l5\` -- 展示 \`svp view l5\` 给用户确认 +- [Toolchain] 运行 \`forge rehash l5\` +- 展示 \`forge view l5\` 给用户确认 ### Step 2: [AI] 设计 L4 Artifacts 根据系统类型选择 L4 变体: -- **Flow** (默认): 请求-响应 pipeline → \`svp prompt design-l4 --intent "..."\` -- **EventGraph**: 事件驱动/CRDT → \`svp prompt design-l4 --kind event-graph --intent "..."\` -- **StateMachine**: 实体生命周期 → \`svp prompt design-l4 --kind state-machine --intent "..."\` +- **Flow** (默认): 请求-响应 pipeline → \`forge prompt design-l4 --intent "..."\` +- **EventGraph**: 事件驱动/CRDT → \`forge prompt design-l4 --kind event-graph --intent "..."\` +- **StateMachine**: 实体生命周期 → \`forge prompt design-l4 --kind state-machine --intent "..."\` - 将 stdout 输出派发给 subagent(读取 complexity 选择模型等级) - Subagent 输出 L4 JSON → 写入 .svp/l4/.json -- [Toolchain] 运行 \`svp rehash l4\` -- 展示 \`svp view l4\` 给用户确认 +- [Toolchain] 运行 \`forge rehash l4\` +- 展示 \`forge view l4\` 给用户确认 ### Step 3: [AI] 设计 L3 Contracts(并行派发) 对每个 L4 step 的 blockRef: -- 运行 \`svp prompt design-l3 --flow --step --intent "..."\` +- 运行 \`forge prompt design-l3 --flow --step --intent "..."\` - 将 stdout 输出派发给 subagent(读取 complexity 选择模型等级) - Subagent 输出 L3 JSON → 写入 .svp/l3/.json -- [Toolchain] 运行 \`svp rehash l3/\` +- [Toolchain] 运行 \`forge rehash l3/\` - **无依赖的 block 并行派发** ### Step 4: [Toolchain] 获取编译任务 -- 运行 \`svp compile-plan\` 获取编译任务列表 +- 运行 \`forge compile-plan\` 获取编译任务列表 ### Step 5: [AI] 编译 L1 代码(并行派发) 对每个 compile 任务: -- 运行 \`svp prompt compile \` +- 运行 \`forge prompt compile \` - 将 stdout 输出派发给 subagent(读取 complexity 选择模型等级) - Subagent 生成 src/.ts 代码文件 - **无依赖的任务并行派发** ### Step 6: [Toolchain] 创建 L2 映射 -- 对每个生成的文件运行 \`svp link --files src/.ts\` +- 对每个生成的文件运行 \`forge link --files src/.ts\` ### Step 7: [Toolchain] 验证 -- 运行 \`svp check\` 验证全部层一致性 +- 运行 \`forge check\` 验证全部层一致性 - 如有问题,定位到对应层修复 - 重复直到 check 通过 @@ -210,38 +214,38 @@ const workflowZh = `## Step 0: 诊断路由 ## Add(向已有系统添加功能) ### Step 1: [Toolchain] 了解当前结构 -- 运行 \`svp view l5\` 和 \`svp view l4/\` 了解现有架构 +- 运行 \`forge view l5\` 和 \`forge view l4/\` 了解现有架构 - 确定新功能属于哪个 L4 flow(或需要新 flow) ### Step 2: [AI] 修改 L4 Flow - 编辑对应的 .svp/l4/.json,添加新 step + blockRef - 新 step 的 blockRef 指向尚不存在的 L3 block id - 更新 dataFlows 连接新 step -- [Toolchain] 运行 \`svp rehash l4\` -- 展示 \`svp view l4/\` 给用户确认 +- [Toolchain] 运行 \`forge rehash l4\` +- 展示 \`forge view l4/\` 给用户确认 ### Step 3: [AI] 设计新 L3 Contract -- 运行 \`svp prompt design-l3 --flow --step --intent "..."\` +- 运行 \`forge prompt design-l3 --flow --step --intent "..."\` - 将 stdout 输出派发给 subagent(读取 complexity 选择模型等级) - Subagent 创建 .svp/l3/.json -- [Toolchain] 运行 \`svp rehash l3/\` +- [Toolchain] 运行 \`forge rehash l3/\` ### Step 4: [AI] 编译新代码 -- 运行 \`svp prompt compile \` +- 运行 \`forge prompt compile \` - 将 stdout 输出派发给 subagent(读取 complexity 选择模型等级) - Subagent 生成 L1 源代码 ### Step 5: [Toolchain] 创建映射并验证 -- \`svp link --files \` -- \`svp check\` 确认全绿 +- \`forge link --files \` +- \`forge check\` 确认全绿 --- ## Change(修改已有需求) ### Step 1: [Toolchain] 诊断当前状态 -- 运行 \`svp check\` 确认当前一致性状态 -- 运行 \`svp view l5\` + \`svp view l4\` + \`svp view l3\` 了解结构 +- 运行 \`forge check\` 确认当前一致性状态 +- 运行 \`forge view l5\` + \`forge view l4\` + \`forge view l3\` 了解结构 ### Step 2: 判断变更层级 - 系统意图变了 → L5 @@ -251,57 +255,57 @@ const workflowZh = `## Step 0: 诊断路由 - 越低层介入越精确越便宜 ### Step 3: [AI] 执行修改 -- L5 变更:编辑 .svp/l5.json → \`svp rehash l5\` -- L4 变更:编辑 .svp/l4/.json → \`svp rehash l4\` -- L3 变更:编辑 .svp/l3/.json → \`svp rehash l3/\` +- L5 变更:编辑 .svp/l5.json → \`forge rehash l5\` +- L4 变更:编辑 .svp/l4/.json → \`forge rehash l4\` +- L3 变更:编辑 .svp/l3/.json → \`forge rehash l3/\` - 展示给用户确认 ### Step 4: [Toolchain] 获取受影响任务 -- 运行 \`svp compile-plan\` 获取受影响实体的重编译任务列表 +- 运行 \`forge compile-plan\` 获取受影响实体的重编译任务列表 ### Step 5: [AI] 重编译受影响代码 对每个 recompile 任务: -- 运行 \`svp prompt recompile \` +- 运行 \`forge prompt recompile \` - 将 stdout 输出派发给 subagent(读取 complexity 选择模型等级) - Subagent 更新 L1 代码 ### Step 6: [Toolchain] 更新映射并验证 -- \`svp link --files \` -- \`svp check\` 确认全绿 +- \`forge link --files \` +- \`forge check\` 确认全绿 --- ## Fix(修复 check 发现的问题) ### Step 1: [Toolchain] 诊断 -- 运行 \`svp check --json\` 获取结构化问题列表 +- 运行 \`forge check --json\` 获取结构化问题列表 ### Step 2: 按 issueCode 分类处理 **HASH_MISMATCH** -- [Toolchain] 运行 \`svp rehash\` 修正 hash +- [Toolchain] 运行 \`forge rehash\` 修正 hash **MISSING_L2** -- [AI] 运行 \`svp prompt compile \` → subagent 生成代码 -- [Toolchain] 运行 \`svp link --files \` +- [AI] 运行 \`forge prompt compile \` → subagent 生成代码 +- [Toolchain] 运行 \`forge link --files \` **SOURCE_DRIFT** -- [AI] 运行 \`svp prompt recompile \` → subagent 更新代码 +- [AI] 运行 \`forge prompt recompile \` → subagent 更新代码 **CONTENT_DRIFT** -- [AI] 运行 \`svp prompt review \` → subagent 判断: +- [AI] 运行 \`forge prompt review \` → subagent 判断: - L3 需要更新?还是 L1 需要修复? - 向用户展示分析结果 **MISSING_BLOCK_REF** -- [AI] 运行 \`svp prompt update-ref \` → subagent 判断: +- [AI] 运行 \`forge prompt update-ref \` → subagent 判断: - 创建缺失的 L3 contract?还是修复 L4 step 引用? **ORPHAN_STEP / NEXT_CYCLE** - 图结构问题 → 提示用户手动修复 L4 JSON ### Step 3: [Toolchain] 验证 -- 重新运行 \`svp check\` 确认修复有效 +- 重新运行 \`forge check\` 确认修复有效 - 每次只修一类问题,验证后再继续 - 重复直到全绿 @@ -309,16 +313,16 @@ const workflowZh = `## Step 0: 诊断路由 ## View(查看当前结构) -- 运行 \`svp view l5\` + \`svp view l4\` + \`svp view l3\` 展示完整系统结构 -- 如有 L2 映射,也展示 \`svp view l2\` +- 运行 \`forge view l5\` + \`forge view l4\` + \`forge view l3\` 展示完整系统结构 +- 如有 L2 映射,也展示 \`forge view l2\` $ARGUMENTS`; const workflowEn = `## Step 0: Diagnostic Router -- Run \`svp check --json\` (ignore errors) + \`svp view l5\` + check whether .svp/ exists +- Run \`forge check --json\` (ignore errors) + \`forge view l5\` + check whether .svp/ exists - Based on the result, determine: - - **No .svp/**: Tell user to run \`svp init\` first, then stop + - **No .svp/**: Tell user to run \`forge init\` first, then stop - **Empty project** (no L4/L3) → Enter **Build** - **Has data** → Ask user to choose a mode: (a) Build — build from scratch @@ -332,46 +336,46 @@ const workflowEn = `## Step 0: Diagnostic Router ## Build (build entire system from scratch) ### Step 1: [AI] Design L5 Blueprint -- Run \`svp prompt design-l5 --intent ""\` +- Run \`forge prompt design-l5 --intent ""\` - Dispatch stdout output to subagent (read complexity to select model tier) - Subagent outputs L5 JSON → write to .svp/l5.json -- [Toolchain] Run \`svp rehash l5\` -- Show \`svp view l5\` to user for confirmation +- [Toolchain] Run \`forge rehash l5\` +- Show \`forge view l5\` to user for confirmation ### Step 2: [AI] Design L4 Artifacts Choose L4 variant based on system type: -- **Flow** (default): Request-response pipeline → \`svp prompt design-l4 --intent "..."\` -- **EventGraph**: Event-driven/CRDT → \`svp prompt design-l4 --kind event-graph --intent "..."\` -- **StateMachine**: Entity lifecycle → \`svp prompt design-l4 --kind state-machine --intent "..."\` +- **Flow** (default): Request-response pipeline → \`forge prompt design-l4 --intent "..."\` +- **EventGraph**: Event-driven/CRDT → \`forge prompt design-l4 --kind event-graph --intent "..."\` +- **StateMachine**: Entity lifecycle → \`forge prompt design-l4 --kind state-machine --intent "..."\` - Dispatch stdout output to subagent (read complexity to select model tier) - Subagent outputs L4 JSON → write to .svp/l4/.json -- [Toolchain] Run \`svp rehash l4\` -- Show \`svp view l4\` to user for confirmation +- [Toolchain] Run \`forge rehash l4\` +- Show \`forge view l4\` to user for confirmation ### Step 3: [AI] Design L3 Contracts (dispatch in parallel) For each blockRef in L4 steps: -- Run \`svp prompt design-l3 --flow --step --intent "..."\` +- Run \`forge prompt design-l3 --flow --step --intent "..."\` - Dispatch stdout output to subagent (read complexity to select model tier) - Subagent outputs L3 JSON → write to .svp/l3/.json -- [Toolchain] Run \`svp rehash l3/\` +- [Toolchain] Run \`forge rehash l3/\` - **Dispatch independent blocks in parallel** ### Step 4: [Toolchain] Get Compile Tasks -- Run \`svp compile-plan\` to get the compile task list +- Run \`forge compile-plan\` to get the compile task list ### Step 5: [AI] Compile L1 Code (dispatch in parallel) For each compile task: -- Run \`svp prompt compile \` +- Run \`forge prompt compile \` - Dispatch stdout output to subagent (read complexity to select model tier) - Subagent generates src/.ts code file - **Dispatch independent tasks in parallel** ### Step 6: [Toolchain] Create L2 Mappings -- For each generated file run \`svp link --files src/.ts\` +- For each generated file run \`forge link --files src/.ts\` ### Step 7: [Toolchain] Verify -- Run \`svp check\` to validate all layer consistency +- Run \`forge check\` to validate all layer consistency - If issues found, locate and fix in the corresponding layer - Repeat until check passes @@ -380,38 +384,38 @@ For each compile task: ## Add (add feature to existing system) ### Step 1: [Toolchain] Understand Current Structure -- Run \`svp view l5\` and \`svp view l4/\` to understand the existing architecture +- Run \`forge view l5\` and \`forge view l4/\` to understand the existing architecture - Determine which L4 flow the new feature belongs to (or whether a new flow is needed) ### Step 2: [AI] Modify L4 Flow - Edit the corresponding .svp/l4/.json, add a new step + blockRef - The new step's blockRef points to a L3 block id that does not yet exist - Update dataFlows to connect the new step -- [Toolchain] Run \`svp rehash l4\` -- Show \`svp view l4/\` to user for confirmation +- [Toolchain] Run \`forge rehash l4\` +- Show \`forge view l4/\` to user for confirmation ### Step 3: [AI] Design New L3 Contract -- Run \`svp prompt design-l3 --flow --step --intent "..."\` +- Run \`forge prompt design-l3 --flow --step --intent "..."\` - Dispatch stdout output to subagent (read complexity to select model tier) - Subagent creates .svp/l3/.json -- [Toolchain] Run \`svp rehash l3/\` +- [Toolchain] Run \`forge rehash l3/\` ### Step 4: [AI] Compile New Code -- Run \`svp prompt compile \` +- Run \`forge prompt compile \` - Dispatch stdout output to subagent (read complexity to select model tier) - Subagent generates L1 source code ### Step 5: [Toolchain] Create Mapping and Verify -- \`svp link --files \` -- \`svp check\` to confirm all green +- \`forge link --files \` +- \`forge check\` to confirm all green --- ## Change (modify existing requirement) ### Step 1: [Toolchain] Diagnose Current State -- Run \`svp check\` to confirm current consistency state -- Run \`svp view l5\` + \`svp view l4\` + \`svp view l3\` to understand the structure +- Run \`forge check\` to confirm current consistency state +- Run \`forge view l5\` + \`forge view l4\` + \`forge view l3\` to understand the structure ### Step 2: Determine the Change Layer - System intent changed → L5 @@ -421,57 +425,57 @@ For each compile task: - The lower the intervention layer, the more precise and cheaper ### Step 3: [AI] Apply Changes -- L5 change: edit .svp/l5.json → \`svp rehash l5\` -- L4 change: edit .svp/l4/.json → \`svp rehash l4\` -- L3 change: edit .svp/l3/.json → \`svp rehash l3/\` +- L5 change: edit .svp/l5.json → \`forge rehash l5\` +- L4 change: edit .svp/l4/.json → \`forge rehash l4\` +- L3 change: edit .svp/l3/.json → \`forge rehash l3/\` - Show to user for confirmation ### Step 4: [Toolchain] Get Affected Tasks -- Run \`svp compile-plan\` to get the recompile task list for affected entities +- Run \`forge compile-plan\` to get the recompile task list for affected entities ### Step 5: [AI] Recompile Affected Code For each recompile task: -- Run \`svp prompt recompile \` +- Run \`forge prompt recompile \` - Dispatch stdout output to subagent (read complexity to select model tier) - Subagent updates L1 code ### Step 6: [Toolchain] Update Mappings and Verify -- \`svp link --files \` -- \`svp check\` to confirm all green +- \`forge link --files \` +- \`forge check\` to confirm all green --- ## Fix (fix issues found by check) ### Step 1: [Toolchain] Diagnose -- Run \`svp check --json\` to get the structured issue list +- Run \`forge check --json\` to get the structured issue list ### Step 2: Handle by issueCode Category **HASH_MISMATCH** -- [Toolchain] Run \`svp rehash\` to fix hash +- [Toolchain] Run \`forge rehash\` to fix hash **MISSING_L2** -- [AI] Run \`svp prompt compile \` → subagent generates code -- [Toolchain] Run \`svp link --files \` +- [AI] Run \`forge prompt compile \` → subagent generates code +- [Toolchain] Run \`forge link --files \` **SOURCE_DRIFT** -- [AI] Run \`svp prompt recompile \` → subagent updates code +- [AI] Run \`forge prompt recompile \` → subagent updates code **CONTENT_DRIFT** -- [AI] Run \`svp prompt review \` → subagent determines: +- [AI] Run \`forge prompt review \` → subagent determines: - Does L3 need updating? Or does L1 need fixing? - Show the analysis results to the user **MISSING_BLOCK_REF** -- [AI] Run \`svp prompt update-ref \` → subagent determines: +- [AI] Run \`forge prompt update-ref \` → subagent determines: - Create the missing L3 contract? Or fix the L4 step reference? **ORPHAN_STEP / NEXT_CYCLE** - Graph structure issues → prompt user to fix manually in L4 JSON ### Step 3: [Toolchain] Verify -- Re-run \`svp check\` to confirm fixes are effective +- Re-run \`forge check\` to confirm fixes are effective - Fix one issue type at a time, verify before continuing - Repeat until all green @@ -479,15 +483,17 @@ For each recompile task: ## View (view current structure) -- Run \`svp view l5\` + \`svp view l4\` + \`svp view l3\` to show full system structure -- If L2 mappings exist, also show \`svp view l2\` +- Run \`forge view l5\` + \`forge view l4\` + \`forge view l3\` to show full system structure +- If L2 mappings exist, also show \`forge view l2\` $ARGUMENTS`; // ── Private: Context body templates ── function contextBodyZh(opts: ContextOptions): string { - const slashRows = opts.slashCommands.map((s) => `| \`${s.command}\` | ${s.description} |`).join("\n"); + const slashRows = opts.slashCommands + .map((s) => `| \`${s.command}\` | ${s.description} |`) + .join("\n"); return ` ## SVP — Semantic Voxel Protocol @@ -535,15 +541,15 @@ graphs/ | 作用域 | 操作 | 方式 | |--------|------|------| -| **AI** | 设计 L5/L4/L3 规格 | \`svp prompt design-*\` → subagent | -| **AI** | 编译 L3→L1 代码 | \`svp prompt compile/recompile\` → subagent | -| **AI** | 审查漂移 | \`svp prompt review\` → subagent | -| **AI** | 修复断裂引用 | \`svp prompt update-ref\` → subagent | -| **Toolchain** | 校验一致性 | \`svp check\` | -| **Toolchain** | 渲染层视图 | \`svp view\` | -| **Toolchain** | 生成编译任务列表 | \`svp compile-plan\` | -| **Toolchain** | 创建/更新 L2 映射 | \`svp link\` | -| **Toolchain** | 重算 hash | \`svp rehash\` | +| **AI** | 设计 L5/L4/L3 规格 | \`forge prompt design-*\` → subagent | +| **AI** | 编译 L3→L1 代码 | \`forge prompt compile/recompile\` → subagent | +| **AI** | 审查漂移 | \`forge prompt review\` → subagent | +| **AI** | 修复断裂引用 | \`forge prompt update-ref\` → subagent | +| **Toolchain** | 校验一致性 | \`forge check\` | +| **Toolchain** | 渲染层视图 | \`forge view\` | +| **Toolchain** | 生成编译任务列表 | \`forge compile-plan\` | +| **Toolchain** | 创建/更新 L2 映射 | \`forge link\` | +| **Toolchain** | 重算 hash | \`forge rehash\` | 核心原则:AI 只做需要创造力/判断力的事。机械操作全部交给 toolchain CLI。 @@ -562,34 +568,34 @@ SVP prompt 包含 \`complexity\` front-matter 字段,指示任务难度: ### Subagent 派发模式 \`\`\` -1. 运行 svp prompt [options] 获取提示词 +1. 运行 forge prompt [options] 获取提示词 2. 读取 prompt 头部 complexity 字段选择模型等级 3. 将 stdout 输出派发给 subagent 执行 -4. Subagent 完成后运行 svp link / svp rehash / svp check +4. Subagent 完成后运行 forge link / forge rehash / forge check \`\`\` ### 可用 CLI 命令 | 命令 | 说明 | |---|---| -| \`svp view l5/l4/l3/l2\` | 以 AI 友好格式查看层数据 | -| \`svp check\` | 校验跨层一致性 | -| \`svp compile-plan\` | 检测变更并生成重编译任务 | -| \`svp rehash [target]\` | 重算 contentHash + 递增 revision | -| \`svp link --files \` | 创建/更新 L2 code block 映射 | -| \`svp prompt \` | 生成上下文感知的 AI 提示词到 stdout | +| \`forge view l5/l4/l3/l2\` | 以 AI 友好格式查看层数据 | +| \`forge check\` | 校验跨层一致性 | +| \`forge compile-plan\` | 检测变更并生成重编译任务 | +| \`forge rehash [target]\` | 重算 contentHash + 递增 revision | +| \`forge link --files \` | 创建/更新 L2 code block 映射 | +| \`forge prompt \` | 生成上下文感知的 AI 提示词到 stdout | ### Prompt 命令 | 命令 | 说明 | |---|---| -| \`svp prompt compile \` | 生成 L3→L1 编译提示词 | -| \`svp prompt recompile \` | 生成重编译提示词(L3 已变更) | -| \`svp prompt review \` | 生成审查提示词(L1 漂移) | -| \`svp prompt update-ref \` | 生成修复断裂 L4→L3 引用的提示词 | -| \`svp prompt design-l5 --intent "..."\` | 生成 L5 设计提示词 | -| \`svp prompt design-l4 --intent "..." [--kind flow|event-graph|state-machine]\` | 生成 L4 设计提示词 | -| \`svp prompt design-l3 --flow --step --intent "..."\` | 生成 L3 设计提示词 | +| \`forge prompt compile \` | 生成 L3→L1 编译提示词 | +| \`forge prompt recompile \` | 生成重编译提示词(L3 已变更) | +| \`forge prompt review \` | 生成审查提示词(L1 漂移) | +| \`forge prompt update-ref \` | 生成修复断裂 L4→L3 引用的提示词 | +| \`forge prompt design-l5 --intent "..."\` | 生成 L5 设计提示词 | +| \`forge prompt design-l4 --intent "..." [--kind flow|event-graph|state-machine]\` | 生成 L4 设计提示词 | +| \`forge prompt design-l3 --flow --step --intent "..."\` | 生成 L3 设计提示词 | ### Slash 命令 @@ -597,15 +603,15 @@ SVP prompt 包含 \`complexity\` front-matter 字段,指示任务难度: |---|---| ${slashRows} -Toolchain 操作直接运行 CLI:\`svp check\`、\`svp view l3\` 等。 +Toolchain 操作直接运行 CLI:\`forge check\`、\`forge view l3\` 等。 ### 核心规则 1. **分层穿透**:仅向下工作。永远不修改上层来修复下层。 2. **上下文隔离**:主 Agent 停留在 L5+L4(~30 行)。L3 设计和 L2+L1 编译派发给 subagent。 -3. **Hash 管理**:在 JSON 中写 \`"placeholder"\` 作为 contentHash。运行 \`svp rehash\` 修正。 -4. **L2 创建**:生成 L1 代码后,运行 \`svp link --files \` 创建 L2 映射。 -5. **验证**:每层完成后运行 \`svp check\` 确保一致性。 +3. **Hash 管理**:在 JSON 中写 \`"placeholder"\` 作为 contentHash。运行 \`forge rehash\` 修正。 +4. **L2 创建**:生成 L1 代码后,运行 \`forge link --files \` 创建 L2 映射。 +5. **验证**:每层完成后运行 \`forge check\` 确保一致性。 ### L3 Contract Box 模型 @@ -634,7 +640,9 @@ description → 描述中间(转换逻辑) } function contextBodyEn(opts: ContextOptions): string { - const slashRows = opts.slashCommands.map((s) => `| \`${s.command}\` | ${s.description} |`).join("\n"); + const slashRows = opts.slashCommands + .map((s) => `| \`${s.command}\` | ${s.description} |`) + .join("\n"); return ` ## SVP — Semantic Voxel Protocol @@ -682,15 +690,15 @@ graphs/ | Scope | Operation | Method | |-------|-----------|--------| -| **AI** | Design L5/L4/L3 specs | \`svp prompt design-*\` → subagent | -| **AI** | Compile L3→L1 code | \`svp prompt compile/recompile\` → subagent | -| **AI** | Review drift | \`svp prompt review\` → subagent | -| **AI** | Fix broken references | \`svp prompt update-ref\` → subagent | -| **Toolchain** | Validate consistency | \`svp check\` | -| **Toolchain** | Render layer views | \`svp view\` | -| **Toolchain** | Generate compile task list | \`svp compile-plan\` | -| **Toolchain** | Create/update L2 mapping | \`svp link\` | -| **Toolchain** | Recompute hash | \`svp rehash\` | +| **AI** | Design L5/L4/L3 specs | \`forge prompt design-*\` → subagent | +| **AI** | Compile L3→L1 code | \`forge prompt compile/recompile\` → subagent | +| **AI** | Review drift | \`forge prompt review\` → subagent | +| **AI** | Fix broken references | \`forge prompt update-ref\` → subagent | +| **Toolchain** | Validate consistency | \`forge check\` | +| **Toolchain** | Render layer views | \`forge view\` | +| **Toolchain** | Generate compile task list | \`forge compile-plan\` | +| **Toolchain** | Create/update L2 mapping | \`forge link\` | +| **Toolchain** | Recompute hash | \`forge rehash\` | Core Principle: AI only does what requires creativity or judgment. All mechanical operations go to the toolchain CLI. @@ -710,34 +718,34 @@ and pass the corresponding model parameter. ### Subagent Dispatch Pattern \`\`\` -1. Run svp prompt [options] to get the prompt +1. Run forge prompt [options] to get the prompt 2. Read the complexity field in the prompt header to select model tier 3. Dispatch stdout output to subagent for execution -4. After subagent completes, run svp link / svp rehash / svp check +4. After subagent completes, run forge link / forge rehash / forge check \`\`\` ### Available CLI Commands | Command | Description | |---|---| -| \`svp view l5/l4/l3/l2\` | View layer data in AI-friendly format | -| \`svp check\` | Validate cross-layer consistency | -| \`svp compile-plan\` | Detect changes and generate recompile tasks | -| \`svp rehash [target]\` | Recompute contentHash + bump revision | -| \`svp link --files \` | Create/update L2 code block mapping | -| \`svp prompt \` | Generate context-aware AI prompt to stdout | +| \`forge view l5/l4/l3/l2\` | View layer data in AI-friendly format | +| \`forge check\` | Validate cross-layer consistency | +| \`forge compile-plan\` | Detect changes and generate recompile tasks | +| \`forge rehash [target]\` | Recompute contentHash + bump revision | +| \`forge link --files \` | Create/update L2 code block mapping | +| \`forge prompt \` | Generate context-aware AI prompt to stdout | ### Prompt Commands | Command | Description | |---|---| -| \`svp prompt compile \` | Generate compile prompt for L3→L1 | -| \`svp prompt recompile \` | Generate recompile prompt (L3 changed) | -| \`svp prompt review \` | Generate review prompt (L1 drift) | -| \`svp prompt update-ref \` | Generate fix prompt for broken L4→L3 refs | -| \`svp prompt design-l5 --intent "..."\` | Generate L5 design prompt | -| \`svp prompt design-l4 --intent "..." [--kind flow|event-graph|state-machine]\` | Generate L4 design prompt | -| \`svp prompt design-l3 --flow --step --intent "..."\` | Generate L3 design prompt | +| \`forge prompt compile \` | Generate compile prompt for L3→L1 | +| \`forge prompt recompile \` | Generate recompile prompt (L3 changed) | +| \`forge prompt review \` | Generate review prompt (L1 drift) | +| \`forge prompt update-ref \` | Generate fix prompt for broken L4→L3 refs | +| \`forge prompt design-l5 --intent "..."\` | Generate L5 design prompt | +| \`forge prompt design-l4 --intent "..." [--kind flow|event-graph|state-machine]\` | Generate L4 design prompt | +| \`forge prompt design-l3 --flow --step --intent "..."\` | Generate L3 design prompt | ### Slash Commands @@ -745,15 +753,15 @@ and pass the corresponding model parameter. |---|---| ${slashRows} -Toolchain operations run CLI directly: \`svp check\`, \`svp view l3\`, etc. +Toolchain operations run CLI directly: \`forge check\`, \`forge view l3\`, etc. ### Core Rules 1. **Layered penetration**: Work top-down only. Never modify upper layers to fix lower layers. 2. **Context isolation**: Main agent stays at L5+L4 (~30 lines). L3 design and L2+L1 compile dispatched to subagents. -3. **Hash management**: Write \`"placeholder"\` for contentHash in JSON. Run \`svp rehash\` to fix. -4. **L2 creation**: After generating L1 code, run \`svp link --files \` to create L2 mapping. -5. **Verification**: Run \`svp check\` after each layer to ensure consistency. +3. **Hash management**: Write \`"placeholder"\` for contentHash in JSON. Run \`forge rehash\` to fix. +4. **L2 creation**: After generating L1 code, run \`forge link --files \` to create L2 mapping. +5. **Verification**: Run \`forge check\` after each layer to ensure consistency. ### L3 Contract Box Model diff --git a/packages/skills/adapters/types.ts b/packages/skills/adapters/types.ts index 5338163..e30e361 100644 --- a/packages/skills/adapters/types.ts +++ b/packages/skills/adapters/types.ts @@ -1,25 +1,31 @@ // adapters/types — Host adapter interface for multi-host support -export type HostId = "claude-code" | "kimi-code" | "codex" | "cursor" | "windsurf" | "github-copilot"; +export type HostId = + | "claude-code" + | "kimi-code" + | "codex" + | "cursor" + | "windsurf" + | "github-copilot"; export interface HostAdapter { readonly id: HostId; readonly displayName: string; /** Directory where skill/command files are written (relative to project root) */ - skillDir(): string; + skillDir: () => string; /** Generate skill/command files */ - generateSkillFiles(language: string): readonly SkillFile[]; + generateSkillFiles: (language: string) => readonly SkillFile[]; /** Path to context file (e.g. "CLAUDE.md" or "AGENTS.md") */ - contextFilePath(): string; + contextFilePath: () => string; /** Marker string to detect if SVP section already exists */ - contextMarker(): string; + contextMarker: () => string; /** Generate the context section to append */ - generateContextSection(projectName: string, language: string): string; + generateContextSection: (projectName: string, language: string) => string; } export interface SkillFile { diff --git a/packages/skills/adapters/windsurf.ts b/packages/skills/adapters/windsurf.ts index 4c1dccc..cb8d598 100644 --- a/packages/skills/adapters/windsurf.ts +++ b/packages/skills/adapters/windsurf.ts @@ -1,6 +1,5 @@ // adapters/windsurf — Windsurf host adapter -import type { HostAdapter, SkillFile } from "./types.js"; import { buildSkillFileContent, genericModelTierLine, @@ -8,6 +7,7 @@ import { genericContextOptions, DEFAULT_CONTEXT_MARKER, } from "./shared.js"; +import type { HostAdapter, SkillFile } from "./types.js"; export const windsurfAdapter: HostAdapter = { id: "windsurf", @@ -18,7 +18,12 @@ export const windsurfAdapter: HostAdapter = { }, generateSkillFiles(language = "en"): readonly SkillFile[] { - return [{ relativePath: "svp.md", content: buildSkillFileContent(language, genericModelTierLine(language)) }]; + return [ + { + relativePath: "svp.md", + content: buildSkillFileContent(language, genericModelTierLine(language)), + }, + ]; }, contextFilePath() { diff --git a/packages/skills/link.ts b/packages/skills/link.ts index 42b73ec..a43826d 100644 --- a/packages/skills/link.ts +++ b/packages/skills/link.ts @@ -1,5 +1,5 @@ // link — 创建 L2CodeBlock(L3 和 L1 之间的桥接层) -// AI 生成 L1 源代码后,运行 svp link 创建 L2 映射 +// AI 生成 L1 源代码后,运行 forge link 创建 L2 映射 // 纯函数,不做 IO import { hashL2 } from "../core/hash.js"; diff --git a/packages/skills/prompt-builder.ts b/packages/skills/prompt-builder.ts index 9f16bad..e7a6189 100644 --- a/packages/skills/prompt-builder.ts +++ b/packages/skills/prompt-builder.ts @@ -2,10 +2,10 @@ // 将编译任务组装为结构化 prompt,复用 core/view.ts 渲染 // 纯函数,不做 IO +import { getLanguage, languageDirective } from "../core/i18n.js"; import { viewL2Detail, viewL3Detail, viewL4Detail, viewL5Overview } from "../core/view.js"; import type { Complexity, TaskAction } from "../core/compile-plan.js"; import type { SkillInput } from "../core/skill.js"; -import { getLanguage, languageDirective } from "../core/i18n.js"; export interface StructuredPrompt { readonly role: string; @@ -39,7 +39,7 @@ const OUTPUT_SPECS: Record = { "- Implementation must satisfy all validate rules and constraints", "- Internal logic should follow the description", "- If Documentation is provided, use it for design intent, edge cases, and error strategy", - "- After writing files, run: svp link --files ", + "- After writing files, run: forge link --files ", "- File naming: src/.ts (or appropriate for language)", ].join("\n"), recompile: [ @@ -47,7 +47,7 @@ const OUTPUT_SPECS: Record = { "- Compare old vs new L3 to identify what changed", "- Modify only the affected parts of the implementation", "- Preserve unchanged logic and tests", - "- After updating files, run: svp link --files ", + "- After updating files, run: forge link --files ", ].join("\n"), review: [ "Analyze the drift between L3 contract and L1 implementation:", @@ -62,7 +62,7 @@ const OUTPUT_SPECS: Record = { "- Option A: Create the missing L3 block with appropriate contract", "- Option B: Fix the L4 step to reference an existing L3 block", "- If creating L3, infer pins from the L4 flow context (upstream/downstream steps)", - "- After creating L3, run: svp rehash l3/", + "- After creating L3, run: forge rehash l3/", ].join("\n"), }; @@ -70,8 +70,8 @@ const OUTPUT_SPECS: Record = { const COMMON_RULES = [ "- Do NOT modify layers above your target — strict downward only", - "- Use svp rehash to fix contentHash after editing JSON files", - "- Use svp link to create/update L2 after generating L1 code", + "- Use forge rehash to fix contentHash after editing JSON files", + "- Use forge link to create/update L2 after generating L1 code", "- Write placeholder for contentHash in JSON — rehash will fix it", "- Keep implementation minimal — satisfy the contract, nothing more", ].join("\n"); diff --git a/packages/skills/prompts/compile.ts b/packages/skills/prompts/compile.ts index 3b6a9c5..f7e3b30 100644 --- a/packages/skills/prompts/compile.ts +++ b/packages/skills/prompts/compile.ts @@ -1,32 +1,34 @@ // compile — L3 → L2+L1 初始编译 prompt 模板 -// 由 svp prompt compile 使用 +// 由 forge prompt compile 使用 import { languageDirective } from "../../core/i18n.js"; export function compileInstructions(language = "en"): string { - return [ - "You are compiling an L3 contract into L1 source code.", - "", - "## Steps", - "", - "1. Read the L3 contract provided in the Input section", - "2. Create L1 source file(s) that implement the contract:", - " - Function signature matches L3 input/output pins", - " - Implementation satisfies all validate rules", - " - Output meets all constraints", - " - Internal logic follows the description", - "3. Run `svp link --files ` to create the L2 mapping", - "", - "## Code Guidelines", - "", - "- File naming: `src/.ts`", - "- Export a single main function named after the block id (camelCase)", - "- Input parameter types must match L3 input pins", - "- Return type must match L3 output pins", - "- Add a corresponding test file: `src/.test.ts`", - "- Use TypeScript strict mode", - "- No external dependencies unless specified in L5 integrations", - ].join("\n") + languageDirective(language); + return ( + [ + "You are compiling an L3 contract into L1 source code.", + "", + "## Steps", + "", + "1. Read the L3 contract provided in the Input section", + "2. Create L1 source file(s) that implement the contract:", + " - Function signature matches L3 input/output pins", + " - Implementation satisfies all validate rules", + " - Output meets all constraints", + " - Internal logic follows the description", + "3. Run `forge link --files ` to create the L2 mapping", + "", + "## Code Guidelines", + "", + "- File naming: `src/.ts`", + "- Export a single main function named after the block id (camelCase)", + "- Input parameter types must match L3 input pins", + "- Return type must match L3 output pins", + "- Add a corresponding test file: `src/.test.ts`", + "- Use TypeScript strict mode", + "- No external dependencies unless specified in L5 integrations", + ].join("\n") + languageDirective(language) + ); } // Backward compat diff --git a/packages/skills/prompts/design-l3.ts b/packages/skills/prompts/design-l3.ts index 73da73f..73ea2b9 100644 --- a/packages/skills/prompts/design-l3.ts +++ b/packages/skills/prompts/design-l3.ts @@ -1,9 +1,9 @@ // design-l3 — L3 Contract 设计 prompt 模板 -// 由 slash commands / svp prompt 驱动 +// 由 slash commands / forge prompt 驱动 -import { complexityHeader } from "./complexity-header.js"; import { languageDirective } from "../../core/i18n.js"; import { viewL4Detail } from "../../core/view.js"; +import { complexityHeader } from "./complexity-header.js"; import type { L3Block } from "../../core/l3.js"; import type { L4Artifact, L4Flow, BlockContext } from "../../core/l4.js"; @@ -111,58 +111,62 @@ export function buildDesignL3Prompt(input: DesignL3Input): string { ? "No existing L3 block. Creating new contract." : ["### Current L3 Block", "```json", JSON.stringify(existingBlock, null, 2), "```"].join("\n"); - return complexityHeader("standard") + [ - `# ${action} L3 Contract: ${blockId}`, - "", - "You are designing a contract box (L3) — the specification for a single logic unit.", - "L3 is the interface between human intent and AI implementation.", - "", - `## L4 Context (${location})`, - "", - l4View, - "", - ...(neighborSection.length > 0 ? ["## Neighbor Context", "", ...neighborSection, ""] : []), - "## User Intent", - "", - input.userIntent, - "", - "## Current State", - "", - currentSection, - "", - "## Instructions", - "", - "Design the contract box with:", - "- **input pins**: Each has `name`, `type` (TypeScript interface name), optional `optional?: true`", - "- **output pins**: Same format as input", - "- **validate**: Input constraints as natural language rules", - " - Key = field path (e.g., `request.items`)", - " - Value = natural language rule (e.g., `array, min 1, max 50`)", - "- **constraints**: Output assertions as natural language", - " - Each string asserts a relationship about the output", - "- **description**: Natural language describing the internal logic", - "", - "The three parts work together:", - "- **validate** constrains INPUT", - "- **constraints** constrains OUTPUT", - "- **description** covers the MIDDLE (transformation logic)", - "", - "Write to `.svp/l3/.json` using this schema:", - "", - "```json", - L3_SCHEMA_EXAMPLE, - "```", - "", - "After writing, run `svp rehash l3/${blockId}` to fix contentHash.", - "Then show `svp view l3/${blockId}` to the user for confirmation.", - "", - "## Rules", - "", - "- Pin types reference TypeScript interface names from the project's types/ directory", - '- validate uses natural language, not code: `"array, min 1"` not `"Array.isArray && length >= 1"`', - "- constraints use natural language assertions about output", - "- description explains HOW to transform input to output", - "- Ensure pin types are compatible with upstream/downstream blocks", - "- Write 'placeholder' for contentHash — rehash will fix it", - ].join("\n") + languageDirective(input.language ?? "en"); + return ( + complexityHeader("standard") + + [ + `# ${action} L3 Contract: ${blockId}`, + "", + "You are designing a contract box (L3) — the specification for a single logic unit.", + "L3 is the interface between human intent and AI implementation.", + "", + `## L4 Context (${location})`, + "", + l4View, + "", + ...(neighborSection.length > 0 ? ["## Neighbor Context", "", ...neighborSection, ""] : []), + "## User Intent", + "", + input.userIntent, + "", + "## Current State", + "", + currentSection, + "", + "## Instructions", + "", + "Design the contract box with:", + "- **input pins**: Each has `name`, `type` (TypeScript interface name), optional `optional?: true`", + "- **output pins**: Same format as input", + "- **validate**: Input constraints as natural language rules", + " - Key = field path (e.g., `request.items`)", + " - Value = natural language rule (e.g., `array, min 1, max 50`)", + "- **constraints**: Output assertions as natural language", + " - Each string asserts a relationship about the output", + "- **description**: Natural language describing the internal logic", + "", + "The three parts work together:", + "- **validate** constrains INPUT", + "- **constraints** constrains OUTPUT", + "- **description** covers the MIDDLE (transformation logic)", + "", + "Write to `.svp/l3/.json` using this schema:", + "", + "```json", + L3_SCHEMA_EXAMPLE, + "```", + "", + "After writing, run `forge rehash l3/${blockId}` to fix contentHash.", + "Then show `forge view l3/${blockId}` to the user for confirmation.", + "", + "## Rules", + "", + "- Pin types reference TypeScript interface names from the project's types/ directory", + '- validate uses natural language, not code: `"array, min 1"` not `"Array.isArray && length >= 1"`', + "- constraints use natural language assertions about output", + "- description explains HOW to transform input to output", + "- Ensure pin types are compatible with upstream/downstream blocks", + "- Write 'placeholder' for contentHash — rehash will fix it", + ].join("\n") + + languageDirective(input.language ?? "en") + ); } diff --git a/packages/skills/prompts/design-l4-event-graph.ts b/packages/skills/prompts/design-l4-event-graph.ts index 3dc7ce9..00b2d37 100644 --- a/packages/skills/prompts/design-l4-event-graph.ts +++ b/packages/skills/prompts/design-l4-event-graph.ts @@ -1,9 +1,9 @@ // design-l4-event-graph — L4 EventGraph 设计 prompt 模板 -import { complexityHeader } from "./complexity-header.js"; import { languageDirective } from "../../core/i18n.js"; import { extractBlockRefs, getL4Kind } from "../../core/l4.js"; import { viewL5Overview } from "../../core/view.js"; +import { complexityHeader } from "./complexity-header.js"; import type { L3Block } from "../../core/l3.js"; import type { L4Artifact } from "../../core/l4.js"; import type { L5Blueprint } from "../../core/l5.js"; @@ -197,65 +197,69 @@ export function buildDesignL4EventGraphPrompt(input: DesignL4EventGraphInput): s ) .join("\n"); - return complexityHeader("heavy") + [ - `# ${action} L4 EventGraph`, - "", - "You are designing an event-graph (L4) for event-driven / reactive systems.", - "An event-graph declares shared state and multiple event handlers, each with its own step chain.", - "", - "## Project Context (L5)", - "", - l5View, - "", - "## User Intent", - "", - input.userIntent, - "", - "## Existing L4 Artifacts", - "", - existingSection, - "", - "## Existing L3 Blocks (available for reuse)", - "", - existingBlocksSection, - "", - "## EventGraph Structure", - "", - "- **state**: Declare shared state keys with type and description", - " - Each key is accessible via `$state.key` in dataFlows", - "- **handlers**: Each handler responds to a specific event", - " - `event`: Event name (e.g., `user.local_edit`, `sync.remote_update`)", - " - `steps`: Same step model as Flow (process/parallel/wait/call)", - " - `dataFlows`: Connect steps, plus special prefixes:", - " - `$event.field` — read from the incoming event payload", - " - `$state.key` — read from or write to shared state", - "", - "## Examples", - "", - "Here are two complete examples showing how to go from intent to EventGraph JSON:", - "", - FEW_SHOT_EXAMPLES, - "", - "## Instructions", - "", - "Write to `.svp/l4/.json` using this schema:", - "", - "```json", - EVENT_GRAPH_SCHEMA_EXAMPLE, - "```", - "", - "After writing, run `svp rehash l4` to fix contentHash.", - "Then show `svp view l4` to the user for confirmation.", - "", - "## Rules", - "", - '- `kind` MUST be `"event-graph"`', - "- Each handler must have a unique `event` name", - "- `state` declares all shared state keys — handlers reference them via `$state.key`", - "- Handler steps follow the same rules as Flow steps (unique ids, valid next chain)", - "- `$event.field` references event payload — no schema validation (external)", - "- `$state.key` must reference a declared state key", - "- Write 'placeholder' for contentHash — rehash will fix it", - "- Do NOT create L3 blocks here — only reference them by id", - ].join("\n") + languageDirective(input.language ?? "en"); + return ( + complexityHeader("heavy") + + [ + `# ${action} L4 EventGraph`, + "", + "You are designing an event-graph (L4) for event-driven / reactive systems.", + "An event-graph declares shared state and multiple event handlers, each with its own step chain.", + "", + "## Project Context (L5)", + "", + l5View, + "", + "## User Intent", + "", + input.userIntent, + "", + "## Existing L4 Artifacts", + "", + existingSection, + "", + "## Existing L3 Blocks (available for reuse)", + "", + existingBlocksSection, + "", + "## EventGraph Structure", + "", + "- **state**: Declare shared state keys with type and description", + " - Each key is accessible via `$state.key` in dataFlows", + "- **handlers**: Each handler responds to a specific event", + " - `event`: Event name (e.g., `user.local_edit`, `sync.remote_update`)", + " - `steps`: Same step model as Flow (process/parallel/wait/call)", + " - `dataFlows`: Connect steps, plus special prefixes:", + " - `$event.field` — read from the incoming event payload", + " - `$state.key` — read from or write to shared state", + "", + "## Examples", + "", + "Here are two complete examples showing how to go from intent to EventGraph JSON:", + "", + FEW_SHOT_EXAMPLES, + "", + "## Instructions", + "", + "Write to `.svp/l4/.json` using this schema:", + "", + "```json", + EVENT_GRAPH_SCHEMA_EXAMPLE, + "```", + "", + "After writing, run `forge rehash l4` to fix contentHash.", + "Then show `forge view l4` to the user for confirmation.", + "", + "## Rules", + "", + '- `kind` MUST be `"event-graph"`', + "- Each handler must have a unique `event` name", + "- `state` declares all shared state keys — handlers reference them via `$state.key`", + "- Handler steps follow the same rules as Flow steps (unique ids, valid next chain)", + "- `$event.field` references event payload — no schema validation (external)", + "- `$state.key` must reference a declared state key", + "- Write 'placeholder' for contentHash — rehash will fix it", + "- Do NOT create L3 blocks here — only reference them by id", + ].join("\n") + + languageDirective(input.language ?? "en") + ); } diff --git a/packages/skills/prompts/design-l4-state-machine.ts b/packages/skills/prompts/design-l4-state-machine.ts index 7a6b7fa..4b89f06 100644 --- a/packages/skills/prompts/design-l4-state-machine.ts +++ b/packages/skills/prompts/design-l4-state-machine.ts @@ -1,9 +1,9 @@ // design-l4-state-machine — L4 StateMachine 设计 prompt 模板 -import { complexityHeader } from "./complexity-header.js"; import { languageDirective } from "../../core/i18n.js"; import { extractBlockRefs, getL4Kind } from "../../core/l4.js"; import { viewL5Overview } from "../../core/view.js"; +import { complexityHeader } from "./complexity-header.js"; import type { L3Block } from "../../core/l3.js"; import type { L4Artifact } from "../../core/l4.js"; import type { L5Blueprint } from "../../core/l5.js"; @@ -183,66 +183,70 @@ export function buildDesignL4StateMachinePrompt(input: DesignL4StateMachineInput ) .join("\n"); - return complexityHeader("heavy") + [ - `# ${action} L4 StateMachine`, - "", - "You are designing a state machine (L4) for entity lifecycle management.", - "A state machine defines states, transitions between them, and optional guards and side effects.", - "", - "## Project Context (L5)", - "", - l5View, - "", - "## User Intent", - "", - input.userIntent, - "", - "## Existing L4 Artifacts", - "", - existingSection, - "", - "## Existing L3 Blocks (available for reuse)", - "", - existingBlocksSection, - "", - "## StateMachine Structure", - "", - "- **entity**: The entity type this state machine manages (e.g., `PurchaseOrder`)", - "- **initialState**: The starting state for new entities", - "- **states**: Each state can have optional side effects:", - " - `onEntry.blockRef` — L3 block triggered when entering this state", - " - `onExit.blockRef` — L3 block triggered when leaving this state", - "- **transitions**: Define allowed state changes:", - " - `from` / `to` — must reference declared states", - " - `event` — the trigger event name", - " - `guard` (optional) — L3 block id that returns boolean (transition only if true)", - "", - "## Examples", - "", - "Here are two complete examples showing how to go from intent to StateMachine JSON:", - "", - FEW_SHOT_EXAMPLES, - "", - "## Instructions", - "", - "Write to `.svp/l4/.json` using this schema:", - "", - "```json", - STATE_MACHINE_SCHEMA_EXAMPLE, - "```", - "", - "After writing, run `svp rehash l4` to fix contentHash.", - "Then show `svp view l4` to the user for confirmation.", - "", - "## Rules", - "", - '- `kind` MUST be `"state-machine"`', - "- `initialState` must reference a declared state", - "- All `from` and `to` in transitions must reference declared states", - "- `guard` references an L3 block that returns a boolean", - "- `onEntry`/`onExit` reference L3 blocks for side effects", - "- Every state should be reachable from `initialState` via transitions", - "- Write 'placeholder' for contentHash — rehash will fix it", - "- Do NOT create L3 blocks here — only reference them by id", - ].join("\n") + languageDirective(input.language ?? "en"); + return ( + complexityHeader("heavy") + + [ + `# ${action} L4 StateMachine`, + "", + "You are designing a state machine (L4) for entity lifecycle management.", + "A state machine defines states, transitions between them, and optional guards and side effects.", + "", + "## Project Context (L5)", + "", + l5View, + "", + "## User Intent", + "", + input.userIntent, + "", + "## Existing L4 Artifacts", + "", + existingSection, + "", + "## Existing L3 Blocks (available for reuse)", + "", + existingBlocksSection, + "", + "## StateMachine Structure", + "", + "- **entity**: The entity type this state machine manages (e.g., `PurchaseOrder`)", + "- **initialState**: The starting state for new entities", + "- **states**: Each state can have optional side effects:", + " - `onEntry.blockRef` — L3 block triggered when entering this state", + " - `onExit.blockRef` — L3 block triggered when leaving this state", + "- **transitions**: Define allowed state changes:", + " - `from` / `to` — must reference declared states", + " - `event` — the trigger event name", + " - `guard` (optional) — L3 block id that returns boolean (transition only if true)", + "", + "## Examples", + "", + "Here are two complete examples showing how to go from intent to StateMachine JSON:", + "", + FEW_SHOT_EXAMPLES, + "", + "## Instructions", + "", + "Write to `.svp/l4/.json` using this schema:", + "", + "```json", + STATE_MACHINE_SCHEMA_EXAMPLE, + "```", + "", + "After writing, run `forge rehash l4` to fix contentHash.", + "Then show `forge view l4` to the user for confirmation.", + "", + "## Rules", + "", + '- `kind` MUST be `"state-machine"`', + "- `initialState` must reference a declared state", + "- All `from` and `to` in transitions must reference declared states", + "- `guard` references an L3 block that returns a boolean", + "- `onEntry`/`onExit` reference L3 blocks for side effects", + "- Every state should be reachable from `initialState` via transitions", + "- Write 'placeholder' for contentHash — rehash will fix it", + "- Do NOT create L3 blocks here — only reference them by id", + ].join("\n") + + languageDirective(input.language ?? "en") + ); } diff --git a/packages/skills/prompts/design-l4.ts b/packages/skills/prompts/design-l4.ts index 07b032d..00a314f 100644 --- a/packages/skills/prompts/design-l4.ts +++ b/packages/skills/prompts/design-l4.ts @@ -1,10 +1,10 @@ // design-l4 — L4 Flow 设计 prompt 模板 -// 由 slash commands / svp prompt 驱动 +// 由 slash commands / forge prompt 驱动 -import { complexityHeader } from "./complexity-header.js"; import { languageDirective } from "../../core/i18n.js"; import { extractBlockRefs, getL4Kind } from "../../core/l4.js"; import { viewL5Overview } from "../../core/view.js"; +import { complexityHeader } from "./complexity-header.js"; import type { L3Block } from "../../core/l3.js"; import type { L4Artifact } from "../../core/l4.js"; import type { L5Blueprint } from "../../core/l5.js"; @@ -60,55 +60,59 @@ export function buildDesignL4Prompt(input: DesignL4Input): string { ) .join("\n"); - return complexityHeader("heavy") + [ - `# ${action} L4 Flow`, - "", - "You are designing a flow (L4) that orchestrates L3 logic blocks.", - "Each flow defines steps, their execution order, and data flow between them.", - "", - "## Project Context (L5)", - "", - l5View, - "", - "## User Intent", - "", - input.userIntent, - "", - "## Existing Flows", - "", - existingFlowsSection, - "", - "## Existing L3 Blocks (available for reuse)", - "", - existingBlocksSection, - "", - "## Instructions", - "", - "Design the flow with:", - "- **steps**: Each step has id, action, and navigation (next/branches/waitFor)", - " - `process`: References an L3 block via `blockRef` (can reference blocks that don't exist yet)", - " - `call`: References another L4 flow via `flowRef`", - " - `parallel`: Fan-out with `branches` array of step ids", - " - `wait`: Join point with `waitFor` array of step ids", - '- **dataFlows**: Connect output pins to input pins: `"stepId.pinName"` → `"stepId.pinName"`', - "- **trigger** (optional): HTTP, event, schedule, or manual", - "", - "Write to `.svp/l4/.json` using this schema:", - "", - "```json", - L4_SCHEMA_EXAMPLE, - "```", - "", - "After writing, run `svp rehash l4` to fix contentHash.", - "Then show `svp view l4` to the user for confirmation.", - "", - "## Rules", - "", - "- Every `process` step MUST have a `blockRef`", - "- Step ids must be unique within the flow", - '- dataFlows use format `"stepId.pinName"`', - "- Fan-out uses `parallel` action, join uses `wait` action", - "- Write 'placeholder' for contentHash — rehash will fix it", - "- Do NOT create L3 blocks here — only reference them by id", - ].join("\n") + languageDirective(input.language ?? "en"); + return ( + complexityHeader("heavy") + + [ + `# ${action} L4 Flow`, + "", + "You are designing a flow (L4) that orchestrates L3 logic blocks.", + "Each flow defines steps, their execution order, and data flow between them.", + "", + "## Project Context (L5)", + "", + l5View, + "", + "## User Intent", + "", + input.userIntent, + "", + "## Existing Flows", + "", + existingFlowsSection, + "", + "## Existing L3 Blocks (available for reuse)", + "", + existingBlocksSection, + "", + "## Instructions", + "", + "Design the flow with:", + "- **steps**: Each step has id, action, and navigation (next/branches/waitFor)", + " - `process`: References an L3 block via `blockRef` (can reference blocks that don't exist yet)", + " - `call`: References another L4 flow via `flowRef`", + " - `parallel`: Fan-out with `branches` array of step ids", + " - `wait`: Join point with `waitFor` array of step ids", + '- **dataFlows**: Connect output pins to input pins: `"stepId.pinName"` → `"stepId.pinName"`', + "- **trigger** (optional): HTTP, event, schedule, or manual", + "", + "Write to `.svp/l4/.json` using this schema:", + "", + "```json", + L4_SCHEMA_EXAMPLE, + "```", + "", + "After writing, run `forge rehash l4` to fix contentHash.", + "Then show `forge view l4` to the user for confirmation.", + "", + "## Rules", + "", + "- Every `process` step MUST have a `blockRef`", + "- Step ids must be unique within the flow", + '- dataFlows use format `"stepId.pinName"`', + "- Fan-out uses `parallel` action, join uses `wait` action", + "- Write 'placeholder' for contentHash — rehash will fix it", + "- Do NOT create L3 blocks here — only reference them by id", + ].join("\n") + + languageDirective(input.language ?? "en") + ); } diff --git a/packages/skills/prompts/design-l5.ts b/packages/skills/prompts/design-l5.ts index 8b328ab..7d415de 100644 --- a/packages/skills/prompts/design-l5.ts +++ b/packages/skills/prompts/design-l5.ts @@ -1,8 +1,8 @@ // design-l5 — L5 Blueprint 设计 prompt 模板 -// 由 slash commands / svp prompt 驱动 +// 由 slash commands / forge prompt 驱动 -import { complexityHeader } from "./complexity-header.js"; import { languageDirective } from "../../core/i18n.js"; +import { complexityHeader } from "./complexity-header.js"; import type { L5Blueprint } from "../../core/l5.js"; export interface DesignL5Input { @@ -36,44 +36,48 @@ export function buildDesignL5Prompt(input: DesignL5Input): string { "\n", ); - return complexityHeader("heavy") + [ - `# ${action} L5 Blueprint`, - "", - "You are designing the top-level blueprint (L5) for an SVP project.", - "L5 captures the system's intent, constraints, domains, and integrations.", - "Keep it concise (~10 lines of effective information).", - "", - "## User Intent", - "", - input.userIntent, - "", - "## Current State", - "", - currentSection, - "", - "## Instructions", - "", - "From the user description, extract:", - "- **intent**: Core problem + solution approach + success criteria (1-2 sentences)", - "- **constraints**: Functional, non-functional, and business constraints", - "- **domains**: Bounded contexts with dependency relationships", - "- **integrations**: External systems with type (database/api/queue/storage)", - "", - "Write the result to `.svp/l5.json` using this schema:", - "", - "```json", - L5_SCHEMA_EXAMPLE, - "```", - "", - "After writing, run `svp rehash l5` to fix the contentHash.", - "Then show `svp view l5` to the user for confirmation.", - "", - "## Rules", - "", - "- Only describe WHAT the system does, not HOW", - "- Keep intent to 1-2 sentences", - "- Constraints are strings, not objects", - "- Domain dependencies reference other domain names", - "- Write 'placeholder' for contentHash — rehash will fix it", - ].join("\n") + languageDirective(input.language ?? "en"); + return ( + complexityHeader("heavy") + + [ + `# ${action} L5 Blueprint`, + "", + "You are designing the top-level blueprint (L5) for an SVP project.", + "L5 captures the system's intent, constraints, domains, and integrations.", + "Keep it concise (~10 lines of effective information).", + "", + "## User Intent", + "", + input.userIntent, + "", + "## Current State", + "", + currentSection, + "", + "## Instructions", + "", + "From the user description, extract:", + "- **intent**: Core problem + solution approach + success criteria (1-2 sentences)", + "- **constraints**: Functional, non-functional, and business constraints", + "- **domains**: Bounded contexts with dependency relationships", + "- **integrations**: External systems with type (database/api/queue/storage)", + "", + "Write the result to `.svp/l5.json` using this schema:", + "", + "```json", + L5_SCHEMA_EXAMPLE, + "```", + "", + "After writing, run `forge rehash l5` to fix the contentHash.", + "Then show `forge view l5` to the user for confirmation.", + "", + "## Rules", + "", + "- Only describe WHAT the system does, not HOW", + "- Keep intent to 1-2 sentences", + "- Constraints are strings, not objects", + "- Domain dependencies reference other domain names", + "- Write 'placeholder' for contentHash — rehash will fix it", + ].join("\n") + + languageDirective(input.language ?? "en") + ); } diff --git a/packages/skills/prompts/recompile.ts b/packages/skills/prompts/recompile.ts index e36ccf3..09847f5 100644 --- a/packages/skills/prompts/recompile.ts +++ b/packages/skills/prompts/recompile.ts @@ -1,31 +1,33 @@ // recompile — L3 变更 → 重编译 L2+L1 prompt 模板 -// 由 svp prompt recompile 使用 +// 由 forge prompt recompile 使用 import { languageDirective } from "../../core/i18n.js"; export function recompileInstructions(language = "en"): string { - return [ - "You are recompiling L1 source code because its L3 contract has changed.", - "", - "## Steps", - "", - "1. Compare the updated L3 contract with the current L2 mapping", - "2. Identify what changed (pins added/removed/renamed, constraints modified, etc.)", - "3. Update only the affected parts of the L1 source code:", - " - If pins changed: update function signature and related logic", - " - If constraints changed: update validation/assertion logic", - " - If description changed: update internal implementation", - "4. Preserve unchanged logic — minimize diff", - "5. Update tests to match the new contract", - "6. Run `svp link --files ` to update the L2 mapping", - "", - "## Important", - "", - "- Do NOT rewrite from scratch — modify incrementally", - "- Keep existing test cases that are still valid", - "- If a pin was renamed, update all usages", - "- If a pin was removed, remove dead code", - ].join("\n") + languageDirective(language); + return ( + [ + "You are recompiling L1 source code because its L3 contract has changed.", + "", + "## Steps", + "", + "1. Compare the updated L3 contract with the current L2 mapping", + "2. Identify what changed (pins added/removed/renamed, constraints modified, etc.)", + "3. Update only the affected parts of the L1 source code:", + " - If pins changed: update function signature and related logic", + " - If constraints changed: update validation/assertion logic", + " - If description changed: update internal implementation", + "4. Preserve unchanged logic — minimize diff", + "5. Update tests to match the new contract", + "6. Run `forge link --files ` to update the L2 mapping", + "", + "## Important", + "", + "- Do NOT rewrite from scratch — modify incrementally", + "- Keep existing test cases that are still valid", + "- If a pin was renamed, update all usages", + "- If a pin was removed, remove dead code", + ].join("\n") + languageDirective(language) + ); } // Backward compat diff --git a/packages/skills/prompts/review.ts b/packages/skills/prompts/review.ts index 3f10d4b..fe15e83 100644 --- a/packages/skills/prompts/review.ts +++ b/packages/skills/prompts/review.ts @@ -1,39 +1,41 @@ // review — L1 漂移审查 prompt 模板 -// 由 svp prompt review 使用 +// 由 forge prompt review 使用 import { languageDirective } from "../../core/i18n.js"; export function reviewInstructions(language = "en"): string { - return [ - "You are reviewing a drift between L3 contract and L1 implementation.", - "The L1 code was manually modified and no longer matches the L3 spec.", - "", - "## Steps", - "", - "1. Compare L3 contract (pins, validate, constraints) with L1 actual exports", - "2. For each difference, classify as:", - " - **L3 needs update**: The code change was intentional, L3 should be updated to match", - " - **L1 is wrong**: The code change was a mistake, L1 should be reverted", - " - **Cosmetic only**: Formatting/naming difference that doesn't affect interface", - "3. Report findings — do NOT make any changes", - "", - "## Report Format", - "", - "For each difference found:", - "```", - "- [L3/L1/cosmetic] ", - " L3 says: ", - " L1 does: ", - " Recommendation: ", - "```", - "", - "## Important", - "", - "- This is a READ-ONLY review — do not modify any files", - "- Focus on interface compatibility (pins, types, exported functions)", - "- Internal implementation changes that don't affect the interface are cosmetic", - "- If L3 needs update, the user must decide — flag it for human review", - ].join("\n") + languageDirective(language); + return ( + [ + "You are reviewing a drift between L3 contract and L1 implementation.", + "The L1 code was manually modified and no longer matches the L3 spec.", + "", + "## Steps", + "", + "1. Compare L3 contract (pins, validate, constraints) with L1 actual exports", + "2. For each difference, classify as:", + " - **L3 needs update**: The code change was intentional, L3 should be updated to match", + " - **L1 is wrong**: The code change was a mistake, L1 should be reverted", + " - **Cosmetic only**: Formatting/naming difference that doesn't affect interface", + "3. Report findings — do NOT make any changes", + "", + "## Report Format", + "", + "For each difference found:", + "```", + "- [L3/L1/cosmetic] ", + " L3 says: ", + " L1 does: ", + " Recommendation: ", + "```", + "", + "## Important", + "", + "- This is a READ-ONLY review — do not modify any files", + "- Focus on interface compatibility (pins, types, exported functions)", + "- Internal implementation changes that don't affect the interface are cosmetic", + "- If L3 needs update, the user must decide — flag it for human review", + ].join("\n") + languageDirective(language) + ); } // Backward compat diff --git a/packages/skills/prompts/update-ref.ts b/packages/skills/prompts/update-ref.ts index 901d2b0..f552a6c 100644 --- a/packages/skills/prompts/update-ref.ts +++ b/packages/skills/prompts/update-ref.ts @@ -1,36 +1,38 @@ // update-ref — L4 断引用修复 prompt 模板 -// 由 svp prompt update-ref 使用 +// 由 forge prompt update-ref 使用 import { languageDirective } from "../../core/i18n.js"; export function updateRefInstructions(language = "en"): string { - return [ - "You are fixing a broken reference in an L4 flow.", - "An L4 step references an L3 block that doesn't exist.", - "", - "## Steps", - "", - "1. Examine the L4 flow and identify which step(s) have broken `blockRef` values", - "2. For each broken reference, decide:", - " - **Option A**: Create the missing L3 block with an appropriate contract", - " - **Option B**: Fix the L4 step to reference an existing L3 block", - "3. If creating a new L3 block:", - " - Infer input/output pins from the L4 dataFlows (upstream output → this block's input)", - " - Write a minimal but useful contract (validate, constraints, description)", - " - Write to `.svp/l3/.json`", - " - Run `svp rehash l3/` to fix contentHash", - "4. If fixing the L4 reference:", - " - Update the step's `blockRef` to point to the correct existing L3 block", - " - Update `dataFlows` if pin names differ", - " - Run `svp rehash l4` to fix contentHash", - "", - "## Important", - "", - "- Prefer creating the missing L3 if the intent is clear from context", - "- Only fix the reference if it's clearly a typo or rename", - "- Do NOT modify other steps — only fix the broken reference", - "- After any change, run `svp check` to verify the fix", - ].join("\n") + languageDirective(language); + return ( + [ + "You are fixing a broken reference in an L4 flow.", + "An L4 step references an L3 block that doesn't exist.", + "", + "## Steps", + "", + "1. Examine the L4 flow and identify which step(s) have broken `blockRef` values", + "2. For each broken reference, decide:", + " - **Option A**: Create the missing L3 block with an appropriate contract", + " - **Option B**: Fix the L4 step to reference an existing L3 block", + "3. If creating a new L3 block:", + " - Infer input/output pins from the L4 dataFlows (upstream output → this block's input)", + " - Write a minimal but useful contract (validate, constraints, description)", + " - Write to `.svp/l3/.json`", + " - Run `forge rehash l3/` to fix contentHash", + "4. If fixing the L4 reference:", + " - Update the step's `blockRef` to point to the correct existing L3 block", + " - Update `dataFlows` if pin names differ", + " - Run `forge rehash l4` to fix contentHash", + "", + "## Important", + "", + "- Prefer creating the missing L3 if the intent is clear from context", + "- Only fix the reference if it's clearly a typo or rename", + "- Do NOT modify other steps — only fix the broken reference", + "- After any change, run `forge check` to verify the fix", + ].join("\n") + languageDirective(language) + ); } // Backward compat diff --git a/packages/skills/rehash.ts b/packages/skills/rehash.ts index 4024e2d..2fb598a 100644 --- a/packages/skills/rehash.ts +++ b/packages/skills/rehash.ts @@ -1,5 +1,5 @@ // rehash — 重算 contentHash + 更新 revision -// AI 写完 JSON 后运行 svp rehash,自动修正 hash +// AI 写完 JSON 后运行 forge rehash,自动修正 hash // 纯函数,不做 IO import { hashL2, hashL3, hashL4, hashL5 } from "../core/hash.js"; diff --git a/packages/skills/templates/claude-md.ts b/packages/skills/templates/claude-md.ts index 2f9d854..f20e897 100644 --- a/packages/skills/templates/claude-md.ts +++ b/packages/skills/templates/claude-md.ts @@ -1,5 +1,5 @@ // claude-md — CLAUDE.md SVP section 生成 -// svp init --host claude-code 时追加到 CLAUDE.md +// forge init --host claude-code 时追加到 CLAUDE.md // // DEPRECATED: This module is a backward-compatible re-export. // New code should use adapters/claude-code.ts directly. diff --git a/packages/skills/templates/slash-commands.ts b/packages/skills/templates/slash-commands.ts index 31a39b4..2c0ca31 100644 --- a/packages/skills/templates/slash-commands.ts +++ b/packages/skills/templates/slash-commands.ts @@ -1,5 +1,5 @@ -// slash-commands — unified /svp slash command template -// svp init --host claude-code 时生成到 .claude/commands/ +// slash-commands — unified /forge slash command template +// forge init --host claude-code 时生成到 .claude/commands/ // 纯 toolchain 操作(check/view)由用户直接运行 CLI,不需要 slash command // // DEPRECATED: This module is a backward-compatible re-export. diff --git a/tests/e2e/design-l3-all-kinds.ts b/tests/e2e/design-l3-all-kinds.ts index 5231d29..ecfce05 100644 --- a/tests/e2e/design-l3-all-kinds.ts +++ b/tests/e2e/design-l3-all-kinds.ts @@ -1,7 +1,7 @@ #!/usr/bin/env tsx // design-l3-all-kinds.ts — E2E test: design-l3 for all L4 kinds (flow, event-graph, state-machine) // -// Runs `svp prompt design-l3` against an existing project, then pipes each prompt to Claude +// Runs `forge prompt design-l3` against an existing project, then pipes each prompt to Claude // to actually generate L3 blocks. Verifies that valid L3 JSON is produced for every blockRef // across all three L4 kinds. // diff --git a/tests/e2e/svp-workflow-zh.ts b/tests/e2e/svp-workflow-zh.ts index f02984f..b333eaf 100644 --- a/tests/e2e/svp-workflow-zh.ts +++ b/tests/e2e/svp-workflow-zh.ts @@ -2,12 +2,12 @@ // svp-workflow-zh.ts — E2E 测试: 全中文 SVP 设计→实现工作流(通过 Claude Code CLI) // // 与 svp-workflow.ts 相同的完整流程,但使用中文 intent 并设置 language=zh: -// 阶段 1: 初始化 → svp init --language zh -// 阶段 2: 设计 L5 → svp prompt design-l5 → claude -p → 验证 l5.json -// 阶段 3: 设计 L4 → svp prompt design-l4 → claude -p → 验证 l4/*.json -// 阶段 4: 设计 L3 → svp prompt design-l3 (逐步) → claude -p → 验证 l3/*.json -// 阶段 5: 检查 → svp check → 跨层一致性校验 -// 阶段 6: 编译 → svp prompt compile (逐个 l3) → claude -p → 验证 l2/*.json + 源码 +// 阶段 1: 初始化 → forge init --language zh +// 阶段 2: 设计 L5 → forge prompt design-l5 → claude -p → 验证 l5.json +// 阶段 3: 设计 L4 → forge prompt design-l4 → claude -p → 验证 l4/*.json +// 阶段 4: 设计 L3 → forge prompt design-l3 (逐步) → claude -p → 验证 l3/*.json +// 阶段 5: 检查 → forge check → 跨层一致性校验 +// 阶段 6: 编译 → forge prompt compile (逐个 l3) → claude -p → 验证 l2/*.json + 源码 // 阶段 7: 报告 → markdown 摘要 // // 用法: @@ -83,9 +83,9 @@ function phase1_setup(opts: CLIOptions): PhaseResult { const errors: string[] = []; if (!initResult.ok) { - errors.push(`svp init 失败: ${initResult.stderr}`); + errors.push(`forge init 失败: ${initResult.stderr}`); } else { - details.push("svp init 完成"); + details.push("forge init 完成"); } // 验证目录结构 @@ -116,7 +116,7 @@ function phase1_setup(opts: CLIOptions): PhaseResult { } steps.push({ - name: "svp init(中文) + 验证结构", + name: "forge init(中文) + 验证结构", ok: initResult.ok && errors.length === 0, durationMs: stepTimer(), details, @@ -146,7 +146,7 @@ function phase2_designL5(projectDir: string, opts: CLIOptions): PhaseResult { if (!promptResult.ok) { steps.push({ - name: "svp prompt design-l5", + name: "forge prompt design-l5", ok: false, durationMs: stepTimer(), details: [], @@ -163,7 +163,7 @@ function phase2_designL5(projectDir: string, opts: CLIOptions): PhaseResult { const hasLangDirective = prompt.includes("Chinese") || prompt.includes("中文"); steps.push({ - name: "svp prompt design-l5", + name: "forge prompt design-l5", ok: true, durationMs: stepTimer(), details: [ @@ -258,7 +258,7 @@ function phase3_designL4(projectDir: string, opts: CLIOptions): PhaseResult { if (!promptResult.ok) { steps.push({ - name: "svp prompt design-l4", + name: "forge prompt design-l4", ok: false, durationMs: stepTimer(), details: [], @@ -272,7 +272,7 @@ function phase3_designL4(projectDir: string, opts: CLIOptions): PhaseResult { const model = mapModel(complexity, opts.allSonnet); steps.push({ - name: "svp prompt design-l4", + name: "forge prompt design-l4", ok: true, durationMs: stepTimer(), details: [ @@ -458,7 +458,7 @@ function phase4_designL3(projectDir: string, opts: CLIOptions): PhaseResult { if (!promptResult.ok) { steps.push({ - name: `svp prompt design-l3 ${blockId}`, + name: `forge prompt design-l3 ${blockId}`, ok: false, durationMs: stepTimer(), details: [], @@ -523,13 +523,13 @@ function phase5_check(projectDir: string): PhaseResult { const rehashTimer = timer(); const rehashResult = runSvp(`rehash -r "${projectDir}"`, projectDir); steps.push({ - name: "svp rehash", + name: "forge rehash", ok: rehashResult.ok, durationMs: rehashTimer(), details: rehashResult.ok ? [`Rehash 完成: ${rehashResult.stdout.trim().split("\n").length} 行输出`] : [], - errors: rehashResult.ok ? [] : [`svp rehash 失败: ${rehashResult.stderr}`], + errors: rehashResult.ok ? [] : [`forge rehash 失败: ${rehashResult.stderr}`], }); const stepTimer = timer(); @@ -540,7 +540,7 @@ function phase5_check(projectDir: string): PhaseResult { let checkOk = result.ok; if (!result.ok && result.stderr) { - errors.push(`svp check 失败: ${result.stderr}`); + errors.push(`forge check 失败: ${result.stderr}`); } try { @@ -572,7 +572,7 @@ function phase5_check(projectDir: string): PhaseResult { } steps.push({ - name: "svp check --json", + name: "forge check --json", ok: checkOk, durationMs: stepTimer(), details, @@ -624,7 +624,7 @@ function phase6_compile(projectDir: string, opts: CLIOptions): PhaseResult { ok: false, durationMs: stepTimer(), details: [], - errors: [`svp prompt compile 失败: ${promptResult.stderr}`], + errors: [`forge prompt compile 失败: ${promptResult.stderr}`], }); continue; } diff --git a/tests/e2e/svp-workflow.ts b/tests/e2e/svp-workflow.ts index 68be952..cc0a740 100644 --- a/tests/e2e/svp-workflow.ts +++ b/tests/e2e/svp-workflow.ts @@ -2,12 +2,12 @@ // svp-workflow.ts — E2E test: full SVP design→implement workflow via Claude Code CLI // // Runs the complete SVP pipeline on a throwaway project: -// Phase 1: Setup → svp init -// Phase 2: Design L5 → svp prompt design-l5 → claude -p → verify l5.json -// Phase 3: Design L4 → svp prompt design-l4 → claude -p → verify l4/*.json -// Phase 4: Design L3 → svp prompt design-l3 (per step) → claude -p → verify l3/*.json -// Phase 5: Check → svp check → cross-layer consistency -// Phase 6: Compile → svp prompt compile (per l3) → claude -p → verify l2/*.json + sources +// Phase 1: Setup → forge init +// Phase 2: Design L5 → forge prompt design-l5 → claude -p → verify l5.json +// Phase 3: Design L4 → forge prompt design-l4 → claude -p → verify l4/*.json +// Phase 4: Design L3 → forge prompt design-l3 (per step) → claude -p → verify l3/*.json +// Phase 5: Check → forge check → cross-layer consistency +// Phase 6: Compile → forge prompt compile (per l3) → claude -p → verify l2/*.json + sources // Phase 7: Report → markdown summary // // Usage: @@ -74,7 +74,7 @@ function phase1_setup(opts: CLIOptions): PhaseResult { const projectDir = mkdtempSync(path.join(tmpdir(), "svp-e2e-")); const stepTimer = timer(); - // Run svp init + // Run forge init const initResult = runSvp( `init -n string-transformer -r "${projectDir}" -i "${PROJECT_INTENT}"`, projectDir, @@ -84,9 +84,9 @@ function phase1_setup(opts: CLIOptions): PhaseResult { const errors: string[] = []; if (!initResult.ok) { - errors.push(`svp init failed: ${initResult.stderr}`); + errors.push(`forge init failed: ${initResult.stderr}`); } else { - details.push("svp init completed"); + details.push("forge init completed"); } // Verify directory structure @@ -102,7 +102,7 @@ function phase1_setup(opts: CLIOptions): PhaseResult { } steps.push({ - name: "svp init + verify structure", + name: "forge init + verify structure", ok: initResult.ok && errors.length === 0, durationMs: stepTimer(), details, @@ -134,7 +134,7 @@ function phase2_designL5(projectDir: string, opts: CLIOptions): PhaseResult { if (!promptResult.ok) { steps.push({ - name: "svp prompt design-l5", + name: "forge prompt design-l5", ok: false, durationMs: stepTimer(), details: [], @@ -148,7 +148,7 @@ function phase2_designL5(projectDir: string, opts: CLIOptions): PhaseResult { const model = mapModel(complexity, opts.allSonnet); steps.push({ - name: "svp prompt design-l5", + name: "forge prompt design-l5", ok: true, durationMs: stepTimer(), details: [ @@ -241,7 +241,7 @@ function phase3_designL4(projectDir: string, opts: CLIOptions): PhaseResult { if (!promptResult.ok) { steps.push({ - name: "svp prompt design-l4", + name: "forge prompt design-l4", ok: false, durationMs: stepTimer(), details: [], @@ -255,7 +255,7 @@ function phase3_designL4(projectDir: string, opts: CLIOptions): PhaseResult { const model = mapModel(complexity, opts.allSonnet); steps.push({ - name: "svp prompt design-l4", + name: "forge prompt design-l4", ok: true, durationMs: stepTimer(), details: [ @@ -446,7 +446,7 @@ function phase4_designL3(projectDir: string, opts: CLIOptions): PhaseResult { if (!promptResult.ok) { steps.push({ - name: `svp prompt design-l3 ${blockId}`, + name: `forge prompt design-l3 ${blockId}`, ok: false, durationMs: stepTimer(), details: [], @@ -515,13 +515,13 @@ function phase5_check(projectDir: string): PhaseResult { const rehashTimer = timer(); const rehashResult = runSvp(`rehash -r "${projectDir}"`, projectDir); steps.push({ - name: "svp rehash", + name: "forge rehash", ok: rehashResult.ok, durationMs: rehashTimer(), details: rehashResult.ok ? [`Rehash completed: ${rehashResult.stdout.trim().split("\n").length} line(s) output`] : [], - errors: rehashResult.ok ? [] : [`svp rehash failed: ${rehashResult.stderr}`], + errors: rehashResult.ok ? [] : [`forge rehash failed: ${rehashResult.stderr}`], }); const stepTimer = timer(); @@ -532,7 +532,7 @@ function phase5_check(projectDir: string): PhaseResult { let checkOk = result.ok; if (!result.ok && result.stderr) { - errors.push(`svp check failed: ${result.stderr}`); + errors.push(`forge check failed: ${result.stderr}`); } // Parse JSON output @@ -566,7 +566,7 @@ function phase5_check(projectDir: string): PhaseResult { } steps.push({ - name: "svp check --json", + name: "forge check --json", ok: checkOk, durationMs: stepTimer(), details, @@ -620,7 +620,7 @@ function phase6_compile(projectDir: string, opts: CLIOptions): PhaseResult { ok: false, durationMs: stepTimer(), details: [], - errors: [`svp prompt compile failed: ${promptResult.stderr}`], + errors: [`forge prompt compile failed: ${promptResult.stderr}`], }); continue; }