@double-codeing/flow2spec 2.2.2 → 3.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +73 -54
  2. package/cli.js +254 -14
  3. package/docs/Flow2Spec-/344/275/277/347/224/250/346/241/210/344/276/213-/346/250/241/346/213/237/345/257/271/350/257/235.md +123 -134
  4. package/docs/Flow2Spec-/346/274/224/350/256/262/347/250/277.md +411 -0
  5. package/docs/Flow2Spec-/350/256/276/350/256/241/350/257/264/346/230/216.md +574 -0
  6. package/docs/Flow2Spec/344/275/277/347/224/250/350/257/264/346/230/216.md +116 -76
  7. package/docs/README-/344/275/223/347/263/273/344/270/216/345/216/237/347/220/206.md +85 -44
  8. package/docs/README-/345/221/275/344/273/244/350/257/264/346/230/216.md +548 -79
  9. package/docs/README-/347/233/256/345/275/225/344/270/216/350/267/257/345/276/204/347/272/246/345/256/232.md +33 -62
  10. package/docs/images//346/212/200/350/203/275/351/227/255/347/216/257/345/233/276.png +0 -0
  11. package/lib/agents.js +15 -3
  12. package/lib/claudeSettingsAdapter.js +114 -0
  13. package/lib/codexAgentsAdapter.js +70 -0
  14. package/lib/flow2specConfig.js +229 -0
  15. package/lib/init.js +698 -25
  16. package/package.json +2 -2
  17. package/templates/AGENTS.md +98 -0
  18. package/templates/flow2spec.config.json +9 -0
  19. package/templates/hooks/f2s-config-inject.js +181 -0
  20. package/templates/knowledge/index.md +68 -0
  21. package/templates/knowledge/manifest-matchers.json +35 -0
  22. package/templates/knowledge/manifest-routing.json +45 -0
  23. package/templates/knowledge/matchers/m-doc-routing.json +11 -0
  24. package/templates/knowledge/matchers/m-f2s-config-precheck.json +15 -0
  25. package/templates/knowledge/matchers/m-implement-from-spec.json +10 -0
  26. package/templates/{template → knowledge/template}//345/220/216/347/253/257/346/212/200/346/234/257/346/250/241/347/211/210.md +3 -2
  27. package/templates/{template → knowledge/template}//347/273/210/347/250/277/346/250/241/347/211/210.md +5 -4
  28. package/templates/knowledge/topics/f2s-config-precheck.md +24 -0
  29. package/templates/knowledge/topics/f2s-fallback-triage.md +60 -0
  30. package/templates/knowledge/topics/f2s-implement-tech-design.md +21 -0
  31. package/templates/knowledge/topics/f2s-stock-docs-vs-req-docs.md +25 -0
  32. package/templates/rules/f2s-config-check.mdc +35 -0
  33. package/templates/rules/f2s-flow2spec-unified-entry.mdc +88 -0
  34. package/templates/rules/f2s-implement-tech-design.mdc +144 -0
  35. package/templates/rules/f2s-karpathy-guidelines.mdc +77 -0
  36. package/templates/rules/f2s-knowledge-preflight.mdc +70 -0
  37. package/templates/rules/f2s-stock-docs-vs-req-docs.mdc +16 -0
  38. package/templates/rules/f2s-task.mdc +202 -0
  39. package/templates/skills/f2s-ctx-build/SKILL.md +74 -173
  40. package/templates/skills/f2s-ctx-rm/SKILL.md +39 -43
  41. package/templates/skills/f2s-doc-add/SKILL.md +69 -106
  42. package/templates/skills/f2s-doc-arch/SKILL.md +20 -9
  43. package/templates/skills/f2s-doc-final/SKILL.md +29 -21
  44. package/templates/skills/f2s-doc-pdf/SKILL.md +17 -10
  45. package/templates/skills/f2s-git-commit/SKILL.md +189 -0
  46. package/templates/skills/f2s-karpathy-guidelines/SKILL.md +20 -0
  47. package/templates/skills/f2s-kb-feat/SKILL.md +72 -50
  48. package/templates/skills/f2s-kb-fix/SKILL.md +77 -46
  49. package/templates/skills/f2s-kb-merge/SKILL.md +9 -0
  50. package/templates/skills/f2s-kb-migrate/SKILL.md +356 -0
  51. package/templates/skills/f2s-kb-sync/SKILL.md +80 -59
  52. package/templates/skills/f2s-kb-upgrade/SKILL.md +225 -0
  53. package/templates/skills/f2s-req-backend/SKILL.md +35 -12
  54. package/templates/skills/f2s-req-clarify/SKILL.md +10 -2
  55. package/templates/skills/f2s-req-plan/SKILL.md +110 -0
  56. package/templates/skills/stock-docs-vs-req-docs/SKILL.md +10 -4
  57. package/docs/images//345/216/237/347/220/206/345/233/2761.png +0 -0
  58. package/docs/images//345/216/237/347/220/206/345/233/2762.png +0 -0
  59. package/docs/images//345/221/275/344/273/244/346/230/216/347/273/206/345/233/276.png +0 -0
  60. package/docs/images//346/227/245/345/270/270/346/223/215/344/275/234/346/265/201/347/250/213/345/233/276.png +0 -0
  61. package/docs/images//347/256/200/350/277/260/345/233/276.png +0 -0
  62. package/templates/rules/implement-tech-design.mdc +0 -177
  63. package/templates/rules/stock-docs-vs-req-docs.mdc +0 -14
package/README.md CHANGED
@@ -1,93 +1,112 @@
1
1
  # Flow2Spec
2
2
 
3
- Flow2Spec 把**配置根里的知识库**(`rules/`、`skills/`、`docs-index.md`、`stock-docs/` 等)与**业务代码**串成一条**可日复一日的闭环**:先落盘可加载的约定与索引 → 日常提问与实现都**优先就着知识库走** → 随时可用 **f2s-kb-feat** / **f2s-kb-fix** 扩展或纠错并**写回**规则与技能;**实现后**(或阶段收尾)再用 **f2s-kb-sync** 将会话/现状沉淀进知识库,下一轮对话立刻继承,少在聊天与零散文档里重复对齐口径。
3
+ Flow2Spec 用于在业务仓库初始化一套可持续的 AI 协作结构:
4
4
 
5
- **闭环为什么省事**:能力拆成一组 **f2s-*** 技能(`skills/<标识>/SKILL.md`),按「当前在闭环的哪一环」选用即可;init 一次把目录与模版备好,后续主要是**对话里触发技能 + 确认大纲/路径**,不必自己拼一套文档治理流程。
5
+ - **业务知识文档**统一在 `.Knowledge/`
6
+ - **规则与技能能力**保留在各 agent 配置根(`.cursor/`、`.claude/`、`.codex/`)
6
7
 
7
- **渐进式读取带来的开发体验**:**`main.mdc`**(唯一 **alwaysApply**)先给总览与入口;**`docs-index.md`** 虽不进自动上下文,但 **`main` 生成时会写入「先打开 docs-index 再按表找 Rule/Skill」的必读约定`**,从而先索引、再专题规则/技能、再 **`stock-docs/`** 长文、**必要时才下钻业务代码**;上下文**层层加深**而非整仓硬塞,**更省 token、更少噪声**,回答更贴已写进知识库的约定与模块边界。
8
-
9
- 在**配置根的父目录**执行 **`flow2spec init`** 写入所选 AI 工具目录(默认 **`.cursor/`**,亦可 **`.claude/`**、**`.codex/`** 等)。命令与顺序见 [README-命令说明](./docs/README-命令说明.md#按使用顺序查找),目录分工见 [目录与路径约定](./docs/README-目录与路径约定.md),对话示例见 [Flow2Spec-使用案例-模拟对话](./docs/Flow2Spec-使用案例-模拟对话.md)。
8
+ 集中管理项目知识,同时不破坏各工具原生的 rules/skills 加载机制。
10
9
 
11
10
  ---
12
11
 
13
12
  ## 快速开始
14
13
 
15
14
  ```bash
16
- # 在目标代码仓库(配置根的父目录)执行(默认仅写入 .cursor/)
17
- npx @ctrip/flow2spec init
18
- # 指定 AI 工具配置目录(可多选)
19
- npx @ctrip/flow2spec init claude
20
- npx @ctrip/flow2spec init cursor claude codex
21
- # 或全局安装后
22
- npm install -g @ctrip/flow2spec
23
- flow2spec init
15
+ npx @double-codeing/flow2spec@latest init
16
+ npx @double-codeing/flow2spec@latest init cursor claude codex
24
17
  ```
25
18
 
26
- - 模板按所选 **agent** 写入对应**配置根**(如 **`.cursor/`**、**`.claude/`**、**`.codex/`**)下的 `rules/`、`skills/`、`template/` 与预建 **`stock-docs/`**(存量上下文源)、**`req-docs/`**(需求与技术方案,按代码实现)。目录分工见 [目录与路径约定](./docs/README-目录与路径约定.md)。
27
- - 工作流说明在 **`skills/<标识>/SKILL.md`**;在 Cursor 中由 Agent 按场景加载对应 Skill。
28
- - 可用 **`flow2spec --help`** 查看全部 agent 名称与示例。
19
+ 可选:全局安装 CLI 后,可在仓库根直接使用 `flow2spec init …`(与上文等价):
29
20
 
30
- **init 详解**:[Flow2Spec使用说明 - init 做了什么](./docs/Flow2Spec使用说明.md#一init-做了什么)。
21
+ ```bash
22
+ npm install -g @double-codeing/flow2spec@latest
23
+ ```
31
24
 
32
- ---
25
+ `init` 完成后的目录结构:
33
26
 
34
- ## 能力与入口(速览)
27
+ | 路径 | 用途 |
28
+ |------|------|
29
+ | `.Knowledge/stock-docs/` | 架构说明、终稿等沉淀文档 |
30
+ | `.Knowledge/req-docs/` | 需求澄清与技术方案 |
31
+ | `.Knowledge/topics/` | 主题路由摘要 |
32
+ | `.Knowledge/template/` | 终稿与技术方案模板 |
33
+ | `.Knowledge/manifest-routing.json` + `matchers/` | 机器可读路由与关键词索引 |
34
+ | `配置根/rules/` + `配置根/skills/` | 各工具规则与技能入口 |
35
+ | `flow2spec.config.json` | 控制 `subAgent`、`switchAgentVerification`、`changeTracking`(各技能独立子项),默认均为 `false` |
35
36
 
36
- | 环节 | 典型技能 / 用法 |
37
- |------|----------------|
38
- | **沉淀知识库(架构说明)** | **f2s-doc-arch** → **f2s-doc-final** → **f2s-ctx-build**:架构初稿→终稿→Rules、Skills、索引(终稿在 `stock-docs/`) |
39
- | **沉淀知识库(已落地能力→上下文)** | **f2s-doc-add**:**工作中**某能力**已做好**,用**一批相关文件路径**把它解析进知识库(初稿→终稿→Rules/Skills/索引);**与**「架构说明」**那条技能链不同** |
40
- | **按方案写代码** | **`req-docs/`** 下技术方案 MD + **`implement-tech-design`**;仅有 PDF 时可用 **f2s-doc-pdf** |
41
- | **纠错与扩展** | **f2s-kb-feat**、**f2s-kb-fix**(任意时机) |
42
- | **实现后写库** | **f2s-kb-sync**(会话/现状 → 大纲确认后写库) |
37
+ > `init` 只做结构与模板补齐,业务文档内容由 `f2s-*` 技能维护。详见 [Flow2Spec使用说明](./docs/Flow2Spec使用说明.md)。
43
38
 
44
- 更细的入参、输出与顺序:[README-命令说明](./docs/README-命令说明.md) · 使用手册:[Flow2Spec使用说明](./docs/Flow2Spec使用说明.md) · 使用案例:[Flow2Spec-使用案例-模拟对话](./docs/Flow2Spec-使用案例-模拟对话.md)
39
+ 包升级后可在业务仓库用 **`/f2s-kb-upgrade`** 对齐知识库模板与路由;细则见 [使用说明](./docs/Flow2Spec使用说明.md)。
45
40
 
46
41
  ---
47
42
 
48
- ## 原理与流程图解
43
+ ## 工作流全景
49
44
 
50
- 下图与「闭环日常流」「渐进式读取」一一对应;技能标识以 **f2s-*** 为准,与图不一致处以 [README-命令说明](./docs/README-命令说明.md) 为准。
45
+ 所有技能均以 `f2s-*` 前缀或主题名在 Agent 内触发。以下按**业务场景**分组,标明推荐执行链路与前置条件。
51
46
 
52
- ### 命令明细(名称、作用、使用时机与频率)
47
+ > **前置要求**:涉及「旧库迁移」或「包模板对齐」时,需先在本地安装最新 CLI:
48
+ > ```bash
49
+ > npm install -g @double-codeing/flow2spec@latest
50
+ > ```
51
+ > 其余场景以仓库内已初始化的规则与技能为准。
53
52
 
54
- ![命令明细图:f2s 技能一览与使用频率](./docs/images/命令明细图.png)
53
+ ### 一、需求交付链路
55
54
 
56
- ### 日常操作与项目仓库(知识库与业务代码)
55
+ PRD 到代码落地的完整路径。
57
56
 
58
- ![日常操作流程图:各技能与知识库、业务代码的关系](./docs/images/日常操作流程图.png)
57
+ > **任务清单控制**:`f2s-req-plan` **强制**创建任务清单;`f2s-implement-tech-design` 是否使用任务清单取决于 `changeTracking.implement` 配置。
59
58
 
60
- ### init 与配置根结构(以 `.cursor` 为例)
59
+ | 场景 | 执行链 | 产出 |
60
+ |------|--------|------|
61
+ | 有 PRD,需澄清后出方案并落地 | `f2s-req-clarify` → `f2s-req-backend` → `f2s-implement-tech-design` → `f2s-kb-feat` | 澄清纪要 → 技术方案 → 实现+知识库 |
62
+ | 已有方案,需强制任务清单后实现 | `f2s-req-plan` | 可确认任务清单与实现编排 |
61
63
 
62
- ![原理图1:flow2spec init 与配置根内知识库、技能与模版](./docs/images/原理图1.png)
64
+ ### 二、知识沉淀链路
63
65
 
64
- ### main.mdc 与 docs-index.md
66
+ 将非结构化信息(口述、草稿、外部文档、代码)转化为可检索的知识资产。
65
67
 
66
- ![原理图2:规则总入口与文档索引](./docs/images/原理图2.png)
68
+ | 场景 | 执行链 | 产出 |
69
+ |------|--------|------|
70
+ | 从口述/草稿到终稿 | `f2s-doc-arch` → `f2s-doc-final` → `f2s-ctx-build` | 架构初稿 → 规范终稿 → 主题路由 |
71
+ | 外部文档转知识库 | `f2s-doc-final` → `f2s-ctx-build` | 可检索 Markdown + 路由索引 |
72
+ | 存量代码/散稿补录 | `f2s-doc-add` 或 `f2s-kb-sync` | 自动提取能力 → 主题索引 |
67
73
 
68
- ### 简述:知识库与代码闭环
74
+ ### 三、日常协作
69
75
 
70
- ![简述图:生成知识库 → 基于知识库实现 → 基于实现反哺知识库](./docs/images/简述图.png)
76
+ 缺陷修复、迭代与上下文同步。
71
77
 
72
- ---
78
+ | 场景 | 技能 |
79
+ |------|------|
80
+ | 修复缺陷 | `f2s-kb-fix` |
81
+ | 新增功能 | `f2s-kb-feat` |
82
+ | 同步已实现能力 | `f2s-kb-sync` |
83
+ | 解决合并冲突 | `f2s-kb-merge` |
73
84
 
74
- ## 文档导航
85
+ ### 四、仓库治理
75
86
 
76
- | 文档 | 说明 |
77
- |------|------|
78
- | [**Flow2Spec使用说明**](./docs/Flow2Spec使用说明.md) | **使用手册**:init、目录约定、推荐顺序、典型流程、技能与工作流、常见问题 |
79
- | [**Flow2Spec-使用案例-模拟对话**](./docs/Flow2Spec-使用案例-模拟对话.md) | **对话版式与场景**:真实输入、命令解释、渐进读取与 **f2s-*** 示例 |
80
- | [README-命令说明](./docs/README-命令说明.md) | 各命令入参/输出、**按使用顺序查找**、快速参考 |
81
- | [README-目录与路径约定](./docs/README-目录与路径约定.md) | **配置根**下 `stock-docs/`、`req-docs/` 等结构、路径与链接约定、文档产物阶段 |
82
- | [README-体系与原理](./docs/README-体系与原理.md) | 架构、设计原则、main 与 docs-index 区别 |
87
+ 一次性或周期性的结构化维护。
88
+
89
+ | 场景 | 技能 | 注意事项 |
90
+ |------|------|----------|
91
+ | 旧版迁移(rules/skills 散稿 → `.Knowledge`) | `f2s-kb-migrate` | 一次性;执行前备份 |
92
+ | 模板对齐(包升级后同步) | `f2s-kb-upgrade` | 可重复执行 |
83
93
 
84
94
  ---
85
95
 
86
- ## CLI
96
+ ## 关键原则
87
97
 
88
- | 命令 | 说明 |
89
- |------|------|
90
- | `flow2spec init [agent ...]` | 将模板写入所选配置根(默认 `cursor` `.cursor/`);详见 **`flow2spec --help`** |
91
- | `flow2spec --help` | 查看用法、可选 agent(cursor / claude / codex)与示例 |
98
+ 1. `.Knowledge/` 只放业务文档与索引,不放规则执行文件。
99
+ 2. `rules/` `skills/` 始终在配置根,保证 Claude/Cursor/Codex 按各自方式加载。
100
+ 3. Codex 不读取 `rules/` 目录,通过 `.codex/AGENTS.md` + `skills/ + .codex/topics/*.md`承载约束入口。
101
+
102
+ ---
103
+
104
+ ## 文档导航
92
105
 
93
- 技能列表与速查见 [Flow2Spec使用说明](./docs/Flow2Spec使用说明.md)
106
+ - [Flow2Spec使用说明](./docs/Flow2Spec使用说明.md)
107
+ - [README-命令说明](./docs/README-命令说明.md)
108
+ - [README-目录与路径约定](./docs/README-目录与路径约定.md)
109
+ - [README-体系与原理](./docs/README-体系与原理.md)
110
+ - [Flow2Spec-使用案例-模拟对话](./docs/Flow2Spec-使用案例-模拟对话.md)
111
+ - [Flow2Spec-设计说明](./docs/Flow2Spec-设计说明.md)
112
+ - [Flow2Spec-演讲稿](./docs/Flow2Spec-演讲稿.md)
package/cli.js CHANGED
@@ -1,33 +1,56 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ const path = require("path");
4
+ const fs = require("fs");
5
+ const readline = require("readline");
3
6
  const runInit = require("./lib/init");
4
7
  const { AGENTS } = require("./lib/agents");
8
+ const {
9
+ loadFlow2specConfig,
10
+ CONFIG_FILENAME,
11
+ CONFIG_FIELDS,
12
+ getMissingConfigFields,
13
+ } = require("./lib/flow2specConfig");
5
14
 
6
15
  const args = process.argv.slice(2);
7
16
  const sub = args[0];
8
17
 
9
18
  const agentList = Object.entries(AGENTS)
10
- .map(([id, { label }]) => `${id}(${label})`)
11
- .join("");
19
+ .map(([id, { label }]) => `${id}(${label})`)
20
+ .join(", ");
12
21
 
13
22
  const help = `
14
- Flow2Spec - 文档前置工作流(AI 配置模板)
23
+ Flow2Spec - 统一知识库工作流(AI 配置入口)
15
24
 
16
25
  用法:
17
- flow2spec init [agent ...] 在当前项目初始化:将模板写入所选 AI 工具配置目录
26
+ flow2spec init [agent ...] [--reset-knowledge] [--yes] 在当前项目初始化:写入 .Knowledge 与所选 agent 入口
27
+ flow2spec config 打印项目根 ${CONFIG_FILENAME} 的解析结果(缺省值合并后)
18
28
  flow2spec --help 显示本说明
19
29
 
20
- agent(可多个,空格分隔;省略时默认为 cursor):
30
+ agent(可多个,空格分隔;省略时交互选择):
21
31
  ${agentList}
22
32
 
23
33
  示例:
24
- flow2spec init # 仅写入 .cursor/(Cursor)
25
- flow2spec init claude # 仅写入 .claude/
34
+ flow2spec init # 交互选择工具和配置
35
+ flow2spec init claude # 直接写入 .claude/,跳过工具选择
26
36
  flow2spec init cursor claude # 同时写入 .cursor/ 与 .claude/
37
+ flow2spec init --yes # 跳过所有问答,使用默认值(适合 CI)
38
+ flow2spec init --reset-knowledge # 强制用模板覆盖 .Knowledge(谨慎)
27
39
 
28
40
  init 会:
29
- 1. templates/ 下内容复制到各所选 agent 的配置根目录下的 rules、skills、template(及预建 stock-docs/、req-docs/)。Claude Code(.claude/)下规则为 .md,frontmatter 路径键为 paths(由模板 .mdc 的 globs 自动转换);Cursor(.cursor/)下仍为 .mdc 与 globs。
30
- 2. 工作流说明位于 skills 各子目录的 SKILL.md;写入 .claude/.codex 时主要为统一存放规则、技能与模版,供对应工具按各自方式加载
41
+ 1. 交互询问要初始化的 AI 工具(cursor / claude / codex,可多选);已通过参数指定则跳过。
42
+ --yes 或非 TTY 环境时跳过问答,使用默认值。
43
+ 2. 对 ${CONFIG_FILENAME} 中缺失的配置字段逐项提问(已有字段不覆盖)。
44
+ 传 --yes 时所有缺失字段使用默认值(均为 false)。
45
+ 3. 默认仅补齐 .Knowledge 缺失模板,并对路由清单做包级/结构增量对齐(manifest-routing + matcherPath 分片;关键词仅写在 matchers/*.json);不替代 f2s-* 对业务文档与路由内容的写入。
46
+ 传 --reset-knowledge 时才会强制用模板覆盖 .Knowledge 中模板承载部分。
47
+ 4. 在各 agent 配置根写入 rules、skills(Claude 规则自动转 .md;Codex 生成 AGENTS.md 汇总)。
48
+ Claude 额外写入 .claude/hooks/f2s-config-inject.js 与 .claude/settings.json(PreToolUse hook),
49
+ 在调用 f2s-* Skill 时注入配置摘要;配置缺失、JSON 无效或 hook 异常时也会注入默认语义说明,避免静默。
50
+ Cursor 额外写入 f2s-config-check.mdc(alwaysApply),强制在技能首步读取配置文件。
51
+ Codex 的 AGENTS.md 顶部包含强制前置步骤说明。
52
+ 5. 每次 init 将包内 templates/knowledge/index.md 复制到 .Knowledge/template/index.template.md,供 f2s-kb-upgrade 技能与 .Knowledge/index.md 对照;不自动改写 index.md。(「知识库升级」指 f2s-kb-upgrade 技能,init 本身不是升级命令。)
53
+ 6. 规则与技能在各 agent 配置根加载;其他模版类文件在 .Knowledge/template/ 等目录。
31
54
 
32
55
  更多说明见 README.md 或 docs/Flow2Spec使用说明.md
33
56
  `;
@@ -37,19 +60,236 @@ if (sub === "--help" || sub === "-h" || !sub) {
37
60
  process.exit(0);
38
61
  }
39
62
 
63
+ if (sub === "config") {
64
+ const cwd = process.cwd();
65
+ const abs = path.join(cwd, CONFIG_FILENAME);
66
+ try {
67
+ const cfg = loadFlow2specConfig(cwd);
68
+ console.log(JSON.stringify({ configPath: abs, ...cfg }, null, 2));
69
+ } catch (e) {
70
+ console.error(e.message || e);
71
+ process.exit(1);
72
+ }
73
+ process.exit(0);
74
+ }
75
+
40
76
  if (sub === "init") {
41
- const agentArgs = args.slice(1);
42
- runInit(process.cwd(), agentArgs)
43
- .then((ids) => {
77
+ const rawArgs = args.slice(1);
78
+ const overwriteKnowledge = rawArgs.includes("--reset-knowledge");
79
+ const skipPrompts = rawArgs.includes("--yes") || rawArgs.includes("-y");
80
+ const agentArgs = rawArgs.filter(
81
+ (a) => a !== "--reset-knowledge" && a !== "--yes" && a !== "-y",
82
+ );
83
+
84
+ const cwd = process.cwd();
85
+
86
+ // ── 清除已输出的 n 行(用于多选 UI 重绘)
87
+ function clearLines(n) {
88
+ if (n <= 0) return;
89
+ process.stdout.write(`\x1b[${n}A\x1b[0J`);
90
+ }
91
+
92
+ /**
93
+ * 多选 UI(raw mode):箭头键移动,空格选/取消,回车确认。
94
+ * 非 TTY 环境直接返回默认选中项。
95
+ */
96
+ async function promptMultiSelect(title, items, defaultSelected = []) {
97
+ if (!process.stdin.isTTY || skipPrompts) {
98
+ return defaultSelected.length ? defaultSelected : [items[0].value];
99
+ }
100
+
101
+ const selected = new Set(defaultSelected.length ? defaultSelected : [items[0].value]);
102
+ let cursor = 0;
103
+ let rendered = 0;
104
+
105
+ function render() {
106
+ if (rendered > 0) clearLines(rendered);
107
+ const lines = [];
108
+ lines.push(` ${title}`);
109
+ for (let i = 0; i < items.length; i++) {
110
+ const sel = selected.has(items[i].value);
111
+ const check = sel ? "\x1b[32m◉\x1b[0m" : "○";
112
+ const arr = i === cursor ? "\x1b[36m›\x1b[0m" : " ";
113
+ const label = items[i].label.padEnd(10);
114
+ const desc = items[i].desc ? ` \x1b[2m${items[i].desc}\x1b[0m` : "";
115
+ lines.push(` ${arr} ${check} ${label}${desc}`);
116
+ }
117
+ lines.push("");
118
+ lines.push(" \x1b[2m↑↓ 移动 空格 选/取消 回车 确认\x1b[0m");
119
+ rendered = lines.length;
120
+ process.stdout.write(lines.join("\n") + "\n");
121
+ }
122
+
123
+ render();
124
+
125
+ return new Promise((resolve) => {
126
+ function onKey(str, key) {
127
+ if (!key) return;
128
+ if (key.ctrl && key.name === "c") process.exit(0);
129
+
130
+ if (key.name === "up") {
131
+ cursor = (cursor - 1 + items.length) % items.length;
132
+ render();
133
+ } else if (key.name === "down") {
134
+ cursor = (cursor + 1) % items.length;
135
+ render();
136
+ } else if (key.name === "space") {
137
+ const val = items[cursor].value;
138
+ if (selected.has(val)) selected.delete(val);
139
+ else selected.add(val);
140
+ render();
141
+ } else if (key.name === "return") {
142
+ process.stdin.removeListener("keypress", onKey);
143
+ const result = selected.size ? [...selected] : [items[0].value];
144
+ if (rendered > 0) clearLines(rendered);
145
+ const labels = result
146
+ .map((v) => items.find((i) => i.value === v)?.value)
147
+ .join(", ");
148
+ process.stdout.write(` ${title} \x1b[32m${labels}\x1b[0m\n`);
149
+ resolve(result);
150
+ }
151
+ }
152
+ process.stdin.on("keypress", onKey);
153
+ });
154
+ }
155
+
156
+ /**
157
+ * 单键 y/n 问答(raw mode)。
158
+ * 非 TTY 或 skipPrompts 时直接返回默认值。
159
+ */
160
+ async function promptBooleanKey(question, defaultValue = false) {
161
+ if (!process.stdin.isTTY || skipPrompts) return defaultValue;
162
+
163
+ const hint = defaultValue
164
+ ? "\x1b[2m[Y/n]\x1b[0m"
165
+ : "\x1b[2m[y/N]\x1b[0m";
166
+ process.stdout.write(` ${question} ${hint} `);
167
+
168
+ return new Promise((resolve) => {
169
+ process.stdin.once("keypress", function (str, key) {
170
+ if (key && key.ctrl && key.name === "c") process.exit(0);
171
+ let result;
172
+ if (!str || str.trim() === "" || key?.name === "return") {
173
+ result = defaultValue;
174
+ } else {
175
+ result = str.trim().toLowerCase() === "y";
176
+ }
177
+ process.stdout.write((result ? "\x1b[32my\x1b[0m" : "n") + "\n");
178
+ resolve(result);
179
+ });
180
+ });
181
+ }
182
+
183
+ async function collectInitOptions() {
184
+ const needAgentPrompt = agentArgs.length === 0 && !skipPrompts;
185
+ const missingFields = getMissingConfigFields(cwd);
186
+ const needConfigPrompt = missingFields.length > 0;
187
+
188
+ // 没有任何需要处理的事情
189
+ if (!needAgentPrompt && !needConfigPrompt) {
190
+ return { configValues: undefined, chosenAgents: agentArgs };
191
+ }
192
+
193
+ // --yes 模式:缺失字段直接用默认值,不弹交互
194
+ if (skipPrompts) {
195
+ const configValues = needConfigPrompt
196
+ ? Object.fromEntries(missingFields.map((f) => [f.key, f.default]))
197
+ : undefined;
198
+ return { configValues, chosenAgents: agentArgs };
199
+ }
200
+
201
+ const isInteractive = process.stdin.isTTY;
202
+ if (isInteractive) {
203
+ readline.emitKeypressEvents(process.stdin);
204
+ process.stdin.setRawMode(true);
205
+ process.stdin.resume();
206
+ }
207
+
208
+ let chosenAgents = agentArgs;
209
+ let configValues;
210
+
211
+ try {
212
+ process.stdout.write("\n");
213
+
214
+ if (needAgentPrompt) {
215
+ const agentItems = Object.entries(AGENTS).map(([id, { label }]) => ({
216
+ value: id,
217
+ label: id,
218
+ desc: label,
219
+ }));
220
+ chosenAgents = await promptMultiSelect(
221
+ "选择要初始化的 AI 工具(可多选)",
222
+ agentItems,
223
+ ["cursor"],
224
+ );
225
+ }
226
+
227
+ if (needConfigPrompt) {
228
+ if (needAgentPrompt) process.stdout.write("\n");
229
+ const isFirstTime = missingFields.length === CONFIG_FIELDS.length;
230
+ process.stdout.write(
231
+ ` 配置 ${CONFIG_FILENAME}${isFirstTime ? "(首次创建)" : "(补充新增字段)"}:\n\n`,
232
+ );
233
+ const values = {};
234
+ for (const field of missingFields) {
235
+ values[field.key] = await promptBooleanKey(
236
+ field.question,
237
+ field.default,
238
+ );
239
+ }
240
+ configValues = values;
241
+ }
242
+ } finally {
243
+ if (isInteractive) {
244
+ process.stdin.setRawMode(false);
245
+ process.stdin.pause();
246
+ }
247
+ }
248
+
249
+ process.stdout.write("\n");
250
+ return { configValues, chosenAgents };
251
+ }
252
+
253
+ collectInitOptions()
254
+ .then(({ configValues, chosenAgents }) =>
255
+ runInit(cwd, chosenAgents, { overwriteKnowledge, configValues }),
256
+ )
257
+ .then(({ ids, knowledgeResult, routingUpgrade, indexSnapshot, projectConfig, claudeHooksResult }) => {
44
258
  const lines = ids.map((id) => {
45
259
  const { root, label } = AGENTS[id];
46
- return ` - ${root}/:(${label})rules、skills、template、stock-docs、req-docs(预建)`;
260
+ if (id === "codex")
261
+ return ` - ${root}/:(${label})AGENTS.md、skills/`;
262
+ if (id === "claude") {
263
+ const hookLine = claudeHooksResult?.settingsChanged
264
+ ? "rules/、skills/、hooks/f2s-config-inject.js、settings.json(已写入 f2s PreToolUse hook)"
265
+ : "rules/、skills/(settings.json 中 f2s hook 已存在,跳过)";
266
+ return ` - ${root}/:(${label})${hookLine}`;
267
+ }
268
+ return ` - ${root}/:(${label})rules/、skills/`;
47
269
  });
270
+ const knowledgeLine = overwriteKnowledge
271
+ ? " - .Knowledge/:已按 --reset-knowledge 强制覆盖模板"
272
+ : ` - .Knowledge/:保留已有内容,补齐缺失模板(新增 ${knowledgeResult?.written || 0},跳过 ${knowledgeResult?.skipped || 0})`;
273
+ const routingLine = overwriteKnowledge
274
+ ? " - .Knowledge/manifest-routing.json + .Knowledge/matchers/*:已随 reset 覆盖到模板版本(不再写入 manifest-matchers.json)"
275
+ : routingUpgrade?.upgraded
276
+ ? " - 路由清单已与模板增量对齐"
277
+ : " - 路由清单已是最新能力路由,无需变更";
278
+ const indexLine =
279
+ indexSnapshot?.written === false
280
+ ? ` - .Knowledge/template/index.template.md:未复制(${indexSnapshot?.reason || "skip"})`
281
+ : " - .Knowledge/template/index.template.md:已从包内 templates/knowledge/index.md 复制(与 .Knowledge/index.md 对照见 f2s-kb-upgrade 技能)";
282
+ const pc = projectConfig || {};
283
+ const configLine = ` - ${CONFIG_FILENAME}:subAgent=${Boolean(pc.subAgent)}, switchAgentVerification=${Boolean(pc.switchAgentVerification)}`;
48
284
  console.log(`
49
285
  ✓ Flow2Spec init 完成
286
+ ${knowledgeLine}
287
+ ${routingLine}
288
+ ${indexLine}
289
+ ${configLine}
50
290
  ${lines.join("\n")}
51
291
 
52
- Cursor 中可通过 Agent Skills 加载 skills/ 目录下工作流(配置根为 .cursor 时)。建议阅读 README 或 docs/Flow2Spec使用说明.md
292
+ 建议阅读 README 或 docs/Flow2Spec使用说明.md,按「规则在配置根、文档在 .Knowledge」的方式使用。
53
293
  `);
54
294
  })
55
295
  .catch((e) => {