@double-codeing/flow2spec 3.1.0 → 3.1.1
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 +2 -2
- package/cli.js +122 -11
- package/docs/en/architecture.md +2 -2
- package/docs/en/commands-reference.md +14 -10
- package/docs/en/design-principles.md +8 -5
- package/docs/en/usage-guide.md +10 -4
- package/docs/en/usage-scenarios.md +2 -2
- package/docs//344/275/223/347/263/273/344/270/216/345/216/237/347/220/206.md +2 -2
- package/docs//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 +1 -1
- package/docs//344/275/277/347/224/250/350/257/264/346/230/216.md +9 -4
- package/docs//345/221/275/344/273/244/350/257/264/346/230/216.md +13 -10
- package/docs//350/256/276/350/256/241/350/257/264/346/230/216.md +84 -53
- package/lib/claudeSettingsAdapter.js +99 -30
- package/lib/flow2specConfig.js +32 -6
- package/lib/init.js +148 -3
- package/package.json +1 -1
- package/templates/AGENTS.codex-stub.md +2 -0
- package/templates/AGENTS.md +13 -0
- package/templates/flow2spec.config.json +5 -2
- package/templates/hooks/f2s-config-inject.js +9 -147
- package/templates/hooks/f2s-config-session.js +95 -0
- package/templates/hooks/f2s-update-check.js +141 -0
- package/templates/knowledge/topics/f2s-config-precheck.md +2 -2
- package/templates/rules/f2s-config-check.mdc +3 -1
- package/templates/rules/f2s-flow2spec-unified-entry.mdc +13 -0
- package/templates/skills/f2s-git-commit/SKILL.md +21 -5
|
@@ -27,12 +27,12 @@
|
|
|
27
27
|
|
|
28
28
|
仓内 **四环**(规则环与技能环分列,勿合并为「规则+技能」):
|
|
29
29
|
|
|
30
|
-
| 环
|
|
31
|
-
|
|
|
32
|
-
| 知识环 | `.Knowledge/`
|
|
33
|
-
| 任务环 | `.task/`
|
|
30
|
+
| 环 | 落点 | 职责 |
|
|
31
|
+
| ------ | ---------------------------- | ------------------------------------ |
|
|
32
|
+
| 知识环 | `.Knowledge/` | 路由 + 主题 + 存量/需求文档 |
|
|
33
|
+
| 任务环 | `.task/` | 跨会话续作、用户代办 |
|
|
34
34
|
| 规则环 | 各工具 `rules` / `AGENTS.md` | 怎么读、怎么做(缺口闸门、路由顺序) |
|
|
35
|
-
| 技能环 | `f2s-*` / `skills/`
|
|
35
|
+
| 技能环 | `f2s-*` / `skills/` | 维护知识、触发 feat/fix/sync 等 |
|
|
36
36
|
|
|
37
37
|
```mermaid
|
|
38
38
|
graph TB
|
|
@@ -72,13 +72,13 @@ graph TB
|
|
|
72
72
|
L2 --- V
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
| 层级 | 路径
|
|
76
|
-
|
|
|
77
|
-
| L0
|
|
78
|
-
| L1
|
|
79
|
-
| L2
|
|
80
|
-
| L3
|
|
81
|
-
| 纵链 | `topicDependencies`
|
|
75
|
+
| 层级 | 路径 | 作用 |
|
|
76
|
+
| ---- | -------------------------- | ------------------------------ |
|
|
77
|
+
| L0 | `manifest-routing.json` | 机读路由、依赖声明 |
|
|
78
|
+
| L1 | `matchers/*.json` | 关键词命中,**match** 只读一片 |
|
|
79
|
+
| L2 | `topics/*.md` | 硬约束摘要;**expand** 拉依赖 |
|
|
80
|
+
| L3 | `stock-docs/`、`req-docs/` | 长文档,按需下钻 |
|
|
81
|
+
| 纵链 | `topicDependencies` | 主题级前置,所有任务共享 |
|
|
82
82
|
|
|
83
83
|
读取流水线:`match → expand → verify → act`(详见 [体系与原理 §4](./体系与原理.md))。
|
|
84
84
|
|
|
@@ -105,8 +105,6 @@ graph LR
|
|
|
105
105
|
note2["规则随工具升级"] -.-> R
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
|
|
110
108
|
### 2. 渐进式路由
|
|
111
109
|
|
|
112
110
|
```mermaid
|
|
@@ -120,8 +118,6 @@ graph LR
|
|
|
120
118
|
M -->|未命中| FB[fallback-triage\n结构化分诊]
|
|
121
119
|
```
|
|
122
120
|
|
|
123
|
-
|
|
124
|
-
|
|
125
121
|
### 3. 技能维护闭环
|
|
126
122
|
|
|
127
123
|
<p><img src="./images/flow-1.png" alt="技能维护闭环" style="max-width:100%;" /></p>
|
|
@@ -154,9 +150,7 @@ graph LR
|
|
|
154
150
|
|
|
155
151
|
</details>
|
|
156
152
|
|
|
157
|
-
七条入口
|
|
158
|
-
|
|
159
|
-
|
|
153
|
+
七条入口 · `f2s-git-commit` 是提交时的知识纪律收口 · `.Knowledge/` 是唯一汇聚点 · 知识驱动 AI,AI 驱动下轮开发
|
|
160
154
|
|
|
161
155
|
### 4. 任务清单与跨会话续作
|
|
162
156
|
|
|
@@ -170,10 +164,51 @@ graph LR
|
|
|
170
164
|
LD --> RS[按原技能约束继续]
|
|
171
165
|
```
|
|
172
166
|
|
|
173
|
-
任务不因会话结束丢失
|
|
167
|
+
任务不因会话结束丢失 · 关键词自动续作,无需重新说明上下文 · 技能约束完整恢复
|
|
174
168
|
|
|
175
169
|
---
|
|
176
170
|
|
|
171
|
+
### 5.知识库更新检测
|
|
172
|
+
|
|
173
|
+
```mermaid
|
|
174
|
+
flowchart LR
|
|
175
|
+
A[用户执行 flow2spec init codex] --> B[写入 .codex/hooks/f2s-update-check.js]
|
|
176
|
+
A --> C[写入 .codex/hooks.json]
|
|
177
|
+
|
|
178
|
+
C --> D[注册 Codex SessionStart hook]
|
|
179
|
+
D --> E[matcher: startup|resume]
|
|
180
|
+
E --> F[command: node .codex/hooks/f2s-update-check.js]
|
|
181
|
+
|
|
182
|
+
G[Codex 新 session 启动或恢复] --> H{项目 hook 已信任?}
|
|
183
|
+
H -- 否 --> H1[Codex 提示通过 /hooks 审核并信任]
|
|
184
|
+
H1 --> H2[本次 hook 不自动执行]
|
|
185
|
+
H -- 是 --> F
|
|
186
|
+
|
|
187
|
+
F --> I{CI 环境?}
|
|
188
|
+
I -- 是 --> Z[静默跳过]
|
|
189
|
+
I -- 否 --> J{updateCheck.enabled=false?}
|
|
190
|
+
|
|
191
|
+
J -- 是 --> Z
|
|
192
|
+
J -- 否 --> K{.Knowledge/update-check.json 今天已检查?}
|
|
193
|
+
|
|
194
|
+
K -- 是 --> Z
|
|
195
|
+
K -- 否 --> L[读取 .Knowledge/manifest-routing.json version]
|
|
196
|
+
|
|
197
|
+
L --> M{有 manifest version?}
|
|
198
|
+
M -- 否 --> Z
|
|
199
|
+
M -- 是 --> N[npm view 当前包名 version]
|
|
200
|
+
|
|
201
|
+
N --> O[写入 .Knowledge/update-check.json 缓存]
|
|
202
|
+
O --> P{manifest version < npm latest?}
|
|
203
|
+
|
|
204
|
+
P -- 否 --> Z
|
|
205
|
+
P -- 是 --> Q[stdout 输出 JSON]
|
|
206
|
+
|
|
207
|
+
Q --> R[hookSpecificOutput.additionalContext]
|
|
208
|
+
R --> S[Codex 把提示注入会话上下文]
|
|
209
|
+
S --> T[提示用户执行 f2s-kb-upgrade]
|
|
210
|
+
```
|
|
211
|
+
|
|
177
212
|
## 设计亮点
|
|
178
213
|
|
|
179
214
|
### 一、路由与上下文加载
|
|
@@ -220,7 +255,7 @@ taskC → [main] ← 漏写了
|
|
|
220
255
|
└──────────────────────────────────────────┘
|
|
221
256
|
```
|
|
222
257
|
|
|
223
|
-
路由层保持轻量
|
|
258
|
+
路由层保持轻量 · 执行细节按需加载 · 两者独立更新
|
|
224
259
|
|
|
225
260
|
#### 4. 禁止全量扫描是硬约束
|
|
226
261
|
|
|
@@ -251,7 +286,7 @@ description: >
|
|
|
251
286
|
用户输入 → Agent 扫 description 做语义匹配 → 触发对应技能
|
|
252
287
|
```
|
|
253
288
|
|
|
254
|
-
触发词在 description 里
|
|
289
|
+
触发词在 description 里 · 不在正文里 · 命中率更高 · 双语覆盖减少漏触发
|
|
255
290
|
|
|
256
291
|
---
|
|
257
292
|
|
|
@@ -300,7 +335,7 @@ git log .Knowledge/
|
|
|
300
335
|
代码变更 + 知识变更 → 同一 commit 或相邻 commit
|
|
301
336
|
```
|
|
302
337
|
|
|
303
|
-
知识有版本
|
|
338
|
+
知识有版本 · 可 review · 可回溯 · 可 blame
|
|
304
339
|
|
|
305
340
|
#### 4. 禁止历史否定堆砌
|
|
306
341
|
|
|
@@ -313,7 +348,7 @@ git log .Knowledge/
|
|
|
313
348
|
→ 原写法已废弃,现改为 Bean 注入
|
|
314
349
|
```
|
|
315
350
|
|
|
316
|
-
每次修复原位改写
|
|
351
|
+
每次修复原位改写 · 不叠加历史 · 知识库永远只描述现在
|
|
317
352
|
|
|
318
353
|
---
|
|
319
354
|
|
|
@@ -337,7 +372,7 @@ implement-tech-design 执行流
|
|
|
337
372
|
输出待完成清单与提醒 ← 不可跳过
|
|
338
373
|
```
|
|
339
374
|
|
|
340
|
-
建议 → 可以被跳过
|
|
375
|
+
建议 → 可以被跳过 · 约束 → 必须明确处理才能继续
|
|
341
376
|
|
|
342
377
|
#### 2. fallback 本身是有程序的 topic
|
|
343
378
|
|
|
@@ -351,9 +386,7 @@ graph TD
|
|
|
351
386
|
Q -->|不确定| STOP[停止执行\n等待明确指令]
|
|
352
387
|
```
|
|
353
388
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
未命中 ≠ 静默失败 · 降级本身有明确程序
|
|
389
|
+
未命中 ≠ 静默失败 · 降级本身有明确程序
|
|
357
390
|
|
|
358
391
|
#### 3. manifest / index 写权硬约束
|
|
359
392
|
|
|
@@ -380,7 +413,7 @@ matchers/*.json(diff 模式)
|
|
|
380
413
|
❌ 整文件重写严格禁止
|
|
381
414
|
```
|
|
382
415
|
|
|
383
|
-
原因:文档需要保证「现行真值覆盖 / 文风一致 / 禁历史否定堆砌」
|
|
416
|
+
原因:文档需要保证「现行真值覆盖 / 文风一致 / 禁历史否定堆砌」 · 要求写的人看到全文上下文
|
|
384
417
|
|
|
385
418
|
#### 5. 任务清单与跨会话续作
|
|
386
419
|
|
|
@@ -413,7 +446,7 @@ todo.json 写权约束
|
|
|
413
446
|
原因:多子 agent 并行落盘时,并发写会导致条目互相覆盖
|
|
414
447
|
```
|
|
415
448
|
|
|
416
|
-
生命周期由技能驱动
|
|
449
|
+
生命周期由技能驱动 · 关键词路由实现跨会话自动续作 · linkedSkill 保证技能约束完整恢复
|
|
417
450
|
|
|
418
451
|
---
|
|
419
452
|
|
|
@@ -435,7 +468,7 @@ todo.json 写权约束
|
|
|
435
468
|
└────────────┴─────────────────┘
|
|
436
469
|
```
|
|
437
470
|
|
|
438
|
-
两个维度正交
|
|
471
|
+
两个维度正交 · 独立配置 · 默认左下角
|
|
439
472
|
|
|
440
473
|
#### 2. 确认权不可下放子 agent
|
|
441
474
|
|
|
@@ -450,9 +483,7 @@ graph LR
|
|
|
450
483
|
S3[步骤3: 落盘] -->|subAgent=true 可并行| SUB2[子 agent]
|
|
451
484
|
```
|
|
452
485
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
用户对话只经过主 agent · 确认决策不可绕过用户 · 子 agent 只做执行不做决策
|
|
486
|
+
用户对话只经过主 agent · 确认决策不可绕过用户 · 子 agent 只做执行不做决策
|
|
456
487
|
|
|
457
488
|
#### 3. 技能可覆盖全局 subAgent 配置
|
|
458
489
|
|
|
@@ -466,7 +497,7 @@ subAgent: true 本技能默认不拆子:
|
|
|
466
497
|
拆子会断上下文,导致澄清质量下降
|
|
467
498
|
```
|
|
468
499
|
|
|
469
|
-
全局配置是允许拆的上限
|
|
500
|
+
全局配置是允许拆的上限 · 技能自己判断是否适合拆 · 配置 true 不等于一定拆
|
|
470
501
|
|
|
471
502
|
#### 4. f2s-kb-sync 先出大纲,确认后再写
|
|
472
503
|
|
|
@@ -479,9 +510,7 @@ graph LR
|
|
|
479
510
|
U -->|取消| STOP[不写入]
|
|
480
511
|
```
|
|
481
512
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
写入是破坏性操作 · 大纲是用户唯一的纠错机会 · 确认前不落盘
|
|
513
|
+
写入是破坏性操作 · 大纲是用户唯一的纠错机会 · 确认前不落盘
|
|
485
514
|
|
|
486
515
|
#### 5. 零输入推断
|
|
487
516
|
|
|
@@ -496,18 +525,20 @@ f2s-kb-sync 三种输入方式
|
|
|
496
525
|
本次实现了什么、有什么值得沉淀
|
|
497
526
|
```
|
|
498
527
|
|
|
499
|
-
会话上下文本身就是信息源
|
|
528
|
+
会话上下文本身就是信息源 · 不需要用户整理再输入
|
|
500
529
|
|
|
501
530
|
#### 5.1 执行开关如何进入 Agent(多端提示)
|
|
502
531
|
|
|
503
532
|
`flow2spec.config.json` 决定 **`subAgent` / `switchAgentVerification` / `changeTracking`**,但各 AI 产品**不保证**会话启动即自动打开该文件。设计上用 **多条弱约束叠加** 降低「未读配置就开跑 `f2s-*`」的概率,同时避免在 `.Knowledge` 再维护一份与 `.codex/topics/f2s-config-check.md` 逐字重复的长文:
|
|
504
533
|
|
|
505
|
-
| 机制
|
|
506
|
-
|
|
|
507
|
-
| **Cursor `f2s-config-check.mdc`**
|
|
508
|
-
| **Claude `f2s-config-
|
|
509
|
-
| **
|
|
510
|
-
|
|
|
534
|
+
| 机制 | 设计意图 |
|
|
535
|
+
| ----------------------------------------------------------- | --------------------------------------------------------------------------------- |
|
|
536
|
+
| **Cursor `f2s-config-check.mdc`** | 规则层强制「技能正文前先 Read」;Cursor hook 仅用于版本更新检测,不自动读取配置。 |
|
|
537
|
+
| **Claude `f2s-config-session` SessionStart** | 会话开始时注入一次配置摘要,降低遗忘概率。 |
|
|
538
|
+
| **Claude `f2s-config-inject` PreToolUse** | 仅在调用 **`f2s-*` Skill** 时做守门提示,提醒首步必须 Read;不反复注入完整配置。 |
|
|
539
|
+
| **Codex `AGENTS.md` / `.codex/topics/f2s-config-check.md`** | 文本层强制「技能正文前先 Read」;Codex hook 仅用于版本更新检测,不自动读取配置。 |
|
|
540
|
+
| **Codex `AGENTS.md` + `renderProjectConfigBlock`** | 顶部 **Read** 硬约束 + **init 快照表**(与磁盘不一致时以 Read 为准)。 |
|
|
541
|
+
| **知识库 `config-precheck` 主题** | 路由命中时只提供**摘要**与链向 Codex 长文,**不**替代 Read JSON。 |
|
|
511
542
|
|
|
512
543
|
**权威仍为**项目根 JSON 的 **Read** 结果;各层为提示而非第二份真值源。操作侧完整表格与路径见 **[使用说明 § 一、`f2s-*` 与 `flow2spec.config.json`](./使用说明.md)**;口述节奏见 **[Flow2Spec-演讲稿 Slide 13b](./Flow2Spec-演讲稿.md)**。
|
|
513
544
|
|
|
@@ -540,7 +571,7 @@ flow2spec init cursor codex ← 跳过 Claude
|
|
|
540
571
|
.Knowledge/ 始终不变,工具随时加减
|
|
541
572
|
```
|
|
542
573
|
|
|
543
|
-
同一份 `.Knowledge/` 驱动所有工具
|
|
574
|
+
同一份 `.Knowledge/` 驱动所有工具 · 加减工具不影响知识内容 · 新工具接入零重建
|
|
544
575
|
|
|
545
576
|
#### 2. 知识主题可插拔:增删不连带
|
|
546
577
|
|
|
@@ -555,7 +586,7 @@ flow2spec init cursor codex ← 跳过 Claude
|
|
|
555
586
|
其他主题完全不受影响
|
|
556
587
|
```
|
|
557
588
|
|
|
558
|
-
新主题只需在 `topicDependencies` 里声明依赖
|
|
589
|
+
新主题只需在 `topicDependencies` 里声明依赖 · 不声明则彼此独立 · 删除无副作用
|
|
559
590
|
|
|
560
591
|
#### 3. 技能可插拔:自包含单元,项目级可覆盖包级
|
|
561
592
|
|
|
@@ -569,13 +600,13 @@ f2s-doc-arch/SKILL.md my-review-skill/SKILL.md
|
|
|
569
600
|
名字不冲突则共存 · 同名则项目级覆盖包级 · 互不感知
|
|
570
601
|
```
|
|
571
602
|
|
|
572
|
-
技能靠 `description` 字段自描述触发词
|
|
603
|
+
技能靠 `description` 字段自描述触发词 · 不需要注册表 · 不需要改全局配置 · 上线即生效
|
|
573
604
|
|
|
574
605
|
#### 4. 路由词表可插拔:分片隔离,局部更新
|
|
575
606
|
|
|
576
607
|
词条变更只改对应 `matchers/m-xxx.json`,其他路由 diff 为零;结构见「[一、路由与上下文加载 → matchers 分片](#matchers-分片不嵌入-manifest)」。
|
|
577
608
|
|
|
578
|
-
词条变更局部化
|
|
609
|
+
词条变更局部化 · 合并冲突最小化 · 新增路由不影响存量
|
|
579
610
|
|
|
580
611
|
#### 5. 执行模型可插拔:config 按项目切换
|
|
581
612
|
|
|
@@ -588,13 +619,14 @@ flow2spec.config.json
|
|
|
588
619
|
switchAgentVerification: false → 落盘侧自验,日常使用
|
|
589
620
|
switchAgentVerification: true → 交叉校验,高置信度关键场景
|
|
590
621
|
|
|
591
|
-
changeTracking.feat
|
|
592
|
-
changeTracking.
|
|
622
|
+
changeTracking.feat: true → f2s-kb-feat 默认创建任务清单
|
|
623
|
+
changeTracking.fix: false → f2s-kb-fix 默认不创建任务清单
|
|
624
|
+
changeTracking.implement: true → implement-tech-design 默认创建任务清单
|
|
593
625
|
|
|
594
626
|
三个维度正交 · 各技能可进一步细化覆盖全局配置
|
|
595
627
|
```
|
|
596
628
|
|
|
597
|
-
改一行配置切换执行策略
|
|
629
|
+
改一行配置切换执行策略 · 不修改任何技能文件 · 新项目开箱即用,老项目按需升级
|
|
598
630
|
|
|
599
631
|
---
|
|
600
632
|
|
|
@@ -641,4 +673,3 @@ flow2spec.config.json
|
|
|
641
673
|
- [体系与原理](./体系与原理.md)
|
|
642
674
|
- [使用案例-模拟对话](./使用案例-模拟对话.md)
|
|
643
675
|
- [Flow2Spec-演讲稿](./Flow2Spec-演讲稿.md)
|
|
644
|
-
|
|
@@ -6,19 +6,19 @@
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const path = require('path');
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const HOOK_COMMAND_CONFIG_INJECT = 'node .claude/hooks/f2s-config-inject.js';
|
|
10
|
+
const HOOK_COMMAND_CONFIG_SESSION = 'node .claude/hooks/f2s-config-session.js';
|
|
11
|
+
const HOOK_COMMAND_UPDATE_CHECK = 'node .claude/hooks/f2s-update-check.js';
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if (!Array.isArray(preToolUseArr)) return false;
|
|
18
|
-
for (const group of preToolUseArr) {
|
|
13
|
+
// 向下兼容
|
|
14
|
+
const HOOK_COMMAND = HOOK_COMMAND_CONFIG_INJECT;
|
|
15
|
+
|
|
16
|
+
function hasHookCommand(arr, fragment) {
|
|
17
|
+
if (!Array.isArray(arr)) return false;
|
|
18
|
+
for (const group of arr) {
|
|
19
19
|
if (!group || !Array.isArray(group.hooks)) continue;
|
|
20
20
|
for (const h of group.hooks) {
|
|
21
|
-
if (h && h.type === 'command' && String(h.command || '').includes(
|
|
21
|
+
if (h && h.type === 'command' && String(h.command || '').includes(fragment)) {
|
|
22
22
|
return true;
|
|
23
23
|
}
|
|
24
24
|
}
|
|
@@ -26,8 +26,13 @@ function hasF2sHook(preToolUseArr) {
|
|
|
26
26
|
return false;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
// 旧函数名保持兼容
|
|
30
|
+
function hasF2sHook(preToolUseArr) {
|
|
31
|
+
return hasHookCommand(preToolUseArr, 'f2s-config-inject');
|
|
32
|
+
}
|
|
33
|
+
|
|
29
34
|
/**
|
|
30
|
-
* 将 f2s PreToolUse hook 合并进现有 settings,返回新对象(不修改原对象)。
|
|
35
|
+
* 将 f2s PreToolUse 守门 hook 合并进现有 settings,返回新对象(不修改原对象)。
|
|
31
36
|
* @param {object} existing 现有 settings(可为 {})
|
|
32
37
|
* @returns {object}
|
|
33
38
|
*/
|
|
@@ -48,6 +53,48 @@ function mergeF2sHook(existing) {
|
|
|
48
53
|
return { settings: next, changed: true };
|
|
49
54
|
}
|
|
50
55
|
|
|
56
|
+
/**
|
|
57
|
+
* 将 f2s SessionStart 配置摘要 hook 合并进 settings。
|
|
58
|
+
* @param {object} existing
|
|
59
|
+
* @returns {{ settings, changed }}
|
|
60
|
+
*/
|
|
61
|
+
function mergeConfigSessionHook(existing) {
|
|
62
|
+
const next = JSON.parse(JSON.stringify(existing || {}));
|
|
63
|
+
if (!next.hooks) next.hooks = {};
|
|
64
|
+
if (!next.hooks.SessionStart) next.hooks.SessionStart = [];
|
|
65
|
+
|
|
66
|
+
if (hasHookCommand(next.hooks.SessionStart, 'f2s-config-session')) {
|
|
67
|
+
return { settings: next, changed: false };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
next.hooks.SessionStart.push({
|
|
71
|
+
hooks: [{ type: 'command', command: HOOK_COMMAND_CONFIG_SESSION }],
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return { settings: next, changed: true };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 将 f2s SessionStart(版本检查)hook 合并进 settings。
|
|
79
|
+
* @param {object} existing
|
|
80
|
+
* @returns {{ settings, changed }}
|
|
81
|
+
*/
|
|
82
|
+
function mergeUpdateCheckHook(existing) {
|
|
83
|
+
const next = JSON.parse(JSON.stringify(existing || {}));
|
|
84
|
+
if (!next.hooks) next.hooks = {};
|
|
85
|
+
if (!next.hooks.SessionStart) next.hooks.SessionStart = [];
|
|
86
|
+
|
|
87
|
+
if (hasHookCommand(next.hooks.SessionStart, 'f2s-update-check')) {
|
|
88
|
+
return { settings: next, changed: false };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
next.hooks.SessionStart.push({
|
|
92
|
+
hooks: [{ type: 'command', command: HOOK_COMMAND_UPDATE_CHECK }],
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
return { settings: next, changed: true };
|
|
96
|
+
}
|
|
97
|
+
|
|
51
98
|
/**
|
|
52
99
|
* 读取 .claude/settings.json(不存在则返回 {})。
|
|
53
100
|
* @param {string} claudeRoot .claude 目录绝对路径
|
|
@@ -74,41 +121,63 @@ function writeSettings(claudeRoot, settings) {
|
|
|
74
121
|
}
|
|
75
122
|
|
|
76
123
|
/**
|
|
77
|
-
*
|
|
78
|
-
* @param {string} claudeRoot
|
|
79
|
-
* @param {string} templatesDir
|
|
124
|
+
* 复制 hook 脚本到 .claude/hooks/。
|
|
80
125
|
*/
|
|
81
|
-
function copyHookScript(claudeRoot, templatesDir) {
|
|
82
|
-
const src = path.join(templatesDir, 'hooks',
|
|
126
|
+
function copyHookScript(claudeRoot, templatesDir, scriptName) {
|
|
127
|
+
const src = path.join(templatesDir, 'hooks', scriptName);
|
|
83
128
|
if (!fs.existsSync(src)) return { written: false, reason: 'missing-template' };
|
|
84
129
|
|
|
85
130
|
const hooksDir = path.join(claudeRoot, 'hooks');
|
|
86
131
|
if (!fs.existsSync(hooksDir)) fs.mkdirSync(hooksDir, { recursive: true });
|
|
87
132
|
|
|
88
|
-
|
|
89
|
-
|
|
133
|
+
let body = fs.readFileSync(src, 'utf8');
|
|
134
|
+
if (body.includes('__FLOW2SPEC_PACKAGE_NAME__')) {
|
|
135
|
+
let packageName = '@double-codeing/flow2spec';
|
|
136
|
+
try {
|
|
137
|
+
packageName = JSON.parse(
|
|
138
|
+
fs.readFileSync(path.join(templatesDir, '..', 'package.json'), 'utf8'),
|
|
139
|
+
).name || packageName;
|
|
140
|
+
} catch (_) {}
|
|
141
|
+
body = body.replace(/__FLOW2SPEC_PACKAGE_NAME__/g, packageName);
|
|
142
|
+
}
|
|
143
|
+
fs.writeFileSync(path.join(hooksDir, scriptName), body, 'utf8');
|
|
90
144
|
return { written: true };
|
|
91
145
|
}
|
|
92
146
|
|
|
93
147
|
/**
|
|
94
|
-
* 主入口:为 claude agent 配置 f2s PreToolUse
|
|
95
|
-
* @param {string} cwd
|
|
96
|
-
* @param {string} templatesDir
|
|
97
|
-
* @returns {{ hookScriptResult, settingsChanged }}
|
|
148
|
+
* 主入口:为 claude agent 配置 f2s hooks(SessionStart 配置摘要 + PreToolUse 守门 + SessionStart 更新检查)。
|
|
149
|
+
* @param {string} cwd
|
|
150
|
+
* @param {string} templatesDir
|
|
151
|
+
* @returns {{ hookScriptResult, updateCheckResult, settingsChanged }}
|
|
98
152
|
*/
|
|
99
153
|
function writeClaudeAgentHooks(cwd, templatesDir) {
|
|
100
154
|
const claudeRoot = path.join(cwd, '.claude');
|
|
101
155
|
if (!fs.existsSync(claudeRoot)) fs.mkdirSync(claudeRoot, { recursive: true });
|
|
102
156
|
|
|
103
|
-
const hookScriptResult = copyHookScript(claudeRoot, templatesDir);
|
|
157
|
+
const hookScriptResult = copyHookScript(claudeRoot, templatesDir, 'f2s-config-inject.js');
|
|
158
|
+
const configSessionResult = copyHookScript(claudeRoot, templatesDir, 'f2s-config-session.js');
|
|
159
|
+
const updateCheckResult = copyHookScript(claudeRoot, templatesDir, 'f2s-update-check.js');
|
|
104
160
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
161
|
+
let settings = readSettings(claudeRoot);
|
|
162
|
+
let changed = false;
|
|
163
|
+
|
|
164
|
+
const r1 = mergeF2sHook(settings);
|
|
165
|
+
if (r1.changed) { settings = r1.settings; changed = true; }
|
|
166
|
+
|
|
167
|
+
const r2 = mergeConfigSessionHook(settings);
|
|
168
|
+
if (r2.changed) { settings = r2.settings; changed = true; }
|
|
169
|
+
|
|
170
|
+
const r3 = mergeUpdateCheckHook(settings);
|
|
171
|
+
if (r3.changed) { settings = r3.settings; changed = true; }
|
|
172
|
+
|
|
173
|
+
if (changed) writeSettings(claudeRoot, settings);
|
|
110
174
|
|
|
111
|
-
return { hookScriptResult, settingsChanged: changed };
|
|
175
|
+
return { hookScriptResult, configSessionResult, updateCheckResult, settingsChanged: changed };
|
|
112
176
|
}
|
|
113
177
|
|
|
114
|
-
module.exports = {
|
|
178
|
+
module.exports = {
|
|
179
|
+
writeClaudeAgentHooks,
|
|
180
|
+
mergeF2sHook,
|
|
181
|
+
mergeConfigSessionHook,
|
|
182
|
+
mergeUpdateCheckHook,
|
|
183
|
+
};
|
package/lib/flow2specConfig.js
CHANGED
|
@@ -8,9 +8,12 @@ const DEFAULTS = {
|
|
|
8
8
|
// switchAgentVerification:false=落盘侧同会话内验;true+技能绑定=交叉验(子落盘主验/主落盘子验)
|
|
9
9
|
switchAgentVerification: false,
|
|
10
10
|
changeTracking: {
|
|
11
|
-
feat:
|
|
11
|
+
feat: true,
|
|
12
12
|
fix: false,
|
|
13
|
-
implement:
|
|
13
|
+
implement: true,
|
|
14
|
+
},
|
|
15
|
+
updateCheck: {
|
|
16
|
+
enabled: true,
|
|
14
17
|
},
|
|
15
18
|
};
|
|
16
19
|
|
|
@@ -35,7 +38,7 @@ const CONFIG_FIELDS = [
|
|
|
35
38
|
{
|
|
36
39
|
key: "changeTracking.feat",
|
|
37
40
|
type: "boolean",
|
|
38
|
-
default:
|
|
41
|
+
default: true,
|
|
39
42
|
question: "启用变更追踪 - f2s-kb-feat(新增能力时创建可续作的任务清单)?",
|
|
40
43
|
},
|
|
41
44
|
{
|
|
@@ -47,9 +50,15 @@ const CONFIG_FIELDS = [
|
|
|
47
50
|
{
|
|
48
51
|
key: "changeTracking.implement",
|
|
49
52
|
type: "boolean",
|
|
50
|
-
default:
|
|
53
|
+
default: true,
|
|
51
54
|
question: "启用变更追踪 - f2s-implement-tech-design(实现技术方案时创建可续作的任务清单)?",
|
|
52
55
|
},
|
|
56
|
+
{
|
|
57
|
+
key: "updateCheck.enabled",
|
|
58
|
+
type: "boolean",
|
|
59
|
+
default: true,
|
|
60
|
+
question: "启用每日版本更新提示(每天第一次 Agent 对话时检查是否有新版 flow2spec)?",
|
|
61
|
+
},
|
|
53
62
|
];
|
|
54
63
|
|
|
55
64
|
function normalizeBool(value, fallback) {
|
|
@@ -83,6 +92,7 @@ function loadFlow2specConfig(cwd) {
|
|
|
83
92
|
const out = {
|
|
84
93
|
...DEFAULTS,
|
|
85
94
|
changeTracking: { ...DEFAULTS.changeTracking },
|
|
95
|
+
updateCheck: { ...DEFAULTS.updateCheck },
|
|
86
96
|
};
|
|
87
97
|
if (!fs.existsSync(abs)) {
|
|
88
98
|
return out;
|
|
@@ -126,6 +136,14 @@ function loadFlow2specConfig(cwd) {
|
|
|
126
136
|
};
|
|
127
137
|
}
|
|
128
138
|
}
|
|
139
|
+
if (Object.prototype.hasOwnProperty.call(raw, "updateCheck")) {
|
|
140
|
+
const uc = raw.updateCheck;
|
|
141
|
+
if (uc && typeof uc === "object" && !Array.isArray(uc)) {
|
|
142
|
+
out.updateCheck = {
|
|
143
|
+
enabled: normalizeBool(uc.enabled, DEFAULTS.updateCheck.enabled),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
129
147
|
return out;
|
|
130
148
|
}
|
|
131
149
|
|
|
@@ -209,10 +227,18 @@ function ensureFlow2specProjectConfig(cwd, templatesDir, options = {}) {
|
|
|
209
227
|
try {
|
|
210
228
|
base = JSON.parse(fs.readFileSync(src, "utf8"));
|
|
211
229
|
} catch {
|
|
212
|
-
base = {
|
|
230
|
+
base = {
|
|
231
|
+
...DEFAULTS,
|
|
232
|
+
changeTracking: { ...DEFAULTS.changeTracking },
|
|
233
|
+
updateCheck: { ...DEFAULTS.updateCheck },
|
|
234
|
+
};
|
|
213
235
|
}
|
|
214
236
|
} else {
|
|
215
|
-
base = {
|
|
237
|
+
base = {
|
|
238
|
+
...DEFAULTS,
|
|
239
|
+
changeTracking: { ...DEFAULTS.changeTracking },
|
|
240
|
+
updateCheck: { ...DEFAULTS.updateCheck },
|
|
241
|
+
};
|
|
216
242
|
}
|
|
217
243
|
const merged = values && typeof values === "object" ? mergeValues(base, values) : base;
|
|
218
244
|
fs.writeFileSync(dest, `${JSON.stringify(merged, null, 2)}\n`, "utf8");
|