flower-trellis 0.3.0-beta.0 → 0.3.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.
@@ -20,6 +20,8 @@ description: |
20
20
 
21
21
  个人 route 配置只决定“已获准执行后的模式”,不是开工授权。读取 `.trellis/.route-prefs.tmp` 前,必须确认当前 workflow 已允许进入对应 target:implement 需要任务已完成规划确认并处于 `in_progress`;check 需要已有本轮实现变更或用户明确要求检查。如果仍在 planning、等待用户确认,或用户表达“等一下 / 我再想想”,停止,不读取个人配置。
22
22
 
23
+ Codex inline mode 只表示主会话默认直接执行,不是 route 选项过滤器。即使当前上下文出现 `<codex-mode>inline...do not dispatch...</codex-mode>` 或 `workflow-state:in_progress-inline`,也不能推断“只能 inline”或跳过 subagent 选项;仍必须读取 `.trellis/.route-prefs.tmp`,或在无有效配置时展示正常 inline/subagent 选项。若本 skill 的紧邻路由决定是 subagent,本步骤允许主 agent dispatch 对应 implement/check sub-agent;禁止的是绕过 `trellis-route` 直接 dispatch。
24
+
23
25
  先判断本次路由目标:
24
26
 
25
27
  - `target=implement`:决定 `inline` / `subagent`。
@@ -239,6 +241,7 @@ OLD_CHECK=$(awk -F= '$1=="check"{print $2}' "$PREF_FILE" 2>/dev/null | tail -n 1
239
241
  5. **轻量 check 是隐藏逃生口**:只有用户明确请求 `light check` / `轻量检查` 时才可走轻量 `trellis-check`。
240
242
  6. **决策与执行分离**:本 skill 只输出指令,下一轮由主 agent 调工具。
241
243
  7. **严格执行用户选择**:路由结论一旦输出,主 agent 必须按指令执行,不可“出于谨慎”再换路径。
244
+ 8. **Codex inline 不裁剪选项**:Codex inline 是默认执行模式,不是只能 inline 的强制模式;route 明确选中 subagent 时,本步骤可按 subagent 路径执行。
242
245
 
243
246
  ---
244
247
 
@@ -252,6 +255,7 @@ OLD_CHECK=$(awk -F= '$1=="check"{print $2}' "$PREF_FILE" 2>/dev/null | tail -n 1
252
255
  - `AskUserQuestion` / `request_user_input` 不可用时,记录为 inline 或 subagent 路径并继续。
253
256
  - 给 check 任何模式附加“跳过编译”指令。
254
257
  - 询问后忽视用户答案默认 subagent。
258
+ - 因 `<codex-mode>` 或 `in_progress-inline` 提到 inline,就自行把无配置 route 结果改成 inline 或隐藏 subagent 选项。
255
259
 
256
260
  ---
257
261
 
@@ -20,6 +20,8 @@ description: |
20
20
 
21
21
  个人 route 配置只决定“已获准执行后的模式”,不是开工授权。读取 `.trellis/.route-prefs.tmp` 前,必须确认当前 workflow 已允许进入对应 target:implement 需要任务已完成规划确认并处于 `in_progress`;check 需要已有本轮实现变更或用户明确要求检查。如果仍在 planning、等待用户确认,或用户表达“等一下 / 我再想想”,停止,不读取个人配置。
22
22
 
23
+ Codex inline mode 只表示主会话默认直接执行,不是 route 选项过滤器。即使当前上下文出现 `<codex-mode>inline...do not dispatch...</codex-mode>` 或 `workflow-state:in_progress-inline`,也不能推断“只能 inline”或跳过 subagent 选项;仍必须读取 `.trellis/.route-prefs.tmp`,或在无有效配置时展示正常 inline/subagent 选项。若本 skill 的紧邻路由决定是 subagent,本步骤允许主 agent dispatch 对应 implement/check sub-agent;禁止的是绕过 `trellis-route` 直接 dispatch。
24
+
23
25
  先判断本次路由目标:
24
26
 
25
27
  - `target=implement`:决定 `inline` / `subagent`。
@@ -239,6 +241,7 @@ OLD_CHECK=$(awk -F= '$1=="check"{print $2}' "$PREF_FILE" 2>/dev/null | tail -n 1
239
241
  5. **轻量 check 是隐藏逃生口**:只有用户明确请求 `light check` / `轻量检查` 时才可走轻量 `trellis-check`。
240
242
  6. **决策与执行分离**:本 skill 只输出指令,下一轮由主 agent 调工具。
241
243
  7. **严格执行用户选择**:路由结论一旦输出,主 agent 必须按指令执行,不可“出于谨慎”再换路径。
244
+ 8. **Codex inline 不裁剪选项**:Codex inline 是默认执行模式,不是只能 inline 的强制模式;route 明确选中 subagent 时,本步骤可按 subagent 路径执行。
242
245
 
243
246
  ---
244
247
 
@@ -252,6 +255,7 @@ OLD_CHECK=$(awk -F= '$1=="check"{print $2}' "$PREF_FILE" 2>/dev/null | tail -n 1
252
255
  - `AskUserQuestion` / `request_user_input` 不可用时,记录为 inline 或 subagent 路径并继续。
253
256
  - 给 check 任何模式附加“跳过编译”指令。
254
257
  - 询问后忽视用户答案默认 subagent。
258
+ - 因 `<codex-mode>` 或 `in_progress-inline` 提到 inline,就自行把无配置 route 结果改成 inline 或隐藏 subagent 选项。
255
259
 
256
260
  ---
257
261
 
@@ -1,6 +1,6 @@
1
1
  <!-- BEGIN skill-garden workflow-state in_progress_inline v0.6 -->
2
2
  HIGHEST PRIORITY SKILL-GARDEN STATE GUARD (in_progress-inline):
3
- Inline mode does not skip `trellis-route`: before implementation, route `implement` first; before checking, route `check` first. Only after the route selects inline should the main session run `trellis-before-dev`, edit code, or run `trellis-check` / `trellis-check-all`.
3
+ Inline mode does not skip or constrain `trellis-route`: before implementation, route `implement` first; before checking, route `check` first. The default inline flow is direct main-session work, but if the immediate route decision selects subagent, that decision permits dispatch for this step.
4
4
  After `trellis-check` / `trellis-check-all`, stop and report; do not run `/trellis:finish-work` unless the user explicitly asks after Phase 3.4 is complete.
5
5
  This guard overrides any lower `Flow: ... -> /trellis:finish-work` line in this state block.
6
6
  At Phase 3.4, code commit/push still goes through `trellis-push` (commit-only for commit-without-push); never bare `git commit`/`git push` on code (hub: Code Commit Confirmation Gate).
@@ -1,7 +1,7 @@
1
1
  {
2
- "syncedAt": "2026-06-16T07:52:49.765Z",
2
+ "syncedAt": "2026-06-16T11:04:55.690Z",
3
3
  "syncedFrom": "vendor/skill-garden/.trellis",
4
- "sourceCommit": "d05cb9de53e0ca04f854a5471d24f4df542f0c0f",
4
+ "sourceCommit": "987263bc8fce1e6057ba5624a9f8d1c8d4d40fe4",
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.0-beta.0",
3
+ "version": "0.3.0-beta.1",
4
4
  "description": "一键安装/升级 Trellis 并自动融合 skill-garden 强化包(默认 Claude + agents)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -129,13 +129,16 @@ export function applyEnhancements(target, opts = {}) {
129
129
  }
130
130
  }
131
131
 
132
- // codex 平台后处理:旧 multi_agent_v2 兼容清理 + 合并 SessionStart hook(仅当 .codex/ 存在)
132
+ // codex 平台后处理:旧 multi_agent_v2 兼容清理 + 合并 SessionStart hook + 强制 sub-agent 调度
133
133
  const codex = applyCodexTweaks(target);
134
134
  if (codex.applied) {
135
135
  const seg = codex.tomlChanged
136
136
  ? "config.toml 已清理旧 multi_agent_v2"
137
137
  : "config.toml 无需清理 multi_agent_v2";
138
- console.log(` ✓ codex 调整:${seg};hooks.json 已合并 SessionStart`);
138
+ const dispatch = codex.dispatchModeChanged
139
+ ? "dispatch_mode 已强制为 sub-agent"
140
+ : "dispatch_mode 已是 sub-agent";
141
+ console.log(` ✓ codex 调整:${seg};hooks.json 已合并 SessionStart;${dispatch}`);
139
142
  }
140
143
 
141
144
  return { variant, installed };
@@ -5,13 +5,153 @@ import path from "node:path";
5
5
  * flower-trellis 对 Trellis 生成的 codex 配置的定制后处理。
6
6
  *
7
7
  * 仅当目标项目已配置 codex 平台(存在 .codex/)时生效;在 init / update 叠加阶段调用,幂等。
8
- * 做两件事:
8
+ * 做三件事:
9
9
  * 1. 兼容旧 Trellis:注释掉 .codex/config.toml 的 [features.multi_agent_v2] 段;
10
10
  * 2. 合并 .codex/hooks.json —— 保留 Trellis 上游 hook 设置,只补 flower 需要的 SessionStart。
11
+ * 3. 强制 .trellis/config.yaml 的 codex.dispatch_mode 为 sub-agent。
11
12
  */
12
13
 
13
14
  const WORKFLOW_HOOK_SCRIPT = ".codex/hooks/inject-workflow-state.py";
14
15
  const SESSION_START_SCRIPT = ".codex/hooks/session-start.py";
16
+ const CODEX_DISPATCH_MODE = "sub-agent";
17
+
18
+ /** 返回一行开头的空白缩进。 */
19
+ function leadingWhitespace(line) {
20
+ return line.match(/^\s*/)?.[0] || "";
21
+ }
22
+
23
+ /** 判断一行是否是未注释的顶层 YAML key。 */
24
+ function isTopLevelKey(line) {
25
+ const trimmed = line.trim();
26
+ return Boolean(trimmed && !trimmed.startsWith("#") && !leadingWhitespace(line) && /^[^:#]+:/.test(trimmed));
27
+ }
28
+
29
+ /** 找到未注释的顶层 `codex:` 块行号。 */
30
+ function findCodexBlockStart(lines) {
31
+ return lines.findIndex((line) => {
32
+ const trimmed = line.trim();
33
+ return isTopLevelKey(line) && /^codex\s*:/.test(trimmed);
34
+ });
35
+ }
36
+
37
+ /** 找到 YAML 顶层块结束位置,注释和空行不结束当前块。 */
38
+ function findTopLevelBlockEnd(lines, start) {
39
+ for (let i = start + 1; i < lines.length; i += 1) {
40
+ if (isTopLevelKey(lines[i])) return i;
41
+ }
42
+ return lines.length;
43
+ }
44
+
45
+ /** 按逗号拆分 YAML inline map 内容,保留引号内逗号。 */
46
+ function splitInlineMapItems(content) {
47
+ const items = [];
48
+ let quote = "";
49
+ let token = "";
50
+ for (const ch of content) {
51
+ if (quote) {
52
+ token += ch;
53
+ if (ch === quote) quote = "";
54
+ continue;
55
+ }
56
+ if (ch === "\"" || ch === "'") {
57
+ quote = ch;
58
+ token += ch;
59
+ continue;
60
+ }
61
+ if (ch === ",") {
62
+ if (token.trim()) items.push(token.trim());
63
+ token = "";
64
+ continue;
65
+ }
66
+ token += ch;
67
+ }
68
+ if (token.trim()) items.push(token.trim());
69
+ return items;
70
+ }
71
+
72
+ /** 解析简单 YAML inline map,失败时返回 null。 */
73
+ function parseInlineMapEntries(value) {
74
+ if (!value.startsWith("{") || !value.endsWith("}")) return null;
75
+ const body = value.slice(1, -1).trim();
76
+ if (!body) return [];
77
+ const entries = [];
78
+ for (const item of splitInlineMapItems(body)) {
79
+ const [key, ...rest] = item.split(":");
80
+ if (!key || rest.length === 0) return null;
81
+ entries.push({ key: key.trim(), value: rest.join(":").trim() });
82
+ }
83
+ return entries;
84
+ }
85
+
86
+ /**
87
+ * 强制目标项目 `.trellis/config.yaml` 使用 Codex sub-agent 调度。
88
+ *
89
+ * Trellis 默认缺失该字段时按 inline 注入 `<codex-mode>`,会让 route 误判 subagent
90
+ * 不可执行。flower 在 Codex 目标上直接写真实配置,避免依赖注释示例或模型推断。
91
+ *
92
+ * @param {string} configPath 目标项目 `.trellis/config.yaml` 路径
93
+ * @returns {boolean} 是否写入
94
+ */
95
+ function forceCodexDispatchMode(configPath) {
96
+ const current = fs.existsSync(configPath) ? fs.readFileSync(configPath, "utf8") : "";
97
+ const normalized = current.replace(/\r\n/g, "\n");
98
+ const hadFinalNewline = normalized.endsWith("\n");
99
+ const lines = normalized.length === 0
100
+ ? []
101
+ : normalized.replace(/\n$/, "").split("\n");
102
+
103
+ const codexStart = findCodexBlockStart(lines);
104
+ if (codexStart === -1) {
105
+ if (lines.length > 0 && lines[lines.length - 1].trim()) lines.push("");
106
+ lines.push("codex:", ` dispatch_mode: ${CODEX_DISPATCH_MODE}`);
107
+ } else {
108
+ const blockEnd = findTopLevelBlockEnd(lines, codexStart);
109
+ const codexMatch = lines[codexStart].match(/^(\s*)codex\s*:\s*(.*)$/);
110
+ const codexIndent = codexMatch?.[1] || "";
111
+ const codexValue = (codexMatch?.[2] || "").trim();
112
+ const inlineEntries = parseInlineMapEntries(codexValue);
113
+ if (inlineEntries) {
114
+ const nextLines = [`${codexIndent}codex:`];
115
+ let hasDispatchMode = false;
116
+ for (const entry of inlineEntries) {
117
+ if (entry.key === "dispatch_mode") {
118
+ hasDispatchMode = true;
119
+ nextLines.push(`${codexIndent} dispatch_mode: ${CODEX_DISPATCH_MODE}`);
120
+ } else {
121
+ nextLines.push(`${codexIndent} ${entry.key}: ${entry.value}`);
122
+ }
123
+ }
124
+ if (!hasDispatchMode) {
125
+ nextLines.splice(1, 0, `${codexIndent} dispatch_mode: ${CODEX_DISPATCH_MODE}`);
126
+ }
127
+ lines.splice(codexStart, 1, ...nextLines);
128
+ } else if (codexValue && !codexValue.startsWith("#")) {
129
+ lines[codexStart] = `${codexIndent}codex:`;
130
+ }
131
+
132
+ const nextBlockEnd = inlineEntries
133
+ ? findTopLevelBlockEnd(lines, codexStart)
134
+ : blockEnd;
135
+ const dispatchIndex = lines.findIndex((line, index) => {
136
+ if (index <= codexStart || index >= nextBlockEnd) return false;
137
+ const trimmed = line.trim();
138
+ return Boolean(trimmed && !trimmed.startsWith("#") && /^dispatch_mode\s*:/.test(trimmed));
139
+ });
140
+
141
+ if (dispatchIndex === -1) {
142
+ lines.splice(codexStart + 1, 0, `${codexIndent} dispatch_mode: ${CODEX_DISPATCH_MODE}`);
143
+ } else {
144
+ const indent = leadingWhitespace(lines[dispatchIndex]);
145
+ lines[dispatchIndex] = `${indent}dispatch_mode: ${CODEX_DISPATCH_MODE}`;
146
+ }
147
+ }
148
+
149
+ const desired = lines.join("\n") + (hadFinalNewline || lines.length > 0 ? "\n" : "");
150
+ if (current === desired) return false;
151
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
152
+ fs.writeFileSync(configPath, desired);
153
+ return true;
154
+ }
15
155
 
16
156
  /**
17
157
  * 注释掉 config.toml 里的 [features.multi_agent_v2] 段(段头 + 段内键,直到下一个 section)。
@@ -141,12 +281,13 @@ function mergeHooks(hooksPath) {
141
281
  /**
142
282
  * codex 平台后处理入口。仅当 .codex/ 存在时执行。
143
283
  * @param {string} target 目标项目根
144
- * @returns {{applied: boolean, tomlChanged?: boolean, hooksWritten?: boolean}}
284
+ * @returns {{applied: boolean, tomlChanged?: boolean, hooksWritten?: boolean, dispatchModeChanged?: boolean}}
145
285
  */
146
286
  export function applyCodexTweaks(target) {
147
287
  const codexDir = path.join(target, ".codex");
148
288
  if (!fs.existsSync(codexDir)) return { applied: false };
149
289
  const tomlChanged = commentMultiAgentV2(path.join(codexDir, "config.toml"));
150
290
  const hooksWritten = mergeHooks(path.join(codexDir, "hooks.json"));
151
- return { applied: true, tomlChanged, hooksWritten };
291
+ const dispatchModeChanged = forceCodexDispatchMode(path.join(target, ".trellis", "config.yaml"));
292
+ return { applied: true, tomlChanged, hooksWritten, dispatchModeChanged };
152
293
  }