@double-codeing/flow2spec 2.2.3 → 3.0.8
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.
- package/README.md +77 -53
- package/cli.js +254 -14
- 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
- package/docs/Flow2Spec-/350/256/276/350/256/241/350/257/264/346/230/216.md +574 -0
- package/docs/Flow2Spec/344/275/277/347/224/250/350/257/264/346/230/216.md +116 -76
- package/docs/README-/344/275/223/347/263/273/344/270/216/345/216/237/347/220/206.md +85 -44
- package/docs/README-/345/221/275/344/273/244/350/257/264/346/230/216.md +548 -79
- 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
- package/lib/agents.js +15 -3
- package/lib/claudeSettingsAdapter.js +114 -0
- package/lib/codexAgentsAdapter.js +70 -0
- package/lib/flow2specConfig.js +229 -0
- package/lib/init.js +698 -25
- package/package.json +2 -2
- package/templates/AGENTS.md +98 -0
- package/templates/flow2spec.config.json +9 -0
- package/templates/hooks/f2s-config-inject.js +181 -0
- package/templates/knowledge/index.md +68 -0
- package/templates/knowledge/manifest-matchers.json +35 -0
- package/templates/knowledge/manifest-routing.json +45 -0
- package/templates/knowledge/matchers/m-doc-routing.json +11 -0
- package/templates/knowledge/matchers/m-f2s-config-precheck.json +15 -0
- package/templates/knowledge/matchers/m-implement-from-spec.json +10 -0
- 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
- package/templates/{template → knowledge/template}//347/273/210/347/250/277/346/250/241/347/211/210.md +5 -4
- package/templates/knowledge/topics/f2s-config-precheck.md +24 -0
- package/templates/knowledge/topics/f2s-fallback-triage.md +60 -0
- package/templates/knowledge/topics/f2s-implement-tech-design.md +21 -0
- package/templates/knowledge/topics/f2s-stock-docs-vs-req-docs.md +25 -0
- package/templates/rules/f2s-config-check.mdc +35 -0
- package/templates/rules/f2s-flow2spec-unified-entry.mdc +88 -0
- package/templates/rules/f2s-implement-tech-design.mdc +144 -0
- package/templates/rules/f2s-karpathy-guidelines.mdc +77 -0
- package/templates/rules/f2s-knowledge-preflight.mdc +70 -0
- package/templates/rules/f2s-stock-docs-vs-req-docs.mdc +16 -0
- package/templates/rules/f2s-task.mdc +202 -0
- package/templates/skills/f2s-ctx-build/SKILL.md +74 -173
- package/templates/skills/f2s-ctx-rm/SKILL.md +39 -43
- package/templates/skills/f2s-doc-add/SKILL.md +69 -106
- package/templates/skills/f2s-doc-arch/SKILL.md +20 -9
- package/templates/skills/f2s-doc-final/SKILL.md +29 -21
- package/templates/skills/f2s-doc-pdf/SKILL.md +17 -10
- package/templates/skills/f2s-git-commit/SKILL.md +189 -0
- package/templates/skills/f2s-karpathy-guidelines/SKILL.md +20 -0
- package/templates/skills/f2s-kb-feat/SKILL.md +72 -50
- package/templates/skills/f2s-kb-fix/SKILL.md +77 -46
- package/templates/skills/f2s-kb-merge/SKILL.md +9 -0
- package/templates/skills/f2s-kb-migrate/SKILL.md +356 -0
- package/templates/skills/f2s-kb-sync/SKILL.md +80 -59
- package/templates/skills/f2s-kb-upgrade/SKILL.md +225 -0
- package/templates/skills/f2s-req-backend/SKILL.md +35 -12
- package/templates/skills/f2s-req-clarify/SKILL.md +10 -2
- package/templates/skills/f2s-req-plan/SKILL.md +110 -0
- package/templates/skills/stock-docs-vs-req-docs/SKILL.md +10 -4
- package/docs/images//345/216/237/347/220/206/345/233/2761.png +0 -0
- package/docs/images//345/216/237/347/220/206/345/233/2762.png +0 -0
- package/docs/images//345/221/275/344/273/244/346/230/216/347/273/206/345/233/276.png +0 -0
- 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
- package/docs/images//347/256/200/350/277/260/345/233/276.png +0 -0
- package/templates/rules/implement-tech-design.mdc +0 -177
- package/templates/rules/stock-docs-vs-req-docs.mdc +0 -14
package/README.md
CHANGED
|
@@ -1,93 +1,117 @@
|
|
|
1
1
|
# Flow2Spec
|
|
2
2
|
|
|
3
|
-
Flow2Spec
|
|
3
|
+
Flow2Spec 用于在业务仓库初始化一套可持续的 AI 协作结构:
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
- **业务知识文档**统一在 `.Knowledge/`
|
|
6
|
+
- **规则与技能能力**保留在各 agent 配置根(`.cursor/`、`.claude/`、`.codex/`)
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
集中管理项目知识,同时不破坏各工具原生的 rules/skills 加载机制。
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
> 🎬 **在线演示**:组内分享用的 13 页 HTML PPT(脱敏版)——**<https://lands-1203.github.io/Flow2Spec/>**
|
|
11
|
+
> `←` `→` 翻页,`S` 打开演讲者模式。源文件见 [presentations/flow2spec-intro-public/](./presentations/flow2spec-intro-public/)。
|
|
10
12
|
|
|
11
13
|
---
|
|
12
14
|
|
|
13
15
|
## 快速开始
|
|
14
16
|
|
|
15
17
|
```bash
|
|
16
|
-
|
|
17
|
-
npx @double-codeing/flow2spec init
|
|
18
|
-
# 指定 AI 工具配置目录(可多选)
|
|
19
|
-
npx @double-codeing/flow2spec init claude
|
|
20
|
-
npx @double-codeing/flow2spec init cursor claude codex
|
|
21
|
-
# 或全局安装后
|
|
22
|
-
npm install -g @double-codeing/flow2spec
|
|
23
|
-
flow2spec init
|
|
18
|
+
npx @double-codeing/flow2spec@latest init
|
|
19
|
+
npx @double-codeing/flow2spec@latest init cursor claude codex
|
|
24
20
|
```
|
|
25
21
|
|
|
26
|
-
|
|
27
|
-
- 工作流说明在 **`skills/<标识>/SKILL.md`**;在 Cursor 中由 Agent 按场景加载对应 Skill。
|
|
28
|
-
- 可用 **`flow2spec --help`** 查看全部 agent 名称与示例。
|
|
22
|
+
可选:全局安装 CLI 后,可在仓库根直接使用 `flow2spec init …`(与上文等价):
|
|
29
23
|
|
|
30
|
-
|
|
24
|
+
```bash
|
|
25
|
+
npm install -g @double-codeing/flow2spec@latest
|
|
26
|
+
```
|
|
31
27
|
|
|
32
|
-
|
|
28
|
+
`init` 完成后的目录结构:
|
|
33
29
|
|
|
34
|
-
|
|
30
|
+
| 路径 | 用途 |
|
|
31
|
+
| ------------------------------------------------ | ------------------------------------------------------------------------------------------------ |
|
|
32
|
+
| `.Knowledge/stock-docs/` | 架构说明、终稿等沉淀文档 |
|
|
33
|
+
| `.Knowledge/req-docs/` | 需求澄清与技术方案 |
|
|
34
|
+
| `.Knowledge/topics/` | 主题路由摘要 |
|
|
35
|
+
| `.Knowledge/template/` | 终稿与技术方案模板 |
|
|
36
|
+
| `.Knowledge/manifest-routing.json` + `matchers/` | 机器可读路由与关键词索引 |
|
|
37
|
+
| `配置根/rules/` + `配置根/skills/` | 各工具规则与技能入口 |
|
|
38
|
+
| `flow2spec.config.json` | 控制 `subAgent`、`switchAgentVerification`、`changeTracking`(各技能独立子项),默认均为 `false` |
|
|
35
39
|
|
|
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**(会话/现状 → 大纲确认后写库) |
|
|
40
|
+
> `init` 只做结构与模板补齐,业务文档内容由 `f2s-*` 技能维护。详见 [Flow2Spec使用说明](./docs/Flow2Spec使用说明.md)。
|
|
43
41
|
|
|
44
|
-
|
|
42
|
+
包升级后可在业务仓库用 **`/f2s-kb-upgrade`** 对齐知识库模板与路由;细则见 [使用说明](./docs/Flow2Spec使用说明.md)。
|
|
45
43
|
|
|
46
44
|
---
|
|
47
45
|
|
|
48
|
-
##
|
|
46
|
+
## 工作流全景
|
|
49
47
|
|
|
50
|
-
|
|
48
|
+
所有技能均以 `f2s-*` 前缀或主题名在 Agent 内触发。以下按**业务场景**分组,标明推荐执行链路与前置条件。
|
|
51
49
|
|
|
52
|
-
|
|
50
|
+
> **前置要求**:涉及「旧库迁移」或「包模板对齐」时,需先在本地安装最新 CLI:
|
|
51
|
+
>
|
|
52
|
+
> ```bash
|
|
53
|
+
> npm install -g @double-codeing/flow2spec@latest
|
|
54
|
+
> ```
|
|
55
|
+
>
|
|
56
|
+
> 其余场景以仓库内已初始化的规则与技能为准。
|
|
53
57
|
|
|
54
|
-
|
|
58
|
+
### 一、需求交付链路
|
|
55
59
|
|
|
56
|
-
|
|
60
|
+
从 PRD 到代码落地的完整路径。
|
|
57
61
|
|
|
58
|
-
|
|
62
|
+
> **任务清单控制**:`f2s-req-plan` **强制**创建任务清单;`f2s-implement-tech-design` 是否使用任务清单取决于 `changeTracking.implement` 配置。
|
|
59
63
|
|
|
60
|
-
|
|
64
|
+
| 场景 | 执行链 | 产出 |
|
|
65
|
+
| ------------------------------ | ----------------------------------------------------------------------------------- | --------------------------------- |
|
|
66
|
+
| 有 PRD,需澄清后出方案并落地 | `f2s-req-clarify` → `f2s-req-backend` → `f2s-implement-tech-design` → `f2s-kb-feat` | 澄清纪要 → 技术方案 → 实现+知识库 |
|
|
67
|
+
| 已有方案,需强制任务清单后实现 | `f2s-req-plan`
|
|
68
|
+
| 可确认任务清单与实现编排 |
|
|
61
69
|
|
|
62
|
-
|
|
70
|
+
### 二、知识沉淀链路
|
|
63
71
|
|
|
64
|
-
|
|
72
|
+
将非结构化信息(口述、草稿、外部文档、代码)转化为可检索的知识资产。
|
|
65
73
|
|
|
66
|
-
|
|
74
|
+
| 场景 | 执行链 | 产出 |
|
|
75
|
+
| ----------------- | -------------------------------------------------- | ------------------------------ |
|
|
76
|
+
| 从口述/草稿到终稿 | `f2s-doc-arch` → `f2s-doc-final` → `f2s-ctx-build` | 架构初稿 → 规范终稿 → 主题路由 |
|
|
77
|
+
| 外部文档转知识库 | `f2s-doc-final` → `f2s-ctx-build` | 可检索 Markdown + 路由索引 |
|
|
78
|
+
| 存量代码/散稿补录 | `f2s-doc-add` 或 `f2s-kb-sync` | 自动提取能力 → 主题索引 |
|
|
67
79
|
|
|
68
|
-
###
|
|
80
|
+
### 三、日常协作
|
|
69
81
|
|
|
70
|
-
|
|
82
|
+
缺陷修复、迭代与上下文同步。
|
|
71
83
|
|
|
72
|
-
|
|
84
|
+
| 场景 | 技能 |
|
|
85
|
+
| -------------- | -------------- |
|
|
86
|
+
| 修复缺陷 | `f2s-kb-fix` |
|
|
87
|
+
| 新增功能 | `f2s-kb-feat` |
|
|
88
|
+
| 同步已实现能力 | `f2s-kb-sync` |
|
|
89
|
+
| 解决合并冲突 | `f2s-kb-merge` |
|
|
73
90
|
|
|
74
|
-
|
|
91
|
+
### 四、仓库治理
|
|
75
92
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
|
79
|
-
|
|
|
80
|
-
|
|
|
81
|
-
|
|
|
82
|
-
| [README-体系与原理](./docs/README-体系与原理.md) | 架构、设计原则、main 与 docs-index 区别 |
|
|
93
|
+
一次性或周期性的结构化维护。
|
|
94
|
+
|
|
95
|
+
| 场景 | 技能 | 注意事项 |
|
|
96
|
+
| -------------------------------------------- | ---------------- | ------------------ |
|
|
97
|
+
| 旧版迁移(rules/skills 散稿 → `.Knowledge`) | `f2s-kb-migrate` | 一次性;执行前备份 |
|
|
98
|
+
| 模板对齐(包升级后同步) | `f2s-kb-upgrade` | 可重复执行 |
|
|
83
99
|
|
|
84
100
|
---
|
|
85
101
|
|
|
86
|
-
##
|
|
102
|
+
## 关键原则
|
|
103
|
+
|
|
104
|
+
1. `.Knowledge/` 只放业务文档与索引,不放规则执行文件。
|
|
105
|
+
2. `rules/` `skills/` 始终在配置根,保证 Claude/Cursor/Codex 按各自方式加载。
|
|
106
|
+
3. Codex 不读取 `rules/` 目录,通过 `.codex/AGENTS.md` + `skills/ + .codex/topics/*.md`承载约束入口。
|
|
87
107
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
| `flow2spec --help` | 查看用法、可选 agent(cursor / claude / codex)与示例 |
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 文档导航
|
|
92
111
|
|
|
93
|
-
|
|
112
|
+
- [Flow2Spec使用说明](./docs/Flow2Spec使用说明.md)
|
|
113
|
+
- [README-命令说明](./docs/README-命令说明.md)
|
|
114
|
+
- [README-目录与路径约定](./docs/README-目录与路径约定.md)
|
|
115
|
+
- [README-体系与原理](./docs/README-体系与原理.md)
|
|
116
|
+
- [Flow2Spec-使用案例-模拟对话](./docs/Flow2Spec-使用案例-模拟对话.md)
|
|
117
|
+
- [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}
|
|
11
|
-
.join("
|
|
19
|
+
.map(([id, { label }]) => `${id}(${label})`)
|
|
20
|
+
.join(", ");
|
|
12
21
|
|
|
13
22
|
const help = `
|
|
14
|
-
Flow2Spec -
|
|
23
|
+
Flow2Spec - 统一知识库工作流(AI 配置入口)
|
|
15
24
|
|
|
16
25
|
用法:
|
|
17
|
-
flow2spec init [agent ...]
|
|
26
|
+
flow2spec init [agent ...] [--reset-knowledge] [--yes] 在当前项目初始化:写入 .Knowledge 与所选 agent 入口
|
|
27
|
+
flow2spec config 打印项目根 ${CONFIG_FILENAME} 的解析结果(缺省值合并后)
|
|
18
28
|
flow2spec --help 显示本说明
|
|
19
29
|
|
|
20
|
-
agent
|
|
30
|
+
agent(可多个,空格分隔;省略时交互选择):
|
|
21
31
|
${agentList}
|
|
22
32
|
|
|
23
33
|
示例:
|
|
24
|
-
flow2spec init #
|
|
25
|
-
flow2spec init 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.
|
|
30
|
-
|
|
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
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
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
|
-
|
|
292
|
+
建议阅读 README 或 docs/Flow2Spec使用说明.md,按「规则在配置根、文档在 .Knowledge」的方式使用。
|
|
53
293
|
`);
|
|
54
294
|
})
|
|
55
295
|
.catch((e) => {
|