flower-trellis 0.2.5-beta.2 → 0.3.0-beta.0
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 +1 -1
- package/enhancements/0.6/.agents/skills/trellis-push/SKILL.md +34 -7
- package/enhancements/0.6/.claude/skills/trellis-push/SKILL.md +34 -7
- package/enhancements/MANIFEST.json +2 -2
- package/package.json +2 -2
- package/src/lib/apply-enhancements.js +4 -4
- package/src/lib/codex-tweaks.js +77 -37
package/README.md
CHANGED
|
@@ -96,7 +96,7 @@ flower banner → 平台多选菜单 → Trellis 原生交互(模板 / monorepo
|
|
|
96
96
|
```
|
|
97
97
|
|
|
98
98
|
- **统一品牌头部**:Trellis 子进程在伪终端(`node-pty`)中运行,其原生的模板 / monorepo / 冲突等交互完整保留,但重复打印的启动 banner 被过滤,全程只呈现一个 flower banner。
|
|
99
|
-
- **按平台铺设技能**:Claude 铺到 `.claude/skills`,Codex / Gemini 等铺到 `.agents/skills`;并对 codex 做后处理(
|
|
99
|
+
- **按平台铺设技能**:Claude 铺到 `.claude/skills`,Codex / Gemini 等铺到 `.agents/skills`;并对 codex 做后处理(兼容清理旧 `config.toml` 的 `[features.multi_agent_v2]`,在保留上游 hooks 的基础上补全 `SessionStart`)。
|
|
100
100
|
- **幂等执行**:`workflow.md` 注入前先按 `BEGIN/END` 标记清除旧块再重注入(块数恒定,不会翻倍,首次注入前备份 `.bak`);技能文件覆盖式铺设,并通过 `.trellis/.flower-manifest.json` 记录已铺路径,升级时删除已淘汰项。
|
|
101
101
|
- **安全中止**:`Ctrl+C` 取消后不会继续叠加。
|
|
102
102
|
|
|
@@ -44,7 +44,7 @@ Step 5 写入已确认的任务进度快照,并补齐运行后字段
|
|
|
44
44
|
Step 6 输出结果
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
除非出现“计划变化、执行失败重试、用户调整 snapshot
|
|
47
|
+
除非出现“计划变化、执行失败重试、用户调整 snapshot、父仓 staged/冲突/task 文件预脏等 bookkeeping 安全条件不满足、merge 冲突”等情况,否则不要在执行中途追加新的确认问题。
|
|
48
48
|
|
|
49
49
|
---
|
|
50
50
|
|
|
@@ -120,7 +120,12 @@ git log <base_branch>..HEAD --oneline
|
|
|
120
120
|
- 有未合并状态、rebase 状态或 merge 冲突残留:立即停止,展示状态;不要继续生成执行计划。
|
|
121
121
|
- 当前分支为空、detached HEAD、或无法确认分支:停止并说明原因。
|
|
122
122
|
- dirty 文件必须按来源分组:**AI 本轮编辑** 与 **未识别 dirty 文件**。未识别 dirty 文件默认不纳入提交计划。
|
|
123
|
-
- 如果父仓(含 `.trellis/` 的仓库)后续要写 snapshot,先记录父仓当前 `git status --
|
|
123
|
+
- 如果父仓(含 `.trellis/` 的仓库)后续要写 snapshot,先记录父仓当前 `git status --porcelain`,供 Step 5 判断:
|
|
124
|
+
- 是否存在未合并 / 冲突状态;
|
|
125
|
+
- 是否存在与本次 bookkeeping 无关的 staged 文件;
|
|
126
|
+
- `<task_dir>/task.json` 是否在写入前已经 dirty;
|
|
127
|
+
- reconfigure 场景下 `.trellis/config.yaml` 是否在写入前已经 dirty 且未在统一计划中确认。
|
|
128
|
+
父仓存在无关、未暂存 dirty 文件本身不阻塞 snapshot;这些文件只需要在计划或结果中提示会保留未提交。
|
|
124
129
|
|
|
125
130
|
---
|
|
126
131
|
|
|
@@ -225,6 +230,8 @@ commit-only 模式下,字段名仍保持 `pushed_commits` 以兼容恢复逻
|
|
|
225
230
|
### 父仓 bookkeeping
|
|
226
231
|
|
|
227
232
|
- <跳过原因,或仅提交 `<task_dir>/task.json`>
|
|
233
|
+
- 无关未暂存 dirty:<list 或 无>(保留未提交,不阻塞 snapshot commit)
|
|
234
|
+
- 无关 staged / 冲突 / 目标文件预脏:<list 或 无>(有则停止,需处理后重新计划)
|
|
228
235
|
|
|
229
236
|
确认后将按上述计划执行。回复 `ok` / `行` / `确认` 执行;回复 `skip snapshot` 跳过快照;回复修改意见则先更新计划;回复 `manual` / `我自己来` 则停止。
|
|
230
237
|
```
|
|
@@ -334,6 +341,15 @@ git checkout <current_branch>
|
|
|
334
341
|
- `pushed_commits`:各 package 的短 hash
|
|
335
342
|
- `notes`:保留已确认 notes;commit-only 模式追加“本地已提交,未推送”
|
|
336
343
|
|
|
344
|
+
写入前先复核目标文件:
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
git status --porcelain -- <task_json_path> [".trellis/config.yaml"]
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
- `<task_json_path>` 如果在本次写入前已经 dirty,且该 dirty 未在统一计划中确认:立即停止并说明原因,避免覆盖或混入别人对同一任务文件的修改。
|
|
351
|
+
- reconfigure 场景下,`.trellis/config.yaml` 如果在本次回写前已经 dirty,且未在统一计划中确认:立即停止并说明原因。
|
|
352
|
+
|
|
337
353
|
写入方式:
|
|
338
354
|
|
|
339
355
|
1. 读 `<task_dir>/task.json`
|
|
@@ -349,15 +365,21 @@ git checkout <current_branch>
|
|
|
349
365
|
写完 snapshot 后,在父仓根目录执行:
|
|
350
366
|
|
|
351
367
|
```bash
|
|
352
|
-
git status --
|
|
368
|
+
git status --porcelain
|
|
369
|
+
git diff --name-only --cached
|
|
353
370
|
```
|
|
354
371
|
|
|
355
|
-
|
|
372
|
+
bookkeeping 的自动处理范围只允许统一计划中确认过的文件:
|
|
356
373
|
|
|
357
374
|
- `<task_dir>/task.json`
|
|
358
375
|
- 如果 Step 4 选择了 reconfigure:`.trellis/config.yaml`
|
|
359
376
|
|
|
360
|
-
|
|
377
|
+
检查规则:
|
|
378
|
+
|
|
379
|
+
- 如果存在未合并路径、rebase 状态或 merge 冲突残留:立即停止,展示状态。
|
|
380
|
+
- 如果 `git diff --name-only --cached` 显示除上述允许文件之外的 staged 文件:立即停止,说明这些 staged 文件会被普通 commit 混入,必须先处理或重新确认。
|
|
381
|
+
- 如果存在除上述允许文件之外的**未暂存** dirty 文件:不要阻塞;保留它们未暂存,并在结果中提示“未纳入 snapshot commit”。
|
|
382
|
+
- 如果允许文件之外的 dirty 同时被 staged 和 unstaged 修改,按 staged 风险处理:立即停止。
|
|
361
383
|
|
|
362
384
|
如果父仓也是本次业务仓库,snapshot / config bookkeeping 仍然使用单独 commit,不和业务 commit 混合。
|
|
363
385
|
|
|
@@ -365,9 +387,11 @@ git status --short
|
|
|
365
387
|
|
|
366
388
|
```bash
|
|
367
389
|
git add <task_json_path> [".trellis/config.yaml"]
|
|
368
|
-
git commit -m "chore(task): update <task_name> push snapshot"
|
|
390
|
+
git commit --only -m "chore(task): update <task_name> push snapshot" -- <task_json_path> [".trellis/config.yaml"]
|
|
369
391
|
```
|
|
370
392
|
|
|
393
|
+
`git commit --only ... -- <paths>` 的目的是把 bookkeeping commit 限定到当前任务的 `task.json`(以及已确认的 `config.yaml`),即使父仓还有无关未暂存 dirty 文件,也不会把它们带入本次提交。执行前仍必须确认 staged 区没有无关文件;如果本地 Git 对 `--only` 参数不兼容,只有在 staged 区确认仅包含上述允许文件时,才可退回普通 `git commit -m ...`。
|
|
394
|
+
|
|
371
395
|
检查父仓是否配置 remote:
|
|
372
396
|
|
|
373
397
|
```bash
|
|
@@ -393,6 +417,7 @@ git remote -v | grep -E "^origin\s+"
|
|
|
393
417
|
|
|
394
418
|
任务进度快照:已写入 `<task_dir>/task.json`,完成 Step 1-3,下一步 Step 5。
|
|
395
419
|
父仓同步:已提交并推送 `chore(task): update <task_name> push snapshot`。
|
|
420
|
+
父仓存在未暂存 dirty 时补充:这些文件已保留未暂存,未纳入 snapshot commit:<list>
|
|
396
421
|
```
|
|
397
422
|
|
|
398
423
|
如果部分仓库已成功、后续失败,必须明确列出:
|
|
@@ -410,7 +435,7 @@ git remote -v | grep -E "^origin\s+"
|
|
|
410
435
|
2. **未识别 dirty 文件隔离** — 默认不纳入提交,除非用户明确确认。
|
|
411
436
|
3. **执行前复核** — git 状态与计划不一致时停止,不临时扩展范围。
|
|
412
437
|
4. **snapshot 合并确认** — 语义字段执行前确认,运行后字段执行后补齐。
|
|
413
|
-
5. **父仓 bookkeeping 隔离** — 只提交已确认的 `task.json` / `config.yaml
|
|
438
|
+
5. **父仓 bookkeeping 隔离** — 只提交已确认的 `task.json` / `config.yaml`;无关未暂存 dirty 文件保留未提交并提示,不阻塞;无关 staged 文件、冲突状态、目标文件预脏必须停止。
|
|
414
439
|
6. **merge 冲突处理** — 冲突时暂停,不静默跳过。
|
|
415
440
|
7. **主分支保护** — 目标分支是 `master` / `main` 时必须在计划中额外警告确认。
|
|
416
441
|
8. **不使用 force push** — 始终使用普通 push。
|
|
@@ -426,6 +451,8 @@ git remote -v | grep -E "^origin\s+"
|
|
|
426
451
|
- ❌ push 后临时追问 merge,而不是在计划中提前确认
|
|
427
452
|
- ❌ snapshot 语义内容未确认就写入 task.json
|
|
428
453
|
- ❌ 父仓 snapshot commit 混入业务代码提交
|
|
454
|
+
- ❌ 父仓存在无关未暂存 dirty 就跳过 snapshot,尽管可以只提交目标 `task.json`
|
|
455
|
+
- ❌ 父仓已有无关 staged 文件时仍执行 snapshot commit
|
|
429
456
|
- ❌ merge 冲突后静默 abort 或跳过
|
|
430
457
|
- ❌ force push 到当前分支或目标分支
|
|
431
458
|
- ❌ 在目标分支上直接开发(只 merge,不在目标分支上改代码)
|
|
@@ -44,7 +44,7 @@ Step 5 写入已确认的任务进度快照,并补齐运行后字段
|
|
|
44
44
|
Step 6 输出结果
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
除非出现“计划变化、执行失败重试、用户调整 snapshot
|
|
47
|
+
除非出现“计划变化、执行失败重试、用户调整 snapshot、父仓 staged/冲突/task 文件预脏等 bookkeeping 安全条件不满足、merge 冲突”等情况,否则不要在执行中途追加新的确认问题。
|
|
48
48
|
|
|
49
49
|
---
|
|
50
50
|
|
|
@@ -120,7 +120,12 @@ git log <base_branch>..HEAD --oneline
|
|
|
120
120
|
- 有未合并状态、rebase 状态或 merge 冲突残留:立即停止,展示状态;不要继续生成执行计划。
|
|
121
121
|
- 当前分支为空、detached HEAD、或无法确认分支:停止并说明原因。
|
|
122
122
|
- dirty 文件必须按来源分组:**AI 本轮编辑** 与 **未识别 dirty 文件**。未识别 dirty 文件默认不纳入提交计划。
|
|
123
|
-
- 如果父仓(含 `.trellis/` 的仓库)后续要写 snapshot,先记录父仓当前 `git status --
|
|
123
|
+
- 如果父仓(含 `.trellis/` 的仓库)后续要写 snapshot,先记录父仓当前 `git status --porcelain`,供 Step 5 判断:
|
|
124
|
+
- 是否存在未合并 / 冲突状态;
|
|
125
|
+
- 是否存在与本次 bookkeeping 无关的 staged 文件;
|
|
126
|
+
- `<task_dir>/task.json` 是否在写入前已经 dirty;
|
|
127
|
+
- reconfigure 场景下 `.trellis/config.yaml` 是否在写入前已经 dirty 且未在统一计划中确认。
|
|
128
|
+
父仓存在无关、未暂存 dirty 文件本身不阻塞 snapshot;这些文件只需要在计划或结果中提示会保留未提交。
|
|
124
129
|
|
|
125
130
|
---
|
|
126
131
|
|
|
@@ -225,6 +230,8 @@ commit-only 模式下,字段名仍保持 `pushed_commits` 以兼容恢复逻
|
|
|
225
230
|
### 父仓 bookkeeping
|
|
226
231
|
|
|
227
232
|
- <跳过原因,或仅提交 `<task_dir>/task.json`>
|
|
233
|
+
- 无关未暂存 dirty:<list 或 无>(保留未提交,不阻塞 snapshot commit)
|
|
234
|
+
- 无关 staged / 冲突 / 目标文件预脏:<list 或 无>(有则停止,需处理后重新计划)
|
|
228
235
|
|
|
229
236
|
确认后将按上述计划执行。回复 `ok` / `行` / `确认` 执行;回复 `skip snapshot` 跳过快照;回复修改意见则先更新计划;回复 `manual` / `我自己来` 则停止。
|
|
230
237
|
```
|
|
@@ -334,6 +341,15 @@ git checkout <current_branch>
|
|
|
334
341
|
- `pushed_commits`:各 package 的短 hash
|
|
335
342
|
- `notes`:保留已确认 notes;commit-only 模式追加“本地已提交,未推送”
|
|
336
343
|
|
|
344
|
+
写入前先复核目标文件:
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
git status --porcelain -- <task_json_path> [".trellis/config.yaml"]
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
- `<task_json_path>` 如果在本次写入前已经 dirty,且该 dirty 未在统一计划中确认:立即停止并说明原因,避免覆盖或混入别人对同一任务文件的修改。
|
|
351
|
+
- reconfigure 场景下,`.trellis/config.yaml` 如果在本次回写前已经 dirty,且未在统一计划中确认:立即停止并说明原因。
|
|
352
|
+
|
|
337
353
|
写入方式:
|
|
338
354
|
|
|
339
355
|
1. 读 `<task_dir>/task.json`
|
|
@@ -349,15 +365,21 @@ git checkout <current_branch>
|
|
|
349
365
|
写完 snapshot 后,在父仓根目录执行:
|
|
350
366
|
|
|
351
367
|
```bash
|
|
352
|
-
git status --
|
|
368
|
+
git status --porcelain
|
|
369
|
+
git diff --name-only --cached
|
|
353
370
|
```
|
|
354
371
|
|
|
355
|
-
|
|
372
|
+
bookkeeping 的自动处理范围只允许统一计划中确认过的文件:
|
|
356
373
|
|
|
357
374
|
- `<task_dir>/task.json`
|
|
358
375
|
- 如果 Step 4 选择了 reconfigure:`.trellis/config.yaml`
|
|
359
376
|
|
|
360
|
-
|
|
377
|
+
检查规则:
|
|
378
|
+
|
|
379
|
+
- 如果存在未合并路径、rebase 状态或 merge 冲突残留:立即停止,展示状态。
|
|
380
|
+
- 如果 `git diff --name-only --cached` 显示除上述允许文件之外的 staged 文件:立即停止,说明这些 staged 文件会被普通 commit 混入,必须先处理或重新确认。
|
|
381
|
+
- 如果存在除上述允许文件之外的**未暂存** dirty 文件:不要阻塞;保留它们未暂存,并在结果中提示“未纳入 snapshot commit”。
|
|
382
|
+
- 如果允许文件之外的 dirty 同时被 staged 和 unstaged 修改,按 staged 风险处理:立即停止。
|
|
361
383
|
|
|
362
384
|
如果父仓也是本次业务仓库,snapshot / config bookkeeping 仍然使用单独 commit,不和业务 commit 混合。
|
|
363
385
|
|
|
@@ -365,9 +387,11 @@ git status --short
|
|
|
365
387
|
|
|
366
388
|
```bash
|
|
367
389
|
git add <task_json_path> [".trellis/config.yaml"]
|
|
368
|
-
git commit -m "chore(task): update <task_name> push snapshot"
|
|
390
|
+
git commit --only -m "chore(task): update <task_name> push snapshot" -- <task_json_path> [".trellis/config.yaml"]
|
|
369
391
|
```
|
|
370
392
|
|
|
393
|
+
`git commit --only ... -- <paths>` 的目的是把 bookkeeping commit 限定到当前任务的 `task.json`(以及已确认的 `config.yaml`),即使父仓还有无关未暂存 dirty 文件,也不会把它们带入本次提交。执行前仍必须确认 staged 区没有无关文件;如果本地 Git 对 `--only` 参数不兼容,只有在 staged 区确认仅包含上述允许文件时,才可退回普通 `git commit -m ...`。
|
|
394
|
+
|
|
371
395
|
检查父仓是否配置 remote:
|
|
372
396
|
|
|
373
397
|
```bash
|
|
@@ -393,6 +417,7 @@ git remote -v | grep -E "^origin\s+"
|
|
|
393
417
|
|
|
394
418
|
任务进度快照:已写入 `<task_dir>/task.json`,完成 Step 1-3,下一步 Step 5。
|
|
395
419
|
父仓同步:已提交并推送 `chore(task): update <task_name> push snapshot`。
|
|
420
|
+
父仓存在未暂存 dirty 时补充:这些文件已保留未暂存,未纳入 snapshot commit:<list>
|
|
396
421
|
```
|
|
397
422
|
|
|
398
423
|
如果部分仓库已成功、后续失败,必须明确列出:
|
|
@@ -410,7 +435,7 @@ git remote -v | grep -E "^origin\s+"
|
|
|
410
435
|
2. **未识别 dirty 文件隔离** — 默认不纳入提交,除非用户明确确认。
|
|
411
436
|
3. **执行前复核** — git 状态与计划不一致时停止,不临时扩展范围。
|
|
412
437
|
4. **snapshot 合并确认** — 语义字段执行前确认,运行后字段执行后补齐。
|
|
413
|
-
5. **父仓 bookkeeping 隔离** — 只提交已确认的 `task.json` / `config.yaml
|
|
438
|
+
5. **父仓 bookkeeping 隔离** — 只提交已确认的 `task.json` / `config.yaml`;无关未暂存 dirty 文件保留未提交并提示,不阻塞;无关 staged 文件、冲突状态、目标文件预脏必须停止。
|
|
414
439
|
6. **merge 冲突处理** — 冲突时暂停,不静默跳过。
|
|
415
440
|
7. **主分支保护** — 目标分支是 `master` / `main` 时必须在计划中额外警告确认。
|
|
416
441
|
8. **不使用 force push** — 始终使用普通 push。
|
|
@@ -426,6 +451,8 @@ git remote -v | grep -E "^origin\s+"
|
|
|
426
451
|
- ❌ push 后临时追问 merge,而不是在计划中提前确认
|
|
427
452
|
- ❌ snapshot 语义内容未确认就写入 task.json
|
|
428
453
|
- ❌ 父仓 snapshot commit 混入业务代码提交
|
|
454
|
+
- ❌ 父仓存在无关未暂存 dirty 就跳过 snapshot,尽管可以只提交目标 `task.json`
|
|
455
|
+
- ❌ 父仓已有无关 staged 文件时仍执行 snapshot commit
|
|
429
456
|
- ❌ merge 冲突后静默 abort 或跳过
|
|
430
457
|
- ❌ force push 到当前分支或目标分支
|
|
431
458
|
- ❌ 在目标分支上直接开发(只 merge,不在目标分支上改代码)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"syncedAt": "2026-06-
|
|
2
|
+
"syncedAt": "2026-06-16T07:52:49.765Z",
|
|
3
3
|
"syncedFrom": "vendor/skill-garden/.trellis",
|
|
4
|
-
"sourceCommit": "
|
|
4
|
+
"sourceCommit": "d05cb9de53e0ca04f854a5471d24f4df542f0c0f",
|
|
5
5
|
"variants": {
|
|
6
6
|
"old": {
|
|
7
7
|
"claudeSkills": [],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flower-trellis",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0-beta.0",
|
|
4
4
|
"description": "一键安装/升级 Trellis 并自动融合 skill-garden 强化包(默认 Claude + agents)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"@inquirer/prompts": "^8.5.2",
|
|
16
|
-
"@mindfoldhq/trellis": "0.6.0
|
|
16
|
+
"@mindfoldhq/trellis": "0.6.0",
|
|
17
17
|
"chalk": "^5.6.2",
|
|
18
18
|
"figlet": "^1.11.0",
|
|
19
19
|
"node-pty": "^1.1.0"
|
|
@@ -129,13 +129,13 @@ export function applyEnhancements(target, opts = {}) {
|
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
// codex
|
|
132
|
+
// codex 平台后处理:旧 multi_agent_v2 兼容清理 + 合并 SessionStart hook(仅当 .codex/ 存在)
|
|
133
133
|
const codex = applyCodexTweaks(target);
|
|
134
134
|
if (codex.applied) {
|
|
135
135
|
const seg = codex.tomlChanged
|
|
136
|
-
? "config.toml
|
|
137
|
-
: "config.toml
|
|
138
|
-
console.log(` ✓ codex 调整:${seg};hooks.json
|
|
136
|
+
? "config.toml 已清理旧 multi_agent_v2"
|
|
137
|
+
: "config.toml 无需清理 multi_agent_v2";
|
|
138
|
+
console.log(` ✓ codex 调整:${seg};hooks.json 已合并 SessionStart`);
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
return { variant, installed };
|
package/src/lib/codex-tweaks.js
CHANGED
|
@@ -6,37 +6,12 @@ import path from "node:path";
|
|
|
6
6
|
*
|
|
7
7
|
* 仅当目标项目已配置 codex 平台(存在 .codex/)时生效;在 init / update 叠加阶段调用,幂等。
|
|
8
8
|
* 做两件事:
|
|
9
|
-
* 1.
|
|
10
|
-
* 2.
|
|
9
|
+
* 1. 兼容旧 Trellis:注释掉 .codex/config.toml 的 [features.multi_agent_v2] 段;
|
|
10
|
+
* 2. 合并 .codex/hooks.json —— 保留 Trellis 上游 hook 设置,只补 flower 需要的 SessionStart。
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
hooks: {
|
|
16
|
-
SessionStart: [
|
|
17
|
-
{
|
|
18
|
-
hooks: [
|
|
19
|
-
{
|
|
20
|
-
type: "command",
|
|
21
|
-
command: "python3 .codex/hooks/session-start.py",
|
|
22
|
-
timeout: 5,
|
|
23
|
-
},
|
|
24
|
-
],
|
|
25
|
-
},
|
|
26
|
-
],
|
|
27
|
-
UserPromptSubmit: [
|
|
28
|
-
{
|
|
29
|
-
hooks: [
|
|
30
|
-
{
|
|
31
|
-
type: "command",
|
|
32
|
-
command: "python3 .codex/hooks/inject-workflow-state.py",
|
|
33
|
-
timeout: 5,
|
|
34
|
-
},
|
|
35
|
-
],
|
|
36
|
-
},
|
|
37
|
-
],
|
|
38
|
-
},
|
|
39
|
-
};
|
|
13
|
+
const WORKFLOW_HOOK_SCRIPT = ".codex/hooks/inject-workflow-state.py";
|
|
14
|
+
const SESSION_START_SCRIPT = ".codex/hooks/session-start.py";
|
|
40
15
|
|
|
41
16
|
/**
|
|
42
17
|
* 注释掉 config.toml 里的 [features.multi_agent_v2] 段(段头 + 段内键,直到下一个 section)。
|
|
@@ -82,17 +57,82 @@ function commentMultiAgentV2(tomlPath) {
|
|
|
82
57
|
}
|
|
83
58
|
|
|
84
59
|
/**
|
|
85
|
-
*
|
|
86
|
-
* @
|
|
60
|
+
* 容错读取 JSON 文件;缺失或格式异常时返回空 hooks 壳。
|
|
61
|
+
* @param {string} hooksPath .codex/hooks.json 路径
|
|
62
|
+
* @returns {{hooks: object}}
|
|
87
63
|
*/
|
|
88
|
-
function
|
|
89
|
-
const desired = JSON.stringify(CODEX_HOOKS, null, 2) + "\n";
|
|
90
|
-
let current = null;
|
|
64
|
+
function readHooksConfig(hooksPath) {
|
|
91
65
|
try {
|
|
92
|
-
|
|
66
|
+
const parsed = JSON.parse(fs.readFileSync(hooksPath, "utf8"));
|
|
67
|
+
if (parsed && typeof parsed === "object") {
|
|
68
|
+
const hooks = parsed.hooks && typeof parsed.hooks === "object" ? parsed.hooks : {};
|
|
69
|
+
return { ...parsed, hooks };
|
|
70
|
+
}
|
|
93
71
|
} catch {
|
|
94
|
-
//
|
|
72
|
+
// 缺失或损坏时用空壳重建,避免 init/update 后处理失败中断主流程
|
|
73
|
+
}
|
|
74
|
+
return { hooks: {} };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 从上游 UserPromptSubmit hook 推导 SessionStart 命令。
|
|
79
|
+
*
|
|
80
|
+
* Trellis 会按平台写入 Python 命令前缀和 UTF-8 参数;复用该命令能避免 flower
|
|
81
|
+
* 在 Windows / Linux / 未来模板之间写死不同的 Python 调用方式。
|
|
82
|
+
*
|
|
83
|
+
* @param {{hooks: object}} config hooks.json 配置
|
|
84
|
+
* @returns {string} SessionStart command
|
|
85
|
+
*/
|
|
86
|
+
function sessionStartCommand(config) {
|
|
87
|
+
const groups = Array.isArray(config.hooks.UserPromptSubmit)
|
|
88
|
+
? config.hooks.UserPromptSubmit
|
|
89
|
+
: [];
|
|
90
|
+
for (const group of groups) {
|
|
91
|
+
const hooks = Array.isArray(group?.hooks) ? group.hooks : [];
|
|
92
|
+
for (const hook of hooks) {
|
|
93
|
+
if (hook?.type !== "command" || typeof hook.command !== "string") continue;
|
|
94
|
+
if (hook.command.includes(WORKFLOW_HOOK_SCRIPT)) {
|
|
95
|
+
return hook.command.replace(WORKFLOW_HOOK_SCRIPT, SESSION_START_SCRIPT);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
95
98
|
}
|
|
99
|
+
return `python3 -X utf8 ${SESSION_START_SCRIPT}`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 构造 flower 额外需要的 SessionStart hook。UserPromptSubmit 由 Trellis 上游维护,这里不覆盖。
|
|
104
|
+
* @param {{hooks: object}} config hooks.json 配置
|
|
105
|
+
* @returns {Array<object>} SessionStart hook 数组
|
|
106
|
+
*/
|
|
107
|
+
function sessionStartHook(config) {
|
|
108
|
+
return [
|
|
109
|
+
{
|
|
110
|
+
hooks: [
|
|
111
|
+
{
|
|
112
|
+
type: "command",
|
|
113
|
+
command: sessionStartCommand(config),
|
|
114
|
+
timeout: 30,
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 合并 flower 需要的 SessionStart hook,保留 Trellis 上游 UserPromptSubmit 设置。
|
|
123
|
+
*
|
|
124
|
+
* Trellis 0.6.0 已把 UserPromptSubmit 的 UTF-8 与 timeout 策略写进模板;flower 只负责
|
|
125
|
+
* 补上 SessionStart,否则整文件覆盖会让上游模板改进在 update 后丢失。
|
|
126
|
+
*
|
|
127
|
+
* @param {string} hooksPath .codex/hooks.json 路径
|
|
128
|
+
* @returns {boolean} 是否写入
|
|
129
|
+
*/
|
|
130
|
+
function mergeHooks(hooksPath) {
|
|
131
|
+
const config = readHooksConfig(hooksPath);
|
|
132
|
+
config.hooks.SessionStart = sessionStartHook(config);
|
|
133
|
+
|
|
134
|
+
const desired = JSON.stringify(config, null, 2) + "\n";
|
|
135
|
+
const current = fs.existsSync(hooksPath) ? fs.readFileSync(hooksPath, "utf8") : "";
|
|
96
136
|
if (current === desired) return false;
|
|
97
137
|
fs.writeFileSync(hooksPath, desired);
|
|
98
138
|
return true;
|
|
@@ -107,6 +147,6 @@ export function applyCodexTweaks(target) {
|
|
|
107
147
|
const codexDir = path.join(target, ".codex");
|
|
108
148
|
if (!fs.existsSync(codexDir)) return { applied: false };
|
|
109
149
|
const tomlChanged = commentMultiAgentV2(path.join(codexDir, "config.toml"));
|
|
110
|
-
const hooksWritten =
|
|
150
|
+
const hooksWritten = mergeHooks(path.join(codexDir, "hooks.json"));
|
|
111
151
|
return { applied: true, tomlChanged, hooksWritten };
|
|
112
152
|
}
|