flower-trellis 0.3.1 → 0.4.0-beta.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 CHANGED
@@ -82,7 +82,7 @@ flower-trellis -v
82
82
  | `--target <dir>` | 目标目录(默认当前目录) |
83
83
  | `--no-update-check` | 本次跳过 flower-trellis 新版本检测(等价环境变量 `FLOWER_NO_UPDATE_CHECK=1`) |
84
84
 
85
- 未指定平台时,交互模式会弹出多选菜单(默认勾选 Claude Code + Codex);也可直接传 `--claude` / `--codex` / `--cursor` 等指定,或用 `-y` 跳过菜单。其余未识别的 flag(如 `-u`、`-f`、`--template`)一律透传给 Trellis。
85
+ 未指定平台时,交互模式会弹出多选菜单(默认勾选 Claude Code + Codex);也可直接传 `--claude` / `--codex` / `--cursor` / `--devin` / `--zcode` / `--trae` 等指定,或用 `-y` 跳过菜单。`--windsurf` 仍作为 Devin 的旧别名透传给 Trellis。其余未识别的 flag(如 `-u`、`-f`、`--template`、`--with-statusline`)一律透传给 Trellis。
86
86
 
87
87
  ### 自动版本检测
88
88
 
@@ -0,0 +1,133 @@
1
+ ---
2
+ name: trellis-auto-loop
3
+ description: "启动、恢复和推进 Trellis 自动任务循环。用于用户明确要求 auto loop、自动跑任务、/goal 类似流程、一次跑多个任务、继续自动 run、查看/停止 auto-loop,或压缩恢复后需要从 .trellis/scripts/auto_loop.py 读取下一步。"
4
+ ---
5
+
6
+ # Trellis Auto Loop
7
+
8
+ 用 `.trellis/scripts/auto_loop.py` 驱动一个接近 `/goal` 的任务循环。Python runner 是状态权威;本 skill 只负责把用户意图映射到 runner 命令,并按 runner 返回的 action 调用现有 Trellis workflow / skill / subagent。
9
+
10
+ ## 核心规则
11
+
12
+ - 只有用户明确要求自动跑、auto loop、goal-like、继续自动 run、批量任务队列时才启动或恢复;不要把普通实现请求自动升级为 auto-loop。
13
+ - 每次开始、恢复、压缩后继续时,先运行 runner 的 `resume` 或 `next`,不要凭聊天摘要推断下一步。
14
+ - 每完成一个 action,必须用 `record --action <next 返回的 action>` 精确写回结果;runner 会拒绝缺失或不匹配的 action。写回后立即再调用 `next`,直到 `done`、`blocked` 或需要用户决策。
15
+ - 默认 profile 是 `commit-only`:自动推进到本地 commit,不 push、不发布、不归档。
16
+ - 多任务只按用户显式给出的任务顺序执行;同一 worktree 不并发。
17
+ - 启动 runner 前先完成 route 准备度判断:已有当前任务 runtime route 决策或个人 `.trellis/.route-prefs.tmp` 时可启动;没有时先进入 `trellis-route` 正常询问 / fallback,写入真实决策后再启动。
18
+ - auto-loop 不默认写 `route_authorization`;如果未来显式传 route 参数,只能表示用户本次明确给出的临时策略,不能当成模型真实执行结果。
19
+ - 代码提交必须走 `trellis-push` 的 commit-only 语义;不要裸 `git commit` / `git push`。
20
+
21
+ ## 启动
22
+
23
+ 用户给了任务列表时,按原顺序传入;用户只说当前任务时,用 `task.py current --source` 的当前任务。
24
+
25
+ 启动前对当前任务执行 route 准备度检查:
26
+
27
+ ```bash
28
+ python3 .agents/skills/trellis-route/scripts/route_state.py resolve --target implement
29
+ python3 .agents/skills/trellis-route/scripts/route_state.py resolve --target check
30
+ ```
31
+
32
+ 如果任一 target 返回 `status=miss`,先按 `trellis-route` 的正常 numbered fallback 询问用户并写入 runtime;不要替用户默认 inline 或 subagent。route 准备完成后启动 runner:
33
+
34
+ ```bash
35
+ python3 ./.trellis/scripts/auto_loop.py start \
36
+ --tasks <task> [<task> ...] \
37
+ --profile commit-only
38
+ ```
39
+
40
+ 多任务队列中,当前任务切换到下一个任务且缺少该任务 route 决策时,回到 `trellis-route` 获取该任务真实选择,再继续 `next` / `record`。个人 `.trellis/.route-prefs.tmp` 会由 `trellis-route` 统一复用并写回 runtime。
41
+
42
+ 启动后立即运行:
43
+
44
+ ```bash
45
+ python3 ./.trellis/scripts/auto_loop.py next
46
+ ```
47
+
48
+ ## 恢复
49
+
50
+ 压缩、重开会话、用户说“继续自动跑 / continue auto loop”时:
51
+
52
+ ```bash
53
+ python3 ./.trellis/scripts/auto_loop.py resume
54
+ python3 ./.trellis/scripts/auto_loop.py next
55
+ ```
56
+
57
+ `resume_capsule` 只用于展示;下一步以 `next` 返回的 JSON 为准。
58
+
59
+ ## Action 映射
60
+
61
+ | runner action | 主 agent 动作 | 成功 record |
62
+ | --- | --- | --- |
63
+ | `refresh_brief` | 使用 `trellis-task-brief` 生成并展示 brief | `record --action refresh_brief --result ok` |
64
+ | `start_task` | 执行返回的 `task.py start ...` 命令 | `record --action start_task --result ok` |
65
+ | `run_implement` | 进入 Phase 2.1,先用 `trellis-route(target=implement)` 决定 inline/subagent,再实现 | `record --action run_implement --result ok --route-mode <mode> --route-source <source>` |
66
+ | `run_check_all` | 进入 Phase 2.2,先用 `trellis-route(target=check)`,执行 check-all | `record --action run_check_all --result ok --route-mode <mode> --route-source <source>` |
67
+ | `run_fix` | 根据 `last_failure` 修复,复用当前任务 implement route | `record --action run_fix --result ok --route-mode <mode> --route-source <source>` |
68
+ | `run_recheck` | 复用当前任务 check route,重新 check-all | `record --action run_recheck --result ok --route-mode <mode> --route-source <source>` |
69
+ | `run_spec_update` | 有代码/测试证据时用 `trellis-update-spec`;无必要更新也 record ok | `record --action run_spec_update --result ok` |
70
+ | `commit_only` | 进入 `trellis-push` commit-only 语义:先由 AI 基于 task artifacts 与 git diff 生成提交计划,只提交当前任务可归属文件 | 执行成功后 `record --action commit_only --result ok --commit <hash>` |
71
+
72
+ 失败时写回:
73
+
74
+ ```bash
75
+ python3 ./.trellis/scripts/auto_loop.py record \
76
+ --action <action> \
77
+ --result failed \
78
+ --failure-type <type> \
79
+ --summary "<失败摘要>" \
80
+ --files <file> [<file> ...]
81
+ ```
82
+
83
+ 需要用户产品决策或越权时写回 blocked:
84
+
85
+ ```bash
86
+ python3 ./.trellis/scripts/auto_loop.py record \
87
+ --action <action> \
88
+ --result blocked \
89
+ --failure-type <type> \
90
+ --summary "<阻塞原因>"
91
+ ```
92
+
93
+ runner 会按 3 轮 fix/recheck 预算决定继续、跳过当前任务或结束队列。
94
+
95
+ route action 成功回写时必须带上 `trellis-route` 输出里的真实 `mode` / `source`,例如
96
+ `--route-mode inline --route-source route-prefs` 或
97
+ `--route-mode check-all-subagent --route-source trellis-route`;不要写 auto-loop 默认值。
98
+
99
+ ## Commit-Only 预授权
100
+
101
+ auto-loop 的 `commit-only` profile 是用户对“当前 run 内任务相关本地提交”的一次性预授权。只有同时满足这些条件时,`trellis-push` 可以跳过二次聊天确认:
102
+
103
+ - `.trellis/scripts/auto_loop.py status` 显示当前 `run_status=running`,且 `outstanding_action.action=commit_only`、`outstanding_action.task` 等于当前活动任务。
104
+ - profile 是 `commit-only`。
105
+ - 模式是 commit-only,不 push、不 merge、不发布、不归档。
106
+ - AI 已基于当前任务 artifacts、`git status`、`git diff` 和必要的文件内容生成提交计划,并能说明每个 planned file 为什么属于当前任务。
107
+ - 提交计划只包含当前任务可归属文件;未识别 dirty 文件保留未提交并写入结果摘要。
108
+ - 执行前复核 git 状态仍与计划一致。
109
+
110
+ 如果计划包含未识别 staged 文件、冲突、push/merge/release/archive、真实外部系统或生产数据效果,停止并把当前任务记为 blocked。
111
+
112
+ 不要用脚本基于时间差或 dirty baseline 猜测文件归属;语义归属由 AI 负责判断,`trellis-push` 负责展示/复核计划、精确暂存 planned files、执行本地 commit,并把 commit hash 回写 runner。预检不安全时只把当前队列项记为 blocked/skipped,并让 auto-loop 可继续后续任务。
113
+
114
+ ## 状态与停止
115
+
116
+ 查看:
117
+
118
+ ```bash
119
+ python3 ./.trellis/scripts/auto_loop.py status
120
+ ```
121
+
122
+ 停止:
123
+
124
+ ```bash
125
+ python3 ./.trellis/scripts/auto_loop.py stop --reason "<原因>"
126
+ ```
127
+
128
+ ## 不要做
129
+
130
+ - 不要手写或手改 `.trellis/.runtime/auto-loop/*.json`。
131
+ - 不要把 `.trellis/.runtime/` 或 `.trellis/.route-prefs.tmp` 加入提交。
132
+ - 不要在 auto-loop 外把普通 check 的 post-check stop gate 当成可跳过。
133
+ - 不要为模糊需求自动创建任务并开跑;先回到 Trellis planning。
@@ -8,6 +8,8 @@ description: "提交并推送配置仓库,可选合并到目标分支,并同
8
8
 
9
9
  核心原则:**先计划、一次确认、后执行**。在任何 `git add`、`git commit`、`git push`、`git merge` 之前,必须先展示完整执行计划,并获得用户确认。
10
10
 
11
+ 例外:当 `trellis-auto-loop` 已显式以 `profile=commit-only` 启动,且 runner 当前有 `outstanding_action.action=commit_only` 时,用户启动 auto-loop 本身就是本次 run 内任务相关本地提交的预授权。此时仍必须由 AI 生成并自检提交计划,但可跳过二次聊天确认;计划只允许 commit-only、不 push/merge/release/archive,并在成功后把 commit hash 回写 runner。预检不安全时只标记当前 auto-loop item blocked/skipped,后续队列仍可继续。
12
+
11
13
  支持多仓库(frontend / backend 等),merge 目标分支记录在 `.trellis/config.yaml` 的 `packages.<name>.merge_target` 中。
12
14
 
13
15
  ---
@@ -62,9 +64,24 @@ Step 6 输出结果
62
64
  | 重新配置 | 本次允许重新指定 merge 目标,并回写 `merge_target` | `重新配置 push 目标分支` / `reconfigure push` |
63
65
  | 临时目标 | 本次临时指定 merge 目标,不修改配置 | `push 到 hotfix 分支` |
64
66
  | snapshot-only | 不提交业务代码,只写入 / 同步任务进度快照 | `只更新 snapshot` / `snapshot-only` |
67
+ | auto-loop commit-only | 由 `trellis-auto-loop` runner 的 `commit_only` action 触发,只提交本任务可归属文件,不二次确认、不 push | `trellis-auto-loop` 内部触发 |
65
68
 
66
69
  `snapshot-only` 只在用户明确要求时使用。它仍必须展示统一执行计划;`pushed_commits` 取计划中确认的现有 commit,或按用户说明记录。
67
70
 
71
+ ### 0.1.1 auto-loop commit-only 判定
72
+
73
+ 只有同时满足以下条件,才可走 auto-loop 预授权路径:
74
+
75
+ - `.trellis/scripts/auto_loop.py status` 显示当前 `run_status=running`,profile 为 `commit-only`。
76
+ - status 摘要里的 `outstanding_action.action` 是 `commit_only`,且 `outstanding_action.task` 等于当前活动任务。
77
+ - 本次 `trellis-push` 语义是 commit-only,不包含 push / merge / release / archive。
78
+ - 计划拟提交文件全部能归属当前任务;未识别 dirty 文件不纳入提交。
79
+ - 执行前复核 git 状态与计划一致。
80
+
81
+ 任一条件不满足时,不得使用预授权。普通 `trellis-push` 继续走“展示计划并等待确认”的规则。
82
+
83
+ auto-loop 预授权路径不使用额外提交 helper。AI 负责根据当前任务 artifacts、`git status`、`git diff` 和必要的文件内容判断文件归属,输出 planned files / retained files / commit message / 归属理由;`trellis-push` 负责边界复核:当前 action/profile/task 匹配、staged 区为空、无冲突、planned files 当前 dirty、planned files 不含 `.trellis/.runtime/`、`.trellis/.route-prefs.tmp`、其他任务目录或未解释文件。通过后只 `git add` planned files 并执行本地 commit,随后回写 `auto_loop.py record --action commit_only --result ok --commit <hash> ...`。预检失败时回写 `record --result blocked`,该 blocked 只影响当前 item;auto-loop runner 后续 `next` 应继续寻找后续 pending 任务。只有 merge/rebase 冲突、repo 状态不可读、脚本损坏或用户明确 stop 才停止整个 run。
84
+
68
85
  ### 0.2 发现候选 Git 仓库
69
86
 
70
87
  读取 `.trellis/config.yaml` 中的 `packages` 配置,但不要把 `git: true` 当作唯一仓库发现规则。`git: true` 主要用于 Trellis session context 展示独立包仓库状态;`trellis-push` 必须以实际 Git root 为准发现可提交仓库:
@@ -131,7 +148,7 @@ git log <base_branch>..HEAD --oneline
131
148
 
132
149
  ## Step 2: 生成统一执行计划
133
150
 
134
- 这是唯一的常规确认点。**确认前禁止执行任何 `git add` / commit / push / merge / task.json 写入。**
151
+ 这是唯一的常规确认点。**确认前禁止执行任何 `git add` / commit / push / merge / task.json 写入。** 唯一例外是已满足 Step 0.1.1 的 auto-loop commit-only 预授权;该路径仍要生成计划并执行自检,只是不再等待用户二次确认。
135
152
 
136
153
  ### 2.1 生成业务提交计划
137
154
 
@@ -13,9 +13,9 @@ description: |
13
13
 
14
14
  # Trellis 路由器:implement / check 执行模式选择
15
15
 
16
- 主 agent 进入 Phase 2.1 实现路由或 Phase 2.2 检查路由时调用本 skill。当前上下文或 session runtime state 内已有合法来源的最近 route 决策时,后续实现、修复、重检默认复用该决策;没有合法决策时才进入本 skill 或同编号 fallback。提交前确实需要最终复查时,回到 Phase 2.2 并复用当前任务的合法 check route,除非用户明确要求重选。
16
+ 主 agent 进入 Phase 2.1 实现路由或 Phase 2.2 检查路由时调用本 skill。当前上下文或 session runtime state 内已有合法来源、target 匹配、且 task 等于当前任务路径的最近 route 决策时,后续实现、修复、重检默认复用该决策;没有合法决策时才进入本 skill 或同编号 fallback。提交前确实需要最终复查时,回到 Phase 2.2 并复用当前任务的合法 check route,除非用户明确要求重选。
17
17
 
18
- 个人配置只写入 `.trellis/.route-prefs.tmp`。该文件匹配 `.trellis/.gitignore` 的 `*.tmp` 规则,属于开发者本地偏好,不纳入 git,也不影响其他开发者。
18
+ 个人配置只写入 `.trellis/.route-prefs.tmp`。该文件匹配 `.trellis/.gitignore` 的 `*.tmp` 规则,属于开发者本地偏好,不纳入 git,也不影响其他开发者。auto-loop 可在 `.trellis/.runtime/auto-loop/<run-id>.json` 写入临时 route 授权;它不是个人偏好,优先级低于 `.route-prefs.tmp`,只用于减少 auto 模式下的交互打断。
19
19
 
20
20
  本轮 route 决策写入当前 session runtime 文件 `.trellis/.runtime/sessions/<context-key>.json` 的 `route_decisions` 字段。该文件匹配 `.trellis/.gitignore` 的 `.runtime/` 规则,只保存当前 AI session/window 的执行状态,用于压缩后恢复;它不是任务属性,不写入 `.trellis/tasks/**`,也不是长期团队配置。
21
21
 
@@ -25,9 +25,9 @@ description: |
25
25
 
26
26
  个人 route 配置只决定“已获准执行后的模式”,不是开工授权。调用 helper 前,必须确认当前 workflow 已允许进入对应 target:implement 需要任务已完成规划确认并处于 `in_progress`;check 用于 Phase 2.2 检查执行,或用户明确要求最终复查 / 轻量检查。最终复查只有在 Phase 2.2 结果缺失、风险较高或用户明确要求复查时才回到 Phase 2.2;回到 Phase 2.2 后优先复用当前任务的合法 check route,除非用户明确要求重选/临时改/清除默认。如果仍在 planning、等待用户确认,或用户表达“等一下 / 我再想想”,停止,不读取 runtime/prefs。
27
27
 
28
- 合法 route 决策必须能追溯到 `trellis-route`、同编号 fallback 选项,或由本 skill 读取到的有效 `.trellis/.route-prefs.tmp` 配置。runtime state 只能保存和恢复这些原始合法来源,不能把 `.runtime` 自身当成新的 `route_decision.source`。用户自然语言说过“inline/subagent”、compact summary、SessionStart 摘要、`codex-mode`、空 `.route-prefs.tmp`、旧单值偏好,都不能单独作为有效 route 决策。
28
+ 合法 route 决策必须能追溯到 `trellis-route`、同编号 fallback 选项、由本 skill 读取到的有效 `.trellis/.route-prefs.tmp` 配置,或由 route helper 校验过的 auto-loop 临时 route 授权,并且 `task` 字段必须等于当前 `task.py current --source` 返回的任务路径。runtime state 只能保存和恢复这些原始合法来源,不能把 `.runtime` 自身当成新的 `route_decision.source`。用户自然语言说过“inline/subagent”、compact summary、SessionStart 摘要、`codex-mode`、空 `.route-prefs.tmp`、旧单值偏好,都不能单独作为有效 route 决策。
29
29
 
30
- 当前上下文内已有 target 匹配且来源合法的 route 决策时,后续实现、check 发现问题、用户指出刚检查过的实现有问题、修复后重检、提交前复查均默认复用最近 implement/check 路由;除非用户明确要求重选/临时改/清除默认,不再调用本 skill。当前上下文没有 route 决策但 runtime state 命中时,本 skill 恢复该决策并输出同样的结构化 `route_decision`。
30
+ 当前上下文内已有 target 匹配、task 等于当前任务路径、且来源合法的 route 决策时,后续实现、check 发现问题、用户指出刚检查过的实现有问题、修复后重检、提交前复查均默认复用最近 implement/check 路由;除非用户明确要求重选/临时改/清除默认,不再调用本 skill。当前上下文没有 route 决策但 runtime state 命中时,本 skill 恢复该决策并输出同样的结构化 `route_decision`。如果上下文里只有上一个任务的 `route_decision`,必须忽略并重新解析当前任务。
31
31
 
32
32
  Codex inline mode 只表示主会话默认直接执行,不是 route 选项过滤器。即使当前上下文出现 `<codex-mode>inline...do not dispatch...</codex-mode>` 或 `workflow-state:in_progress-inline`,也不能推断“只能 inline”或跳过 subagent 选项;仍必须通过 helper resolve runtime/prefs,或在无有效配置时展示正常 inline/subagent 选项。若本 skill 的紧邻路由决定是 subagent,本步骤允许主 agent dispatch 对应 implement/check sub-agent;禁止的是绕过 `trellis-route` 直接 dispatch。
33
33
 
@@ -50,7 +50,7 @@ Codex inline mode 只表示主会话默认直接执行,不是 route 选项过
50
50
 
51
51
  ## Step 0.5: 解析已有 route state
52
52
 
53
- 仅在没有覆盖意图、当前上下文没有 target 匹配的合法 `route_decision` 时调用 helper 解析已有状态。helper 的解析顺序固定为:当前 session runtime 文件里的 `route_decisions` → `.trellis/.route-prefs.tmp`。命中 `.route-prefs.tmp` 时,helper 会自动把 `source=route-prefs` 的决策写回当前 session runtime,后续压缩恢复不需要再次读 prefs
53
+ 仅在没有覆盖意图、当前上下文没有 target + 当前 task 匹配的合法 `route_decision` 时调用 helper 解析已有状态。helper 的解析顺序固定为:当前 session runtime 文件里的 `route_decisions` → `.trellis/.route-prefs.tmp` `.trellis/.runtime/auto-loop/<run-id>.json` 临时授权。命中 `.route-prefs.tmp` auto-loop 临时授权时,helper 会自动把对应决策写回当前 session runtime,后续压缩恢复不需要再次读 prefs 或 auto-loop 状态。
54
54
 
55
55
  调用随本 skill 分发的 helper;不要在对话中内嵌或改写 helper 逻辑:
56
56
 
@@ -60,7 +60,7 @@ python3 .agents/skills/trellis-route/scripts/route_state.py resolve --target <im
60
60
  python3 .claude/skills/trellis-route/scripts/route_state.py resolve --target <implement|check>
61
61
  ```
62
62
 
63
- helper 只接受当前 session 或唯一 session fallback 的 `.trellis/.runtime/sessions/<context-key>.json`,并只从 `route_decisions.<target>` 恢复决策。命中时输出 `{"status":"hit", ...}`,其中默认输出里的 `mode` / `source` 已经过 task/target/source/mode/scope 校验,可跳过 Step 2 并进入 Step 3 输出决策。`origin=route-prefs` 表示来自个人 route 配置,并且 helper 已写回 session runtime state;`origin=runtime` 表示来自 session runtime state。
63
+ helper 只接受当前 session 或唯一 session fallback 的 `.trellis/.runtime/sessions/<context-key>.json`,并只从 `route_decisions.<target>` 恢复当前任务的决策。命中时输出 `{"status":"hit", ...}`,其中默认输出里的 `task` / `mode` / `source` 已经过 task/target/source/mode/scope 校验,可跳过 Step 2 并进入 Step 3 输出决策。`origin=route-prefs` 表示来自个人 route 配置,并且 helper 已写回 session runtime state;`origin=auto-loop` 表示来自 auto-loop 临时授权且 helper 已写回 session runtime state;`origin=runtime` 表示来自 session runtime state。
64
64
 
65
65
  helper 默认输出为精简 JSON,只包含 route 执行必需的 `status`、`origin`、`mode`、`source`/`reason` 等字段。需要排查完整 `decision`、session 文件、context key、任务路径、个人配置路径或写回标记时,在同一命令末尾加 `--verbose`;不要为了诊断信息额外读取 runtime 文件。
66
66
 
@@ -181,7 +181,9 @@ python3 .agents/skills/trellis-route/scripts/route_state.py clear-pref --target
181
181
  python3 .claude/skills/trellis-route/scripts/route_state.py clear-pref --target <implement|check>
182
182
  ```
183
183
 
184
- helper 写入规则:保留另一个 target 的 runtime 决策和偏好;覆盖当前 target;`source` 保持原始合法来源,正常 skill 交互用 `trellis-route`,普通聊天编号 fallback 用 `numbered-fallback`,prefs 命中由 helper 写 `route-prefs`;保留 session runtime 文件中的 `platform`、`current_task`、`current_run` 等既有字段;内部 `task` 必须写当前 `task.py current --source` 的任务路径。写入失败不阻塞本次 route 输出,但要在回复中简短说明未持久化,压缩后可能再次询问。
184
+ helper 写入规则:保留另一个 target 的 runtime 决策和偏好;覆盖当前 target;`source` 保持原始合法来源,正常 skill 交互用 `trellis-route`,普通聊天编号 fallback 用 `numbered-fallback`,prefs 命中由 helper 写 `route-prefs`,auto-loop 临时授权命中由 helper 写 `auto-loop`;保留 session runtime 文件中的 `platform`、`current_task`、`current_run`、`current_auto_run` 等既有字段;内部 `task` 必须写当前 `task.py current --source` 的任务路径。写入失败不阻塞本次 route 输出,但要在回复中简短说明未持久化,压缩后可能再次询问。
185
+
186
+ 当同一 session 从一个任务切换到另一个任务时,helper 写入当前任务任一 target 的决策前会清理 runtime 中属于其他任务的 `route_decisions`。个人默认仍保留在 `.trellis/.route-prefs.tmp`;清理只影响当前 session 的临时恢复状态,目的是避免新任务的 check 阶段误复用上一个任务的 check 路由。
185
187
 
186
188
  ---
187
189
 
@@ -205,6 +207,7 @@ helper 写入规则:保留另一个 target 的 runtime 决策和偏好;覆
205
207
  ```markdown
206
208
  路由决定:<inline/subagent> <implement | check-all | check>
207
209
  [来自个人 route 配置:`.trellis/.route-prefs.tmp` (<key>=<value>)。]
210
+ [来自 auto-loop 临时 route 授权:`.trellis/.runtime/auto-loop/<run-id>.json`。]
208
211
  [来自 session runtime route state:`.trellis/.runtime/sessions/<context-key>.json` 的 `route_decisions`。]
209
212
  [已写入 session runtime route state:`.trellis/.runtime/sessions/<context-key>.json` 的 `route_decisions`。]
210
213
  [说明:用户明确请求轻量检查,使用隐藏逃生口。]
@@ -212,8 +215,9 @@ helper 写入规则:保留另一个 target 的 runtime 决策和偏好;覆
212
215
  route_decision:
213
216
  target: <implement | check>
214
217
  mode: <inline | subagent | check-all-inline | check-all-subagent | check-inline | check-subagent>
215
- source: <trellis-route | route-prefs | numbered-fallback>
218
+ source: <trellis-route | route-prefs | auto-loop | numbered-fallback>
216
219
  scope: task
220
+ task: <current task path>
217
221
 
218
222
  接下来主 agent 应当:
219
223
  - <路由表里对应的工具调用形式>
@@ -223,7 +227,7 @@ route_decision:
223
227
  - <要避免的工具调用>
224
228
  ```
225
229
 
226
- 中括号内行为条件性出现:仅命中个人配置时显示配置行;仅命中 runtime state 时显示“来自”行;写入 runtime state 成功时显示“已写入”行;仅轻量 check 时显示隐藏逃生口说明;仅 implement subagent + skip_compile=true 时附加“跳过编译”段。`route_decision` 必须保留在回复中,但默认只保留 target/mode/source/scope;需要 task/path/decided_at 等诊断字段时重新调用 helper 并加 `--verbose`。compact summary 若只有自然语言描述,后续 agent 仍应优先读取 runtime state,而不是把 summary 当证据。
230
+ 中括号内行为条件性出现:仅命中个人配置时显示配置行;仅命中 runtime state 时显示“来自”行;写入 runtime state 成功时显示“已写入”行;仅轻量 check 时显示隐藏逃生口说明;仅 implement subagent + skip_compile=true 时附加“跳过编译”段。`route_decision` 必须保留在回复中,并至少保留 target/mode/source/scope/task;需要 path/decided_at 等诊断字段时重新调用 helper 并加 `--verbose`。compact summary 若只有自然语言描述,后续 agent 仍应优先读取 runtime state,而不是把 summary 当证据。
227
231
 
228
232
  ---
229
233
 
@@ -233,13 +237,14 @@ route_decision:
233
237
  2. **runtime state 私有**:`.trellis/.runtime/sessions/` 是 session/window 状态,gitignored,不能进入提交计划。
234
238
  3. **压缩恢复少打断**:当前上下文没有 route 决策时,先读 runtime state;命中当前任务/target 的合法决策就复用,不查 `trellis mem`,也不重问。
235
239
  4. **个人默认次于本轮决策**:runtime state 表示“本轮已选”,优先于 `.route-prefs.tmp`。
236
- 5. **显式覆盖优先于一切**:用户要求临时改、重新选择或清除默认时,必须重新展示选项,不能让 runtime state 或配置优先。
237
- 6. **当前任务复用路由**:当前任务内已有合法来源的最近 implement/check 路由时,后续实现、修复、重检和复查默认沿用,不再次询问模式。
238
- 7. **check 默认全面检查**:普通 check 路由只展示 `check-all` inline/subagent,不推荐轻量 `trellis-check`。
239
- 8. **轻量 check 是隐藏逃生口**:只有用户明确请求 `light check` / `轻量检查` 时才可走轻量 `trellis-check`。
240
- 9. **决策与执行分离**:本 skill 只输出指令,下一轮由主 agent 调工具。
241
- 10. **严格执行用户选择**:路由结论一旦输出,主 agent 必须按指令执行,不可“出于谨慎”再换路径。
242
- 11. **Codex inline 不裁剪选项**:Codex inline 是默认执行模式,不是只能 inline 的强制模式;route 明确选中 subagent 时,本步骤可按 subagent 路径执行。
240
+ 5. **auto 授权低于个人默认**:auto-loop 临时授权只在 runtime 和个人偏好都 miss 时生效。
241
+ 6. **显式覆盖优先于一切**:用户要求临时改、重新选择或清除默认时,必须重新展示选项,不能让 runtime state 或配置优先。
242
+ 7. **当前任务复用路由**:当前任务内已有合法来源的最近 implement/check 路由时,后续实现、修复、重检和复查默认沿用,不再次询问模式。
243
+ 8. **check 默认全面检查**:普通 check 路由只展示 `check-all` inline/subagent,不推荐轻量 `trellis-check`。
244
+ 9. **轻量 check 是隐藏逃生口**:只有用户明确请求 `light check` / `轻量检查` 时才可走轻量 `trellis-check`。
245
+ 10. **决策与执行分离**:本 skill 只输出指令,下一轮由主 agent 调工具。
246
+ 11. **严格执行用户选择**:路由结论一旦输出,主 agent 必须按指令执行,不可“出于谨慎”再换路径。
247
+ 12. **Codex inline 不裁剪选项**:Codex inline 是默认执行模式,不是只能 inline 的强制模式;route 明确选中 subagent 时,本步骤可按 subagent 路径执行。
243
248
 
244
249
  ---
245
250
 
@@ -12,7 +12,7 @@ from pathlib import Path
12
12
  from typing import Any
13
13
 
14
14
 
15
- VALID_SOURCES = {"trellis-route", "numbered-fallback", "route-prefs"}
15
+ VALID_SOURCES = {"trellis-route", "numbered-fallback", "route-prefs", "auto-loop"}
16
16
  VALID_MODES = {
17
17
  "implement": {"inline", "subagent"},
18
18
  "check": {
@@ -83,6 +83,7 @@ def _output(args: argparse.Namespace, data: dict[str, Any], verbose: dict[str, A
83
83
  def _decision_summary(decision: dict[str, Any]) -> dict[str, Any]:
84
84
  """提取默认输出需要的最小 route 决策字段。"""
85
85
  return {
86
+ "task": decision.get("task"),
86
87
  "mode": decision.get("mode"),
87
88
  "source": decision.get("source"),
88
89
  }
@@ -129,6 +130,16 @@ def _pref_path(repo_root: Path) -> Path:
129
130
  return repo_root / ".trellis/.route-prefs.tmp"
130
131
 
131
132
 
133
+ def _auto_loop_dir(repo_root: Path) -> Path:
134
+ """返回 auto-loop runtime 目录。"""
135
+ return repo_root / ".trellis/.runtime/auto-loop"
136
+
137
+
138
+ def _auto_loop_pointer(repo_root: Path) -> Path:
139
+ """返回当前 auto-loop run 指针文件。"""
140
+ return _auto_loop_dir(repo_root) / "current.json"
141
+
142
+
132
143
  def _read_prefs(repo_root: Path) -> dict[str, str]:
133
144
  """读取个人 route 默认配置,只保留合法 key-value。"""
134
145
  path = _pref_path(repo_root)
@@ -149,6 +160,74 @@ def _read_prefs(repo_root: Path) -> dict[str, str]:
149
160
  return prefs
150
161
 
151
162
 
163
+ def _running_auto_run_paths(repo_root: Path) -> list[Path]:
164
+ """扫描当前项目内所有 running auto-loop run。"""
165
+ running: list[Path] = []
166
+ for path in sorted(_auto_loop_dir(repo_root).glob("auto-*.json")):
167
+ state = _read_json(path)
168
+ if state.get("status") == "running":
169
+ running.append(path)
170
+ return running
171
+
172
+
173
+ def _auto_state_path(repo_root: Path, run_id: Any) -> Path | None:
174
+ """把 run id 转成状态文件路径,非法值返回 None。"""
175
+ if not isinstance(run_id, str) or not run_id.strip():
176
+ return None
177
+ return _auto_loop_dir(repo_root) / f"{run_id.strip()}.json"
178
+
179
+
180
+ def _auto_route_mode(repo_root: Path, context_key: str, target: str) -> tuple[str | None, Path | None, str | None]:
181
+ """读取当前 running auto-loop run 的临时 route 授权。
182
+
183
+ auto 授权低于个人 `.route-prefs.tmp`,只在当前 session runtime 绑定了
184
+ running `current_auto_run`,或全局 current 指针能指向 running run 时生效。
185
+ 如果这些指针 stale,则忽略 stale pointer,并 fallback 到唯一 running run。
186
+ """
187
+ session_path = _session_path(repo_root, context_key)
188
+ session = _read_json(session_path)
189
+ candidate_paths: list[Path] = []
190
+ stale_paths: list[Path] = []
191
+
192
+ for run_id in (session.get("current_auto_run"), _read_json(_auto_loop_pointer(repo_root)).get("run_id")):
193
+ path = _auto_state_path(repo_root, run_id)
194
+ if path is None:
195
+ continue
196
+ state = _read_json(path)
197
+ if state.get("status") == "running":
198
+ candidate_paths.append(path)
199
+ else:
200
+ stale_paths.append(path)
201
+
202
+ if not candidate_paths:
203
+ candidate_paths = _running_auto_run_paths(repo_root)
204
+ unique_paths = []
205
+ seen: set[str] = set()
206
+ for path in candidate_paths:
207
+ key = str(path)
208
+ if key in seen:
209
+ continue
210
+ seen.add(key)
211
+ unique_paths.append(path)
212
+ if len(unique_paths) != 1:
213
+ reason = "no-unique-auto-run"
214
+ if stale_paths and not unique_paths:
215
+ reason = "stale-auto-run-pointer"
216
+ return None, stale_paths[0] if stale_paths else None, reason
217
+
218
+ path = unique_paths[0]
219
+ state = _read_json(path)
220
+
221
+ auth = state.get("route_authorization")
222
+ if not isinstance(auth, dict):
223
+ return None, path, "no-route-authorization"
224
+
225
+ mode = auth.get(target)
226
+ if mode in PREF_MODES[target]:
227
+ return mode, path, None
228
+ return None, path, "invalid-auto-route-mode"
229
+
230
+
152
231
  def _write_prefs(repo_root: Path, prefs: dict[str, str]) -> None:
153
232
  """写入个人 route 默认配置;为空则删除文件。"""
154
233
  path = _pref_path(repo_root)
@@ -218,6 +297,13 @@ def _write_runtime_decision(
218
297
  decisions = context.get("route_decisions")
219
298
  if not isinstance(decisions, dict):
220
299
  decisions = {}
300
+ # 同一 AI session 可能连续处理多个 Trellis 任务。写入新任务的任一路由时,
301
+ # 先丢弃旧任务的 runtime 决策,避免后续 check 阶段绕过 helper 时误复用上个任务的选择。
302
+ decisions = {
303
+ key: value
304
+ for key, value in decisions.items()
305
+ if isinstance(value, dict) and value.get("task") == current_task
306
+ }
221
307
  decision = _decision(target, mode, source, current_task)
222
308
  decision["decided_at"] = now
223
309
  decisions[target] = decision
@@ -280,7 +366,7 @@ def read_runtime(args: argparse.Namespace) -> int:
280
366
 
281
367
 
282
368
  def resolve_route(args: argparse.Namespace) -> int:
283
- """按 runtime → prefs 的优先级解析 route 决策。"""
369
+ """按 runtime → prefs → auto-loop 的优先级解析 route 决策。"""
284
370
  repo_root = _repo_root()
285
371
  if repo_root is None:
286
372
  return _print({"status": "miss", "reason": "not-trellis-project"})
@@ -337,15 +423,45 @@ def resolve_route(args: argparse.Namespace) -> int:
337
423
  }
338
424
  )
339
425
 
426
+ auto_mode, auto_path, auto_reason = _auto_route_mode(repo_root, context_key, args.target)
427
+ if auto_mode in PREF_MODES[args.target]:
428
+ written_path, auto_decision = _write_runtime_decision(
429
+ repo_root,
430
+ context_key,
431
+ current_task,
432
+ args.target,
433
+ auto_mode,
434
+ "auto-loop",
435
+ )
436
+ return _output(
437
+ args,
438
+ {
439
+ "status": "hit",
440
+ "origin": "auto-loop",
441
+ **_decision_summary(auto_decision),
442
+ },
443
+ {
444
+ "decision": auto_decision,
445
+ "path": _rel_path(repo_root, written_path),
446
+ "auto_path": _rel_path(repo_root, auto_path) if auto_path else None,
447
+ "pref_path": _rel_path(repo_root, _pref_path(repo_root)),
448
+ "context_key": context_key,
449
+ "task": current_task,
450
+ "wrote_runtime": True,
451
+ }
452
+ )
453
+
340
454
  return _output(
341
455
  args,
342
456
  {
343
457
  "status": "miss",
344
- "reason": "no-valid-decision-or-pref",
458
+ "reason": "no-valid-decision-pref-or-auto",
345
459
  },
346
460
  {
347
461
  "path": _rel_path(repo_root, path),
348
462
  "pref_path": _rel_path(repo_root, _pref_path(repo_root)),
463
+ "auto_reason": auto_reason,
464
+ "auto_path": _rel_path(repo_root, auto_path) if auto_path else None,
349
465
  "context_key": context_key,
350
466
  "task": current_task,
351
467
  }
@@ -0,0 +1,133 @@
1
+ ---
2
+ name: trellis-auto-loop
3
+ description: "启动、恢复和推进 Trellis 自动任务循环。用于用户明确要求 auto loop、自动跑任务、/goal 类似流程、一次跑多个任务、继续自动 run、查看/停止 auto-loop,或压缩恢复后需要从 .trellis/scripts/auto_loop.py 读取下一步。"
4
+ ---
5
+
6
+ # Trellis Auto Loop
7
+
8
+ 用 `.trellis/scripts/auto_loop.py` 驱动一个接近 `/goal` 的任务循环。Python runner 是状态权威;本 skill 只负责把用户意图映射到 runner 命令,并按 runner 返回的 action 调用现有 Trellis workflow / skill / subagent。
9
+
10
+ ## 核心规则
11
+
12
+ - 只有用户明确要求自动跑、auto loop、goal-like、继续自动 run、批量任务队列时才启动或恢复;不要把普通实现请求自动升级为 auto-loop。
13
+ - 每次开始、恢复、压缩后继续时,先运行 runner 的 `resume` 或 `next`,不要凭聊天摘要推断下一步。
14
+ - 每完成一个 action,必须用 `record --action <next 返回的 action>` 精确写回结果;runner 会拒绝缺失或不匹配的 action。写回后立即再调用 `next`,直到 `done`、`blocked` 或需要用户决策。
15
+ - 默认 profile 是 `commit-only`:自动推进到本地 commit,不 push、不发布、不归档。
16
+ - 多任务只按用户显式给出的任务顺序执行;同一 worktree 不并发。
17
+ - 启动 runner 前先完成 route 准备度判断:已有当前任务 runtime route 决策或个人 `.trellis/.route-prefs.tmp` 时可启动;没有时先进入 `trellis-route` 正常询问 / fallback,写入真实决策后再启动。
18
+ - auto-loop 不默认写 `route_authorization`;如果未来显式传 route 参数,只能表示用户本次明确给出的临时策略,不能当成模型真实执行结果。
19
+ - 代码提交必须走 `trellis-push` 的 commit-only 语义;不要裸 `git commit` / `git push`。
20
+
21
+ ## 启动
22
+
23
+ 用户给了任务列表时,按原顺序传入;用户只说当前任务时,用 `task.py current --source` 的当前任务。
24
+
25
+ 启动前对当前任务执行 route 准备度检查:
26
+
27
+ ```bash
28
+ python3 .agents/skills/trellis-route/scripts/route_state.py resolve --target implement
29
+ python3 .agents/skills/trellis-route/scripts/route_state.py resolve --target check
30
+ ```
31
+
32
+ 如果任一 target 返回 `status=miss`,先按 `trellis-route` 的正常 numbered fallback 询问用户并写入 runtime;不要替用户默认 inline 或 subagent。route 准备完成后启动 runner:
33
+
34
+ ```bash
35
+ python3 ./.trellis/scripts/auto_loop.py start \
36
+ --tasks <task> [<task> ...] \
37
+ --profile commit-only
38
+ ```
39
+
40
+ 多任务队列中,当前任务切换到下一个任务且缺少该任务 route 决策时,回到 `trellis-route` 获取该任务真实选择,再继续 `next` / `record`。个人 `.trellis/.route-prefs.tmp` 会由 `trellis-route` 统一复用并写回 runtime。
41
+
42
+ 启动后立即运行:
43
+
44
+ ```bash
45
+ python3 ./.trellis/scripts/auto_loop.py next
46
+ ```
47
+
48
+ ## 恢复
49
+
50
+ 压缩、重开会话、用户说“继续自动跑 / continue auto loop”时:
51
+
52
+ ```bash
53
+ python3 ./.trellis/scripts/auto_loop.py resume
54
+ python3 ./.trellis/scripts/auto_loop.py next
55
+ ```
56
+
57
+ `resume_capsule` 只用于展示;下一步以 `next` 返回的 JSON 为准。
58
+
59
+ ## Action 映射
60
+
61
+ | runner action | 主 agent 动作 | 成功 record |
62
+ | --- | --- | --- |
63
+ | `refresh_brief` | 使用 `trellis-task-brief` 生成并展示 brief | `record --action refresh_brief --result ok` |
64
+ | `start_task` | 执行返回的 `task.py start ...` 命令 | `record --action start_task --result ok` |
65
+ | `run_implement` | 进入 Phase 2.1,先用 `trellis-route(target=implement)` 决定 inline/subagent,再实现 | `record --action run_implement --result ok --route-mode <mode> --route-source <source>` |
66
+ | `run_check_all` | 进入 Phase 2.2,先用 `trellis-route(target=check)`,执行 check-all | `record --action run_check_all --result ok --route-mode <mode> --route-source <source>` |
67
+ | `run_fix` | 根据 `last_failure` 修复,复用当前任务 implement route | `record --action run_fix --result ok --route-mode <mode> --route-source <source>` |
68
+ | `run_recheck` | 复用当前任务 check route,重新 check-all | `record --action run_recheck --result ok --route-mode <mode> --route-source <source>` |
69
+ | `run_spec_update` | 有代码/测试证据时用 `trellis-update-spec`;无必要更新也 record ok | `record --action run_spec_update --result ok` |
70
+ | `commit_only` | 进入 `trellis-push` commit-only 语义:先由 AI 基于 task artifacts 与 git diff 生成提交计划,只提交当前任务可归属文件 | 执行成功后 `record --action commit_only --result ok --commit <hash>` |
71
+
72
+ 失败时写回:
73
+
74
+ ```bash
75
+ python3 ./.trellis/scripts/auto_loop.py record \
76
+ --action <action> \
77
+ --result failed \
78
+ --failure-type <type> \
79
+ --summary "<失败摘要>" \
80
+ --files <file> [<file> ...]
81
+ ```
82
+
83
+ 需要用户产品决策或越权时写回 blocked:
84
+
85
+ ```bash
86
+ python3 ./.trellis/scripts/auto_loop.py record \
87
+ --action <action> \
88
+ --result blocked \
89
+ --failure-type <type> \
90
+ --summary "<阻塞原因>"
91
+ ```
92
+
93
+ runner 会按 3 轮 fix/recheck 预算决定继续、跳过当前任务或结束队列。
94
+
95
+ route action 成功回写时必须带上 `trellis-route` 输出里的真实 `mode` / `source`,例如
96
+ `--route-mode inline --route-source route-prefs` 或
97
+ `--route-mode check-all-subagent --route-source trellis-route`;不要写 auto-loop 默认值。
98
+
99
+ ## Commit-Only 预授权
100
+
101
+ auto-loop 的 `commit-only` profile 是用户对“当前 run 内任务相关本地提交”的一次性预授权。只有同时满足这些条件时,`trellis-push` 可以跳过二次聊天确认:
102
+
103
+ - `.trellis/scripts/auto_loop.py status` 显示当前 `run_status=running`,且 `outstanding_action.action=commit_only`、`outstanding_action.task` 等于当前活动任务。
104
+ - profile 是 `commit-only`。
105
+ - 模式是 commit-only,不 push、不 merge、不发布、不归档。
106
+ - AI 已基于当前任务 artifacts、`git status`、`git diff` 和必要的文件内容生成提交计划,并能说明每个 planned file 为什么属于当前任务。
107
+ - 提交计划只包含当前任务可归属文件;未识别 dirty 文件保留未提交并写入结果摘要。
108
+ - 执行前复核 git 状态仍与计划一致。
109
+
110
+ 如果计划包含未识别 staged 文件、冲突、push/merge/release/archive、真实外部系统或生产数据效果,停止并把当前任务记为 blocked。
111
+
112
+ 不要用脚本基于时间差或 dirty baseline 猜测文件归属;语义归属由 AI 负责判断,`trellis-push` 负责展示/复核计划、精确暂存 planned files、执行本地 commit,并把 commit hash 回写 runner。预检不安全时只把当前队列项记为 blocked/skipped,并让 auto-loop 可继续后续任务。
113
+
114
+ ## 状态与停止
115
+
116
+ 查看:
117
+
118
+ ```bash
119
+ python3 ./.trellis/scripts/auto_loop.py status
120
+ ```
121
+
122
+ 停止:
123
+
124
+ ```bash
125
+ python3 ./.trellis/scripts/auto_loop.py stop --reason "<原因>"
126
+ ```
127
+
128
+ ## 不要做
129
+
130
+ - 不要手写或手改 `.trellis/.runtime/auto-loop/*.json`。
131
+ - 不要把 `.trellis/.runtime/` 或 `.trellis/.route-prefs.tmp` 加入提交。
132
+ - 不要在 auto-loop 外把普通 check 的 post-check stop gate 当成可跳过。
133
+ - 不要为模糊需求自动创建任务并开跑;先回到 Trellis planning。