@ibotor/smart-trellis 0.5.19 → 0.5.20
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 +11 -0
- package/dist/cli/smart.js +2 -1
- package/dist/cli/smart.js.map +1 -1
- package/dist/commands/smart-init.d.ts +4 -0
- package/dist/commands/smart-init.d.ts.map +1 -1
- package/dist/commands/smart-init.js +84 -0
- package/dist/commands/smart-init.js.map +1 -1
- package/dist/migrations/manifests/0.5.20.json +16 -0
- package/dist/migrations/manifests/0.6.0-beta.22.json +9 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-workflow.md +12 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/workflow.md +3 -2
- package/dist/templates/opencode/plugins/inject-workflow-state.js +77 -3
- package/dist/templates/pi/extensions/trellis/index.ts.txt +72 -3
- package/dist/templates/shared-hooks/inject-workflow-state.py +31 -2
- package/dist/templates/trellis/config.yaml +15 -0
- package/dist/templates/trellis/workflow.md +18 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -68,6 +68,11 @@ smart-trellis init
|
|
|
68
68
|
smart-trellis init --yes
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
+
不加 `--yes` 时,初始化过程会提示你选择 AI 工具,并选择 Trellis activation mode:
|
|
72
|
+
|
|
73
|
+
- `auto`:默认行为,Trellis 可以为开发/重构需求自动启动任务流程。
|
|
74
|
+
- `explicit`:Trellis 只在你明确使用 `/trellis start` / `$trellis-start` / “走 Trellis” 时启动。
|
|
75
|
+
|
|
71
76
|
如果你希望直接指定工具:
|
|
72
77
|
|
|
73
78
|
```bash
|
|
@@ -80,6 +85,12 @@ smart-trellis init --tools codex,cursor
|
|
|
80
85
|
smart-trellis init --tools codex,cursor --user adong
|
|
81
86
|
```
|
|
82
87
|
|
|
88
|
+
如果你希望 Trellis 不主动接管普通开发需求,而是必须显式使用 `/trellis start` / `$trellis-start` / “走 Trellis” 才启动:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
smart-trellis init --tools codex,cursor --trellis-activation explicit
|
|
92
|
+
```
|
|
93
|
+
|
|
83
94
|
上面的命令大致等价于:
|
|
84
95
|
|
|
85
96
|
```bash
|
package/dist/cli/smart.js
CHANGED
|
@@ -21,7 +21,8 @@ const initCommand = program
|
|
|
21
21
|
.option("-t, --template <name>", "Use a remote spec template (e.g., electron-fullstack)")
|
|
22
22
|
.option("--overwrite", "Overwrite existing spec directory when using template")
|
|
23
23
|
.option("--append", "Only add missing files when using template")
|
|
24
|
-
.option("-r, --registry <source>", "Use a custom template registry (e.g., gh:myorg/myrepo/specs)")
|
|
24
|
+
.option("-r, --registry <source>", "Use a custom template registry (e.g., gh:myorg/myrepo/specs)")
|
|
25
|
+
.option("--trellis-activation <mode>", "Set no-task Trellis activation mode: auto or explicit");
|
|
25
26
|
for (const tool of getInitToolChoices()) {
|
|
26
27
|
initCommand.option(`--${tool.key}`, `Include ${tool.name}`);
|
|
27
28
|
}
|
package/dist/cli/smart.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"smart.js","sourceRoot":"","sources":["../../src/cli/smart.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAyB,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAElD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,eAAe,CAAC;KACrB,WAAW,CAAC,wCAAwC,CAAC;KACrD,OAAO,CAAC,OAAO,EAAE,eAAe,EAAE,2BAA2B,CAAC,CAAC;AAElE,MAAM,WAAW,GAAG,OAAO;KACxB,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,gEAAgE,CAAC;KAC7E,MAAM,CAAC,iBAAiB,EAAE,6CAA6C,CAAC;KACxE,MAAM,CAAC,WAAW,EAAE,6CAA6C,CAAC;KAClE,MAAM,CACL,mBAAmB,EACnB,mDAAmD,CACpD;KACA,MAAM,CAAC,aAAa,EAAE,yCAAyC,CAAC;KAChE,MAAM,CAAC,qBAAqB,EAAE,oCAAoC,CAAC;KACnE,MAAM,CAAC,YAAY,EAAE,qBAAqB,CAAC;KAC3C,MAAM,CAAC,eAAe,EAAE,yBAAyB,CAAC;KAClD,MAAM,CACL,uBAAuB,EACvB,uDAAuD,CACxD;KACA,MAAM,CACL,aAAa,EACb,uDAAuD,CACxD;KACA,MAAM,CAAC,UAAU,EAAE,4CAA4C,CAAC;KAChE,MAAM,CACL,yBAAyB,EACzB,8DAA8D,CAC/D,CAAC;AAEJ,KAAK,MAAM,IAAI,IAAI,kBAAkB,EAAE,EAAE,CAAC;IACxC,WAAW,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,GAAG,EAAE,EAAE,WAAW,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED,WAAW,CAAC,MAAM,CAAC,KAAK,EAAE,OAAyB,EAAE,EAAE;IACrD,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CACX,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,EACnB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAC/C,CAAC;QACF,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;YACnD,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC9D,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
|
1
|
+
{"version":3,"file":"smart.js","sourceRoot":"","sources":["../../src/cli/smart.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAyB,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAElD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,eAAe,CAAC;KACrB,WAAW,CAAC,wCAAwC,CAAC;KACrD,OAAO,CAAC,OAAO,EAAE,eAAe,EAAE,2BAA2B,CAAC,CAAC;AAElE,MAAM,WAAW,GAAG,OAAO;KACxB,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,gEAAgE,CAAC;KAC7E,MAAM,CAAC,iBAAiB,EAAE,6CAA6C,CAAC;KACxE,MAAM,CAAC,WAAW,EAAE,6CAA6C,CAAC;KAClE,MAAM,CACL,mBAAmB,EACnB,mDAAmD,CACpD;KACA,MAAM,CAAC,aAAa,EAAE,yCAAyC,CAAC;KAChE,MAAM,CAAC,qBAAqB,EAAE,oCAAoC,CAAC;KACnE,MAAM,CAAC,YAAY,EAAE,qBAAqB,CAAC;KAC3C,MAAM,CAAC,eAAe,EAAE,yBAAyB,CAAC;KAClD,MAAM,CACL,uBAAuB,EACvB,uDAAuD,CACxD;KACA,MAAM,CACL,aAAa,EACb,uDAAuD,CACxD;KACA,MAAM,CAAC,UAAU,EAAE,4CAA4C,CAAC;KAChE,MAAM,CACL,yBAAyB,EACzB,8DAA8D,CAC/D;KACA,MAAM,CACL,6BAA6B,EAC7B,uDAAuD,CACxD,CAAC;AAEJ,KAAK,MAAM,IAAI,IAAI,kBAAkB,EAAE,EAAE,CAAC;IACxC,WAAW,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,GAAG,EAAE,EAAE,WAAW,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED,WAAW,CAAC,MAAM,CAAC,KAAK,EAAE,OAAyB,EAAE,EAAE;IACrD,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CACX,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,EACnB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAC/C,CAAC;QACF,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;YACnD,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC9D,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type InitOptions } from "./init.js";
|
|
2
2
|
import type { CliFlag } from "../types/ai-tools.js";
|
|
3
3
|
type SmartToolFlagOptions = Partial<Record<CliFlag, boolean>>;
|
|
4
|
+
export type TrellisActivationMode = "auto" | "explicit";
|
|
4
5
|
export type SmartInitOptions = SmartToolFlagOptions & {
|
|
5
6
|
append?: boolean;
|
|
6
7
|
force?: boolean;
|
|
@@ -9,12 +10,15 @@ export type SmartInitOptions = SmartToolFlagOptions & {
|
|
|
9
10
|
registry?: string;
|
|
10
11
|
skipExisting?: boolean;
|
|
11
12
|
template?: string;
|
|
13
|
+
trellisActivation?: string;
|
|
12
14
|
tools?: string;
|
|
13
15
|
user?: string;
|
|
14
16
|
yes?: boolean;
|
|
15
17
|
};
|
|
16
18
|
export declare const SMART_DEFAULT_TOOLS: CliFlag[];
|
|
17
19
|
export declare function parseSmartToolsOption(value: string): CliFlag[];
|
|
20
|
+
export declare function parseSmartTrellisActivationMode(value: string | undefined): TrellisActivationMode | undefined;
|
|
21
|
+
export declare function patchTrellisActivationConfig(content: string, mode: TrellisActivationMode): string;
|
|
18
22
|
export declare function createSmartInitOptions(options: SmartInitOptions, tools: CliFlag[], detectedUser?: string): InitOptions;
|
|
19
23
|
export declare function detectDeveloperName(cwd?: string): string | undefined;
|
|
20
24
|
export declare function smartInit(options: SmartInitOptions): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"smart-init.d.ts","sourceRoot":"","sources":["../../src/commands/smart-init.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"smart-init.d.ts","sourceRoot":"","sources":["../../src/commands/smart-init.ts"],"names":[],"mappings":"AAMA,OAAO,EAAQ,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAEpD,KAAK,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AAC9D,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,UAAU,CAAC;AAExD,MAAM,MAAM,gBAAgB,GAAG,oBAAoB,GAAG;IACpD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,OAAO,CAAC;CACf,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,OAAO,EAAwB,CAAC;AAgBlE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,EAAE,CAkB9D;AAED,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,qBAAqB,GAAG,SAAS,CAWnC;AAED,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,qBAAqB,GAC1B,MAAM,CAwCR;AAmDD,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAE,OAAO,EAAE,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,WAAW,CA6Bb;AAED,wBAAgB,mBAAmB,CACjC,GAAG,GAAE,MAAsB,GAC1B,MAAM,GAAG,SAAS,CAoBpB;AAuED,wBAAsB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAWxE"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { execSync } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
2
3
|
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
3
5
|
import inquirer from "inquirer";
|
|
4
6
|
import { getInitToolChoices } from "../configurators/index.js";
|
|
5
7
|
import { init } from "./init.js";
|
|
@@ -27,6 +29,59 @@ export function parseSmartToolsOption(value) {
|
|
|
27
29
|
}
|
|
28
30
|
return uniqueTools;
|
|
29
31
|
}
|
|
32
|
+
export function parseSmartTrellisActivationMode(value) {
|
|
33
|
+
if (value === undefined) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
const normalized = value.trim().toLowerCase();
|
|
37
|
+
if (normalized === "auto" || normalized === "explicit") {
|
|
38
|
+
return normalized;
|
|
39
|
+
}
|
|
40
|
+
throw new Error(`Unknown Trellis activation mode "${value}". Supported modes: auto, explicit`);
|
|
41
|
+
}
|
|
42
|
+
export function patchTrellisActivationConfig(content, mode) {
|
|
43
|
+
const generatedExample = /# trellis:\r?\n# {3}activation_mode: auto\r?\n# {3}# activation_mode: explicit/;
|
|
44
|
+
if (generatedExample.test(content)) {
|
|
45
|
+
return content.replace(generatedExample, `trellis:\n activation_mode: ${mode}`);
|
|
46
|
+
}
|
|
47
|
+
const lines = content.split("\n");
|
|
48
|
+
const trellisStart = lines.findIndex((line) => /^trellis\s*:\s*(?:#.*)?$/.test(line.trim()));
|
|
49
|
+
if (trellisStart !== -1) {
|
|
50
|
+
let trellisEnd = lines.length;
|
|
51
|
+
for (let index = trellisStart + 1; index < lines.length; index += 1) {
|
|
52
|
+
const trimmed = lines[index]?.trim() ?? "";
|
|
53
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
54
|
+
continue;
|
|
55
|
+
const indent = (lines[index] ?? "").length - (lines[index] ?? "").trimStart().length;
|
|
56
|
+
if (indent === 0) {
|
|
57
|
+
trellisEnd = index;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
for (let index = trellisStart + 1; index < trellisEnd; index += 1) {
|
|
62
|
+
const match = (lines[index] ?? "").match(/^(\s*)activation_mode\s*:/);
|
|
63
|
+
if (match) {
|
|
64
|
+
lines[index] = `${match[1]}activation_mode: ${mode}`;
|
|
65
|
+
return lines.join("\n");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
lines.splice(trellisStart + 1, 0, ` activation_mode: ${mode}`);
|
|
69
|
+
return lines.join("\n");
|
|
70
|
+
}
|
|
71
|
+
const separator = content.endsWith("\n") ? "\n" : "\n\n";
|
|
72
|
+
return `${content}${separator}trellis:\n activation_mode: ${mode}\n`;
|
|
73
|
+
}
|
|
74
|
+
function applyTrellisActivationConfig(cwd, mode) {
|
|
75
|
+
if (!mode) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const configPath = path.join(cwd, ".trellis", "config.yaml");
|
|
79
|
+
if (!fs.existsSync(configPath)) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const content = fs.readFileSync(configPath, "utf-8");
|
|
83
|
+
fs.writeFileSync(configPath, patchTrellisActivationConfig(content, mode), "utf-8");
|
|
84
|
+
}
|
|
30
85
|
function applyToolFlags(initOptions, tools) {
|
|
31
86
|
for (const tool of tools) {
|
|
32
87
|
initOptions[tool] = true;
|
|
@@ -114,9 +169,38 @@ async function resolveSmartTools(options) {
|
|
|
114
169
|
]);
|
|
115
170
|
return answers.tools;
|
|
116
171
|
}
|
|
172
|
+
async function resolveSmartTrellisActivation(options) {
|
|
173
|
+
const parsed = parseSmartTrellisActivationMode(options.trellisActivation);
|
|
174
|
+
const hasExplicitTools = !!options.tools ||
|
|
175
|
+
supportedToolFlags().some((tool) => options[tool] === true);
|
|
176
|
+
if (parsed || options.yes || hasExplicitTools) {
|
|
177
|
+
return parsed;
|
|
178
|
+
}
|
|
179
|
+
const answers = await inquirer.prompt([
|
|
180
|
+
{
|
|
181
|
+
type: "list",
|
|
182
|
+
name: "trellisActivation",
|
|
183
|
+
message: "Select Trellis activation mode:",
|
|
184
|
+
default: "auto",
|
|
185
|
+
choices: [
|
|
186
|
+
{
|
|
187
|
+
name: "auto - Trellis can start for implementation/refactor work",
|
|
188
|
+
value: "auto",
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
name: "explicit - only start when you invoke /trellis start",
|
|
192
|
+
value: "explicit",
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
},
|
|
196
|
+
]);
|
|
197
|
+
return answers.trellisActivation;
|
|
198
|
+
}
|
|
117
199
|
export async function smartInit(options) {
|
|
118
200
|
const tools = await resolveSmartTools(options);
|
|
201
|
+
const activationMode = await resolveSmartTrellisActivation(options);
|
|
119
202
|
const initOptions = createSmartInitOptions(options, tools, detectDeveloperName());
|
|
120
203
|
await init(initOptions);
|
|
204
|
+
applyTrellisActivationConfig(process.cwd(), activationMode);
|
|
121
205
|
}
|
|
122
206
|
//# sourceMappingURL=smart-init.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"smart-init.js","sourceRoot":"","sources":["../../src/commands/smart-init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,QAAQ,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAoB,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"smart-init.js","sourceRoot":"","sources":["../../src/commands/smart-init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.5.20",
|
|
3
|
+
"description": "Patch: add a Trellis activation-mode config option so teams can require explicit Trellis start commands.",
|
|
4
|
+
"breaking": false,
|
|
5
|
+
"recommendMigrate": false,
|
|
6
|
+
"changelog": "**Enhancements:**\n- feat(workflow): new `trellis.activation_mode: auto | explicit` config. `auto` preserves the current no-task behavior; `explicit` keeps Trellis idle unless the current user message explicitly invokes Trellis, such as `/trellis start`, `$trellis-start`, or `走 Trellis`.",
|
|
7
|
+
"migrations": [],
|
|
8
|
+
"configSectionsAdded": [
|
|
9
|
+
{
|
|
10
|
+
"file": ".trellis/config.yaml",
|
|
11
|
+
"sentinel": "activation_mode:",
|
|
12
|
+
"sectionHeading": "Trellis (activation behavior)"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"notes": "Patch on top of 0.5.19. Run `trellis update` to append the commented activation-mode config section to existing `.trellis/config.yaml` files. Set `trellis.activation_mode: explicit` when you want Trellis to run only after an explicit Trellis command."
|
|
16
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.6.0-beta.22",
|
|
3
|
+
"description": "Beta patch: remove the duplicated pull-based prelude from the generated Codex sub-agent toml files, and restore the 0.5.19 migration manifest that was missing on the beta branch.",
|
|
4
|
+
"breaking": false,
|
|
5
|
+
"recommendMigrate": false,
|
|
6
|
+
"changelog": "**Bug Fixes:**\n- fix(codex): the generated `.codex/agents/trellis-check.toml` and `trellis-implement.toml` no longer contain the \"Required: Load Trellis Context First\" prelude twice. The codex toml source templates carried an inline copy that predated the code-injected prelude (`injectPullBasedPreludeToml`); the injector then added a second copy. The inline copies are removed so the injector is the single source, and a regression test asserts the prelude appears exactly once across all class-2 platforms.\n- fix(migrations): restore `src/migrations/manifests/0.5.19.json`, which was missing on the 0.6.0-beta branch. Its absence broke the manifest-continuity guard and would break `trellis update` for users on 0.5.19.",
|
|
7
|
+
"migrations": [],
|
|
8
|
+
"notes": "Beta patch on top of 0.6.0-beta.21. Run `trellis update` to regenerate `.codex/agents/` without the duplicated prelude."
|
|
9
|
+
}
|
|
@@ -13,7 +13,7 @@ When the user wants to change Trellis phases, next-action hints, whether to crea
|
|
|
13
13
|
| Need | Edit point |
|
|
14
14
|
| --- | --- |
|
|
15
15
|
| Change phase names or phase order | `Phase Index` and the corresponding Phase sections. |
|
|
16
|
-
| Change whether to create a task when there is no task | `[workflow-state:no_task]` state
|
|
16
|
+
| Change whether to create a task when there is no task | Prefer `trellis.activation_mode` in `.trellis/config.yaml` for auto vs explicit start; edit `[workflow-state:no_task]` / `[workflow-state:no_task-explicit]` for custom wording. |
|
|
17
17
|
| Change the next step during planning | Phase 1 and `[workflow-state:planning]`. |
|
|
18
18
|
| Change whether an agent is required during in_progress | Phase 2 and `[workflow-state:in_progress]`. |
|
|
19
19
|
| Change wrap-up after completion | Phase 3 and `[workflow-state:completed]`. |
|
|
@@ -27,6 +27,17 @@ When the user wants to change Trellis phases, next-action hints, whether to crea
|
|
|
27
27
|
4. Workflow-state changes only need an edit to the `[workflow-state:STATUS]` block in `.trellis/workflow.md`. The hook is parser-only — it reads whatever you put in the block. Keep the opening and closing tags' STATUS strings identical (`[workflow-state:foo]…[/workflow-state:foo]`); mismatched STATUS pairs are silently dropped.
|
|
28
28
|
5. Make the AI reread `.trellis/workflow.md`; do not keep using rules from the old conversation.
|
|
29
29
|
|
|
30
|
+
## Example: Require Explicit Trellis Start
|
|
31
|
+
|
|
32
|
+
To keep Trellis from taking over ordinary development requests, set:
|
|
33
|
+
|
|
34
|
+
```yaml
|
|
35
|
+
trellis:
|
|
36
|
+
activation_mode: explicit
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
With this mode, the workflow-state hook selects `[workflow-state:no_task-explicit]` while the session has no active task. Trellis starts only when the user explicitly invokes it, such as `/trellis start`, `$trellis-start`, or "走 Trellis".
|
|
40
|
+
|
|
30
41
|
## Example: Relax Task Creation Requirements
|
|
31
42
|
|
|
32
43
|
To change when task creation can be skipped, usually edit `[workflow-state:no_task]`:
|
package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/workflow.md
CHANGED
|
@@ -44,11 +44,12 @@ Hooks choose the right block based on current task status and inject it into the
|
|
|
44
44
|
| State | Meaning |
|
|
45
45
|
| --- | --- |
|
|
46
46
|
| `no_task` | The current session has no active task. |
|
|
47
|
+
| `no_task-explicit` | Alternate no-task prompt selected by `trellis.activation_mode: explicit`; the emitted status still says `no_task`, but Trellis stays idle unless the user explicitly invokes it. |
|
|
47
48
|
| `planning` | The task is still in requirements, research, or context configuration. |
|
|
48
49
|
| `in_progress` | The task has entered implementation and checking. |
|
|
49
50
|
| `completed` | The task is complete and waiting for wrap-up or archive. |
|
|
50
51
|
|
|
51
|
-
If the user wants to
|
|
52
|
+
If the user wants Trellis to run only on explicit requests, set `trellis.activation_mode: explicit` in `.trellis/config.yaml`. If the user wants to change the actual wording or trigger policy, edit the relevant state blocks and the routing table above them.
|
|
52
53
|
|
|
53
54
|
## Local Modification Patterns
|
|
54
55
|
|
|
@@ -57,7 +58,7 @@ Common changes:
|
|
|
57
58
|
| Goal | Edit point |
|
|
58
59
|
| --- | --- |
|
|
59
60
|
| Add a phase | Update the Phase Index, phase body, routing, and state blocks. |
|
|
60
|
-
| Change task creation policy |
|
|
61
|
+
| Change task creation policy | Set `trellis.activation_mode` in `.trellis/config.yaml`, or update the `no_task` / `no_task-explicit` state blocks and Phase 1 description. |
|
|
61
62
|
| Change the default implementation/check path | Update Phase 2 and skill routing. |
|
|
62
63
|
| Change the wrap-up flow | Update Phase 3 and `finish-work` related descriptions. Note the current split: Phase 3.4 = AI-driven code commits (batched, user-confirmed), Phase 3.5 = `/finish-work` (archive + record session). `/finish-work` refuses to run if the working tree is dirty. |
|
|
63
64
|
| Change platform differences | Update routing descriptions grouped by platform. |
|
|
@@ -58,6 +58,74 @@ function loadBreadcrumbs(directory) {
|
|
|
58
58
|
return result
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
function unquoteYamlScalar(value) {
|
|
62
|
+
const trimmed = value.trim()
|
|
63
|
+
if (trimmed.length >= 2) {
|
|
64
|
+
const first = trimmed[0]
|
|
65
|
+
const last = trimmed[trimmed.length - 1]
|
|
66
|
+
if ((first === "\"" || first === "'") && first === last) {
|
|
67
|
+
return trimmed.slice(1, -1)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return trimmed
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function stripYamlInlineComment(value) {
|
|
74
|
+
let quote = null
|
|
75
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
76
|
+
const ch = value[index]
|
|
77
|
+
if (quote) {
|
|
78
|
+
if (ch === quote) quote = null
|
|
79
|
+
continue
|
|
80
|
+
}
|
|
81
|
+
if (ch === "\"" || ch === "'") {
|
|
82
|
+
quote = ch
|
|
83
|
+
continue
|
|
84
|
+
}
|
|
85
|
+
if (ch === "#" && (index === 0 || /\s/.test(value[index - 1] || ""))) {
|
|
86
|
+
return value.slice(0, index)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return value
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function readTrellisActivationMode(directory) {
|
|
93
|
+
const configPath = join(directory, ".trellis", "config.yaml")
|
|
94
|
+
if (!existsSync(configPath)) return "auto"
|
|
95
|
+
let content
|
|
96
|
+
try {
|
|
97
|
+
content = readFileSync(configPath, "utf-8")
|
|
98
|
+
} catch {
|
|
99
|
+
return "auto"
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
let inTrellis = false
|
|
103
|
+
let trellisIndent = -1
|
|
104
|
+
for (const line of content.split(/\r?\n/)) {
|
|
105
|
+
const trimmed = line.trim()
|
|
106
|
+
if (!trimmed || trimmed.startsWith("#")) continue
|
|
107
|
+
|
|
108
|
+
const indent = line.length - line.trimStart().length
|
|
109
|
+
if (inTrellis && indent <= trellisIndent) {
|
|
110
|
+
inTrellis = false
|
|
111
|
+
}
|
|
112
|
+
if (!inTrellis && indent === 0 && /^trellis\s*:\s*(?:#.*)?$/.test(trimmed)) {
|
|
113
|
+
inTrellis = true
|
|
114
|
+
trellisIndent = indent
|
|
115
|
+
continue
|
|
116
|
+
}
|
|
117
|
+
if (!inTrellis) continue
|
|
118
|
+
|
|
119
|
+
const match = trimmed.match(/^activation(?:_mode)?\s*:\s*(.*)$/)
|
|
120
|
+
if (!match) continue
|
|
121
|
+
const value = unquoteYamlScalar(stripYamlInlineComment(match[1] || ""))
|
|
122
|
+
.trim()
|
|
123
|
+
.toLowerCase()
|
|
124
|
+
return value === "explicit" ? "explicit" : "auto"
|
|
125
|
+
}
|
|
126
|
+
return "auto"
|
|
127
|
+
}
|
|
128
|
+
|
|
61
129
|
/**
|
|
62
130
|
* Get (taskId, status) from active task, or null if no active task.
|
|
63
131
|
*/
|
|
@@ -89,8 +157,11 @@ function getActiveTask(ctx, platformInput = null) {
|
|
|
89
157
|
* "Refer to workflow.md for current step." line
|
|
90
158
|
* - no_task pseudo-status (id === null) → header omits task info
|
|
91
159
|
*/
|
|
92
|
-
function buildBreadcrumb(id, status, templates, source = null) {
|
|
93
|
-
let body = templates[
|
|
160
|
+
function buildBreadcrumb(id, status, templates, source = null, breadcrumbKey = status) {
|
|
161
|
+
let body = templates[breadcrumbKey]
|
|
162
|
+
if (body === undefined && breadcrumbKey !== status) {
|
|
163
|
+
body = templates[status]
|
|
164
|
+
}
|
|
94
165
|
if (body === undefined) {
|
|
95
166
|
body = "Refer to workflow.md for current step."
|
|
96
167
|
}
|
|
@@ -128,10 +199,13 @@ export default async ({ directory }) => {
|
|
|
128
199
|
return
|
|
129
200
|
}
|
|
130
201
|
const templates = loadBreadcrumbs(directory)
|
|
202
|
+
const activationMode = readTrellisActivationMode(directory)
|
|
203
|
+
const noTaskKey =
|
|
204
|
+
activationMode === "explicit" ? "no_task-explicit" : "no_task"
|
|
131
205
|
const task = getActiveTask(ctx, input)
|
|
132
206
|
const breadcrumb = task
|
|
133
207
|
? buildBreadcrumb(task.id, task.status, templates, task.source)
|
|
134
|
-
: buildBreadcrumb(null, "no_task", templates)
|
|
208
|
+
: buildBreadcrumb(null, "no_task", templates, null, noTaskKey)
|
|
135
209
|
|
|
136
210
|
const parts = output?.parts || []
|
|
137
211
|
const textPartIndex = parts.findIndex(
|
|
@@ -659,6 +659,68 @@ function loadWorkflowBreadcrumbs(projectRoot: string): Record<string, string> {
|
|
|
659
659
|
return result;
|
|
660
660
|
}
|
|
661
661
|
|
|
662
|
+
function unquoteYamlScalar(value: string): string {
|
|
663
|
+
const trimmed = value.trim();
|
|
664
|
+
if (trimmed.length >= 2) {
|
|
665
|
+
const first = trimmed[0];
|
|
666
|
+
const last = trimmed[trimmed.length - 1];
|
|
667
|
+
if ((first === '"' || first === "'") && first === last) {
|
|
668
|
+
return trimmed.slice(1, -1);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
return trimmed;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
function stripYamlInlineComment(value: string): string {
|
|
675
|
+
let quote: string | null = null;
|
|
676
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
677
|
+
const ch = value[index] ?? "";
|
|
678
|
+
if (quote) {
|
|
679
|
+
if (ch === quote) quote = null;
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
682
|
+
if (ch === '"' || ch === "'") {
|
|
683
|
+
quote = ch;
|
|
684
|
+
continue;
|
|
685
|
+
}
|
|
686
|
+
if (ch === "#" && (index === 0 || /\s/.test(value[index - 1] ?? ""))) {
|
|
687
|
+
return value.slice(0, index);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
return value;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
function readTrellisActivationMode(projectRoot: string): "auto" | "explicit" {
|
|
694
|
+
const config = readText(join(projectRoot, ".trellis", "config.yaml"));
|
|
695
|
+
if (!config) return "auto";
|
|
696
|
+
|
|
697
|
+
let inTrellis = false;
|
|
698
|
+
let trellisIndent = -1;
|
|
699
|
+
for (const line of config.split(/\r?\n/)) {
|
|
700
|
+
const trimmed = line.trim();
|
|
701
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
702
|
+
|
|
703
|
+
const indent = line.length - line.trimStart().length;
|
|
704
|
+
if (inTrellis && indent <= trellisIndent) {
|
|
705
|
+
inTrellis = false;
|
|
706
|
+
}
|
|
707
|
+
if (!inTrellis && indent === 0 && /^trellis\s*:\s*(?:#.*)?$/.test(trimmed)) {
|
|
708
|
+
inTrellis = true;
|
|
709
|
+
trellisIndent = indent;
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
if (!inTrellis) continue;
|
|
713
|
+
|
|
714
|
+
const match = trimmed.match(/^activation(?:_mode)?\s*:\s*(.*)$/);
|
|
715
|
+
if (!match) continue;
|
|
716
|
+
const value = unquoteYamlScalar(stripYamlInlineComment(match[1] ?? ""))
|
|
717
|
+
.trim()
|
|
718
|
+
.toLowerCase();
|
|
719
|
+
return value === "explicit" ? "explicit" : "auto";
|
|
720
|
+
}
|
|
721
|
+
return "auto";
|
|
722
|
+
}
|
|
723
|
+
|
|
662
724
|
function readActiveTaskStatus(
|
|
663
725
|
projectRoot: string,
|
|
664
726
|
taskDir: string,
|
|
@@ -681,6 +743,7 @@ function buildWorkflowStateBreadcrumb(
|
|
|
681
743
|
contextKey: string | null,
|
|
682
744
|
): string {
|
|
683
745
|
const templates = loadWorkflowBreadcrumbs(projectRoot);
|
|
746
|
+
const activationMode = readTrellisActivationMode(projectRoot);
|
|
684
747
|
const taskDir = readCurrentTask(
|
|
685
748
|
projectRoot,
|
|
686
749
|
undefined,
|
|
@@ -691,18 +754,24 @@ function buildWorkflowStateBreadcrumb(
|
|
|
691
754
|
let lookupKey: string;
|
|
692
755
|
if (!taskDir) {
|
|
693
756
|
header = "Status: no_task\nSource: session";
|
|
694
|
-
lookupKey =
|
|
757
|
+
lookupKey =
|
|
758
|
+
activationMode === "explicit" ? "no_task-explicit" : "no_task";
|
|
695
759
|
} else {
|
|
696
760
|
const info = readActiveTaskStatus(projectRoot, taskDir);
|
|
697
761
|
if (!info) {
|
|
698
762
|
header = "Status: no_task\nSource: session";
|
|
699
|
-
lookupKey =
|
|
763
|
+
lookupKey =
|
|
764
|
+
activationMode === "explicit" ? "no_task-explicit" : "no_task";
|
|
700
765
|
} else {
|
|
701
766
|
header = `Task: ${info.taskId} (${info.status})\nSource: session`;
|
|
702
767
|
lookupKey = info.status;
|
|
703
768
|
}
|
|
704
769
|
}
|
|
705
|
-
const
|
|
770
|
+
const fallbackKey = lookupKey === "no_task-explicit" ? "no_task" : lookupKey;
|
|
771
|
+
const body =
|
|
772
|
+
templates[lookupKey] ??
|
|
773
|
+
templates[fallbackKey] ??
|
|
774
|
+
"Refer to workflow.md for current step.";
|
|
706
775
|
return `<workflow-state>\n${header}\n${body}\n</workflow-state>`;
|
|
707
776
|
}
|
|
708
777
|
|
|
@@ -270,10 +270,33 @@ def _codex_mode_banner(config: dict) -> str:
|
|
|
270
270
|
return f"<codex-mode>{mode}</codex-mode>"
|
|
271
271
|
|
|
272
272
|
|
|
273
|
+
def _trellis_activation_mode(config: dict) -> str:
|
|
274
|
+
"""Read ``trellis.activation_mode`` from .trellis/config.yaml.
|
|
275
|
+
|
|
276
|
+
``auto`` preserves the default no-task behavior: Trellis classifies the
|
|
277
|
+
user's request and may create a task for implementation work. ``explicit``
|
|
278
|
+
uses the alternate no_task breadcrumb so Trellis only starts when the
|
|
279
|
+
current user message explicitly invokes it.
|
|
280
|
+
"""
|
|
281
|
+
if not isinstance(config, dict):
|
|
282
|
+
return "auto"
|
|
283
|
+
trellis_cfg = config.get("trellis")
|
|
284
|
+
if not isinstance(trellis_cfg, dict):
|
|
285
|
+
return "auto"
|
|
286
|
+
value = trellis_cfg.get("activation_mode", trellis_cfg.get("activation"))
|
|
287
|
+
if isinstance(value, str) and value.strip().lower() == "explicit":
|
|
288
|
+
return "explicit"
|
|
289
|
+
return "auto"
|
|
290
|
+
|
|
291
|
+
|
|
273
292
|
def resolve_breadcrumb_key(
|
|
274
293
|
status: str, platform: str | None, config: dict
|
|
275
294
|
) -> str:
|
|
276
|
-
"""Pick the breadcrumb tag key based on Codex dispatch_mode.
|
|
295
|
+
"""Pick the breadcrumb tag key based on activation + Codex dispatch_mode.
|
|
296
|
+
|
|
297
|
+
When ``trellis.activation_mode: explicit`` and there is no active task,
|
|
298
|
+
route to ``[workflow-state:no_task-explicit]`` instead of the default
|
|
299
|
+
``[workflow-state:no_task]``. Active tasks keep normal status routing.
|
|
277
300
|
|
|
278
301
|
Codex defaults to ``inline`` because sub-agents run with ``fork_turns="none"``
|
|
279
302
|
isolation and can't inherit the parent session's task context. Users can
|
|
@@ -283,6 +306,12 @@ def resolve_breadcrumb_key(
|
|
|
283
306
|
|
|
284
307
|
Non-codex platforms return the plain status unchanged.
|
|
285
308
|
"""
|
|
309
|
+
if status == "no_task":
|
|
310
|
+
return (
|
|
311
|
+
"no_task-explicit"
|
|
312
|
+
if _trellis_activation_mode(config) == "explicit"
|
|
313
|
+
else status
|
|
314
|
+
)
|
|
286
315
|
if platform == "codex":
|
|
287
316
|
mode = "inline"
|
|
288
317
|
if isinstance(config, dict):
|
|
@@ -360,7 +389,7 @@ def main() -> int:
|
|
|
360
389
|
)
|
|
361
390
|
if platform == "codex":
|
|
362
391
|
parts: list[str] = [CODEX_SUB_AGENT_NOTICE]
|
|
363
|
-
if task is None:
|
|
392
|
+
if task is None and _trellis_activation_mode(config) != "explicit":
|
|
364
393
|
parts.append(CODEX_NO_TASK_BOOTSTRAP_NOTICE)
|
|
365
394
|
parts.append(_codex_mode_banner(config))
|
|
366
395
|
parts.append(breadcrumb)
|
|
@@ -32,6 +32,21 @@ max_journal_lines: 2000
|
|
|
32
32
|
#
|
|
33
33
|
# session_auto_commit: true
|
|
34
34
|
|
|
35
|
+
#-------------------------------------------------------------------------------
|
|
36
|
+
# Trellis (activation behavior)
|
|
37
|
+
#-------------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
# Controls when Trellis takes over a conversation that has no active Trellis task.
|
|
40
|
+
# - auto (default): Trellis classifies no-task requests and may create a task
|
|
41
|
+
# for implementation / build / refactor work.
|
|
42
|
+
# - explicit: Trellis stays out of ordinary requests. It only starts when the
|
|
43
|
+
# current user message explicitly invokes Trellis (for example `/trellis start`,
|
|
44
|
+
# `$trellis-start`, "use Trellis", or "走 Trellis").
|
|
45
|
+
#
|
|
46
|
+
# trellis:
|
|
47
|
+
# activation_mode: auto
|
|
48
|
+
# # activation_mode: explicit
|
|
49
|
+
|
|
35
50
|
#-------------------------------------------------------------------------------
|
|
36
51
|
# Task Lifecycle Hooks
|
|
37
52
|
#-------------------------------------------------------------------------------
|
|
@@ -99,7 +99,7 @@ python3 ./.trellis/scripts/get_context.py --mode phase --step <X.Y> # detailed
|
|
|
99
99
|
<!--
|
|
100
100
|
WORKFLOW-STATE BREADCRUMB CONTRACT (read this before editing the tag blocks below)
|
|
101
101
|
|
|
102
|
-
The
|
|
102
|
+
The [workflow-state:STATUS] blocks embedded in the ## Phase Index section
|
|
103
103
|
below are the SINGLE source of truth for the per-turn `<workflow-state>`
|
|
104
104
|
breadcrumb that every supported AI platform's UserPromptSubmit hook
|
|
105
105
|
reads. inject-workflow-state.py (Python platforms) and
|
|
@@ -119,6 +119,9 @@ python3 ./.trellis/scripts/get_context.py --mode phase --step <X.Y> # detailed
|
|
|
119
119
|
|
|
120
120
|
TAG ↔ PHASE scoping:
|
|
121
121
|
[workflow-state:no_task] → no active task; before Phase 1
|
|
122
|
+
[workflow-state:no_task-explicit]
|
|
123
|
+
→ no active task when trellis.activation_mode
|
|
124
|
+
is explicit; Trellis starts only by request
|
|
122
125
|
[workflow-state:planning] → all of Phase 1 (status='planning')
|
|
123
126
|
[workflow-state:in_progress] → Phase 2 + Phase 3.1-3.4
|
|
124
127
|
(status stays 'in_progress' from
|
|
@@ -155,6 +158,17 @@ No active task. **A Direct answer** — pure Q&A / explanation / lookup / chat;
|
|
|
155
158
|
**C Inline change** (per-turn only, escape hatch for B) — the user's CURRENT message MUST contain one of: "skip trellis" / "no task" / "just do it" / "don't create a task" / "跳过 trellis" / "别走流程" / "小修一下" / "直接改" / "先别建任务" → briefly acknowledge ("ok, skipping trellis flow this turn"), then inline. **Without seeing one of these phrases you must NOT inline on your own**; do not invent an override the user never said.
|
|
156
159
|
[/workflow-state:no_task]
|
|
157
160
|
|
|
161
|
+
<!-- Per-turn breadcrumb: shown when there is no active task and
|
|
162
|
+
.trellis/config.yaml sets trellis.activation_mode: explicit -->
|
|
163
|
+
|
|
164
|
+
[workflow-state:no_task-explicit]
|
|
165
|
+
No active Trellis task. `trellis.activation_mode: explicit` is enabled, so Trellis must stay idle unless the user's CURRENT message explicitly invokes Trellis.
|
|
166
|
+
**Do not auto-start Trellis**: do NOT create a Trellis task, do NOT load `$trellis-start`, and do NOT switch to the Trellis workflow for ordinary development requests.
|
|
167
|
+
**Explicit triggers**: `/trellis start`, `/trellis:start`, `$trellis-start`, "use Trellis", "start Trellis", "走 Trellis", "用 trellis", "创建 Trellis task", or another unambiguous request to run Trellis.
|
|
168
|
+
**Without an explicit trigger**: continue with the platform/global workflow already in force (for Codex this may include global AGENTS.md, Superpowers, or normal skills), and answer or implement normally.
|
|
169
|
+
**With an explicit trigger**: load `trellis-start`, then create or continue the task per the normal `[workflow-state:no_task]` rules.
|
|
170
|
+
[/workflow-state:no_task-explicit]
|
|
171
|
+
|
|
158
172
|
### Phase 1: Plan
|
|
159
173
|
- 1.0 Create task `[required · once]` (just `task.py create`; status enters planning)
|
|
160
174
|
- 1.1 Requirement exploration `[required · repeatable]`
|
|
@@ -638,11 +652,12 @@ This section is for developers who want to modify the Trellis workflow itself. A
|
|
|
638
652
|
|
|
639
653
|
Edit the corresponding step's walkthrough body in the Phase 1 / 2 / 3 sections above. **Critical constraint**: if you change a step's `[required · once]` marker or add a new `[required · once]` step, you MUST also add a matching enforcement line to that phase's `[workflow-state:STATUS]` tag block — otherwise the per-turn breadcrumb omits the reinforcement, and the AI silently skips the step. The regression tests assert this.
|
|
640
654
|
|
|
641
|
-
|
|
655
|
+
The tag blocks live in the `## Phase Index` section above, immediately after each phase summary:
|
|
642
656
|
|
|
643
657
|
| Scope | Corresponding tag |
|
|
644
658
|
|---|---|
|
|
645
|
-
| No active task (before Phase 1) | `[workflow-state:no_task]` (after the Phase Index ASCII art) |
|
|
659
|
+
| No active task (before Phase 1, auto activation) | `[workflow-state:no_task]` (after the Phase Index ASCII art) |
|
|
660
|
+
| No active task (`activation_mode: explicit`) | `[workflow-state:no_task-explicit]` |
|
|
646
661
|
| All of Phase 1 (task created → ready for implementation) | `[workflow-state:planning]` (after Phase 1 summary) |
|
|
647
662
|
| Phase 2 + Phase 3.1–3.4 (implementation + check + wrap-up) | `[workflow-state:in_progress]` (after Phase 2 summary) |
|
|
648
663
|
| After Phase 3.5 (archived) | `[workflow-state:completed]` (after Phase 3 summary; **currently DEAD**) |
|