@mindfoldhq/trellis 0.6.0-beta.7 → 0.6.0-beta.8
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/dist/configurators/codex.d.ts.map +1 -1
- package/dist/configurators/codex.js +5 -3
- package/dist/configurators/codex.js.map +1 -1
- package/dist/configurators/shared.js +4 -4
- package/dist/configurators/shared.js.map +1 -1
- package/dist/migrations/manifests/0.6.0-beta.8.json +9 -0
- package/dist/templates/claude/agents/trellis-check.md +2 -2
- package/dist/templates/claude/agents/trellis-implement.md +8 -7
- package/dist/templates/codebuddy/agents/trellis-check.md +2 -2
- package/dist/templates/codebuddy/agents/trellis-implement.md +8 -7
- package/dist/templates/codex/agents/trellis-check.toml +4 -4
- package/dist/templates/codex/agents/trellis-implement.toml +4 -4
- package/dist/templates/codex/hooks/session-start.py +183 -119
- package/dist/templates/codex/skills/before-dev/SKILL.md +12 -6
- package/dist/templates/codex/skills/brainstorm/SKILL.md +113 -51
- package/dist/templates/codex/skills/check/SKILL.md +86 -18
- package/dist/templates/codex/skills/start/SKILL.md +33 -323
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-context-loading.md +7 -4
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-spec-structure.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-workflow.md +3 -2
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/context-injection.md +5 -5
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/spec-system.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/task-system.md +8 -6
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/agents.md +5 -4
- package/dist/templates/common/commands/continue.md +6 -5
- package/dist/templates/common/commands/start.md +7 -6
- package/dist/templates/common/skills/before-dev.md +12 -6
- package/dist/templates/common/skills/brainstorm.md +56 -42
- package/dist/templates/common/skills/check.md +7 -1
- package/dist/templates/copilot/hooks/session-start.py +183 -90
- package/dist/templates/copilot/prompts/before-dev.prompt.md +12 -6
- package/dist/templates/copilot/prompts/brainstorm.prompt.md +146 -84
- package/dist/templates/copilot/prompts/check.prompt.md +86 -18
- package/dist/templates/copilot/prompts/parallel.prompt.md +16 -8
- package/dist/templates/copilot/prompts/start.prompt.md +33 -367
- package/dist/templates/cursor/agents/trellis-check.md +2 -2
- package/dist/templates/cursor/agents/trellis-implement.md +8 -7
- package/dist/templates/droid/droids/trellis-check.md +2 -2
- package/dist/templates/droid/droids/trellis-implement.md +8 -7
- package/dist/templates/gemini/agents/trellis-implement.md +7 -6
- package/dist/templates/kiro/agents/trellis-check.json +1 -1
- package/dist/templates/kiro/agents/trellis-implement.json +1 -1
- package/dist/templates/opencode/agents/trellis-check.md +2 -2
- package/dist/templates/opencode/agents/trellis-implement.md +9 -8
- package/dist/templates/opencode/lib/session-utils.js +212 -123
- package/dist/templates/opencode/plugins/inject-subagent-context.js +23 -7
- package/dist/templates/opencode/plugins/inject-workflow-state.js +1 -4
- package/dist/templates/pi/extensions/trellis/index.ts.txt +7 -5
- package/dist/templates/qoder/agents/trellis-implement.md +7 -6
- package/dist/templates/shared-hooks/inject-subagent-context.py +36 -14
- package/dist/templates/shared-hooks/inject-workflow-state.py +18 -42
- package/dist/templates/shared-hooks/session-start.py +197 -163
- package/dist/templates/trellis/scripts/common/task_context.py +3 -3
- package/dist/templates/trellis/scripts/common/task_store.py +39 -7
- package/dist/templates/trellis/scripts/common/workflow_phase.py +7 -10
- package/dist/templates/trellis/scripts/task.py +3 -3
- package/dist/templates/trellis/workflow.md +98 -98
- package/package.json +1 -1
|
@@ -27,8 +27,8 @@ You are already the `trellis-implement` sub-agent that the main session dispatch
|
|
|
27
27
|
|
|
28
28
|
Look for the `<!-- trellis-hook-injected -->` marker in your input above.
|
|
29
29
|
|
|
30
|
-
- **If the marker is present**:
|
|
31
|
-
- **If the marker is absent**: hook injection didn't fire (Windows + Claude Code, `--continue` resume, fork distribution, hooks disabled, etc.). Find the active task path from your dispatch prompt's first line `Active task: <path>` (or run `python3 ./.trellis/scripts/task.py current --source` as a fallback), then Read `<task-path>/prd.md`, `<task-path>/
|
|
30
|
+
- **If the marker is present**: task artifacts, spec, and research files have already been auto-loaded for you above. Proceed with the implementation work directly.
|
|
31
|
+
- **If the marker is absent**: hook injection didn't fire (Windows + Claude Code, `--continue` resume, fork distribution, hooks disabled, etc.). Find the active task path from your dispatch prompt's first line `Active task: <path>` (or run `python3 ./.trellis/scripts/task.py current --source` as a fallback), then Read `<task-path>/implement.jsonl`, each listed file, `<task-path>/prd.md`, `<task-path>/design.md` if present, and `<task-path>/implement.md` if present before doing the work.
|
|
32
32
|
|
|
33
33
|
## Context
|
|
34
34
|
|
|
@@ -36,13 +36,14 @@ Before implementing, read:
|
|
|
36
36
|
- `.trellis/workflow.md` - Project workflow
|
|
37
37
|
- `.trellis/spec/` - Development guidelines
|
|
38
38
|
- Task `prd.md` - Requirements document
|
|
39
|
-
- Task `
|
|
39
|
+
- Task `design.md` - Technical design (if exists)
|
|
40
|
+
- Task `implement.md` - Execution plan (if exists)
|
|
40
41
|
|
|
41
42
|
## Core Responsibilities
|
|
42
43
|
|
|
43
44
|
1. **Understand specs** - Read relevant spec files in `.trellis/spec/`
|
|
44
|
-
2. **Understand
|
|
45
|
-
3. **Implement features** - Write code following specs and
|
|
45
|
+
2. **Understand task artifacts** - Read prd.md, design.md if present, and implement.md if present
|
|
46
|
+
3. **Implement features** - Write code following specs and task artifacts
|
|
46
47
|
4. **Self-check** - Ensure code quality
|
|
47
48
|
5. **Report results** - Report completion status
|
|
48
49
|
|
|
@@ -68,15 +69,15 @@ Read relevant specs based on task type:
|
|
|
68
69
|
|
|
69
70
|
### 2. Understand Requirements
|
|
70
71
|
|
|
71
|
-
Read the task's prd.md and
|
|
72
|
+
Read the task's prd.md, design.md if present, and implement.md if present:
|
|
72
73
|
|
|
73
74
|
- What are the core requirements
|
|
74
75
|
- Key points of technical design
|
|
75
|
-
-
|
|
76
|
+
- Implementation order, validation commands, and rollback points
|
|
76
77
|
|
|
77
78
|
### 3. Implement Features
|
|
78
79
|
|
|
79
|
-
- Write code following specs and
|
|
80
|
+
- Write code following specs and task artifacts
|
|
80
81
|
- Follow existing code patterns
|
|
81
82
|
- Only do what's required, no over-engineering
|
|
82
83
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* global process */
|
|
2
2
|
import { existsSync, readFileSync, readdirSync, statSync } from "fs"
|
|
3
|
-
import {
|
|
3
|
+
import { join } from "path"
|
|
4
4
|
import { execFileSync } from "child_process"
|
|
5
5
|
import { platform } from "os"
|
|
6
6
|
import { debugLog } from "./trellis-context.js"
|
|
@@ -8,9 +8,8 @@ import { debugLog } from "./trellis-context.js"
|
|
|
8
8
|
const PYTHON_CMD = platform() === "win32" ? "python" : "python3"
|
|
9
9
|
|
|
10
10
|
const FIRST_REPLY_NOTICE = `<first-reply-notice>
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Then continue directly with the user's request. This notice is one-shot: do not repeat it after the first assistant reply in the same session.
|
|
11
|
+
First visible reply: say once in Chinese that Trellis SessionStart context is loaded, then answer directly.
|
|
12
|
+
This notice is one-shot: do not repeat it after the first assistant reply in the same session.
|
|
14
13
|
</first-reply-notice>`
|
|
15
14
|
|
|
16
15
|
function hasCuratedJsonlEntry(jsonlPath) {
|
|
@@ -38,13 +37,18 @@ function getTaskStatus(ctx, platformInput = null) {
|
|
|
38
37
|
const active = ctx.getActiveTask(platformInput)
|
|
39
38
|
const taskRef = active.taskPath
|
|
40
39
|
if (!taskRef) {
|
|
41
|
-
return
|
|
40
|
+
return (
|
|
41
|
+
"Status: NO ACTIVE TASK\n" +
|
|
42
|
+
"Next-Action: Classify the current turn before creating any Trellis task. " +
|
|
43
|
+
"Simple conversation / small task asks only whether this turn should create a Trellis task. " +
|
|
44
|
+
"Complex task asks whether task creation and planning are allowed."
|
|
45
|
+
)
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
const taskDir = ctx.resolveTaskDir(taskRef)
|
|
45
49
|
|
|
46
50
|
if (active.stale || !taskDir || !existsSync(taskDir)) {
|
|
47
|
-
return `Status: STALE POINTER\nTask: ${taskRef}\
|
|
51
|
+
return `Status: STALE POINTER\nTask: ${taskRef}\nNext-Action: Task directory not found. Run: python3 ./.trellis/scripts/task.py finish`
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
let taskData = {}
|
|
@@ -61,39 +65,49 @@ function getTaskStatus(ctx, platformInput = null) {
|
|
|
61
65
|
const taskStatus = taskData.status || "unknown"
|
|
62
66
|
|
|
63
67
|
if (taskStatus === "completed") {
|
|
64
|
-
|
|
65
|
-
return `Status: COMPLETED\nTask: ${taskTitle}\nSource: ${active.source}\nNext: Archive with \`python3 ./.trellis/scripts/task.py archive ${dirName}\` or start a new task`
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
let hasContext = false
|
|
69
|
-
for (const jsonlName of ["implement.jsonl", "check.jsonl"]) {
|
|
70
|
-
const jsonlPath = join(taskDir, jsonlName)
|
|
71
|
-
if (existsSync(jsonlPath) && hasCuratedJsonlEntry(jsonlPath)) {
|
|
72
|
-
hasContext = true
|
|
73
|
-
break
|
|
74
|
-
}
|
|
68
|
+
return `Status: COMPLETED\nTask: ${taskTitle}\nNext-Action: Run /trellis:finish-work. If the working tree is dirty, return to Phase 3.4 first.`
|
|
75
69
|
}
|
|
76
70
|
|
|
77
71
|
const hasPrd = existsSync(join(taskDir, "prd.md"))
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
72
|
+
const hasDesign = existsSync(join(taskDir, "design.md"))
|
|
73
|
+
const hasImplementPlan = existsSync(join(taskDir, "implement.md"))
|
|
74
|
+
const artifactNames = ["prd.md", "design.md", "implement.md", "implement.jsonl", "check.jsonl"]
|
|
75
|
+
const present = artifactNames.filter(name => existsSync(join(taskDir, name)))
|
|
76
|
+
if (existsSync(join(taskDir, "research"))) present.push("research/")
|
|
77
|
+
const presentLine = present.length > 0 ? present.join(", ") : "(none)"
|
|
78
|
+
const implementJsonl = join(taskDir, "implement.jsonl")
|
|
79
|
+
const checkJsonl = join(taskDir, "check.jsonl")
|
|
80
|
+
const jsonlReady =
|
|
81
|
+
(!existsSync(implementJsonl) || hasCuratedJsonlEntry(implementJsonl)) &&
|
|
82
|
+
(!existsSync(checkJsonl) || hasCuratedJsonlEntry(checkJsonl))
|
|
83
|
+
|
|
84
|
+
if (taskStatus === "planning" && !hasPrd) {
|
|
85
|
+
return `Status: PLANNING\nTask: ${taskTitle}\nPresent: ${presentLine}\nNext-Action: Load trellis-brainstorm and write prd.md. Stay in planning.`
|
|
81
86
|
}
|
|
82
87
|
|
|
83
|
-
if (
|
|
84
|
-
|
|
88
|
+
if (taskStatus === "planning") {
|
|
89
|
+
const missingComplex = []
|
|
90
|
+
if (!hasDesign) missingComplex.push("design.md")
|
|
91
|
+
if (!hasImplementPlan) missingComplex.push("implement.md")
|
|
92
|
+
const nextBits = []
|
|
93
|
+
if (missingComplex.length > 0) {
|
|
94
|
+
nextBits.push(
|
|
95
|
+
`Lightweight task can request start review with PRD-only; complex task must add ${missingComplex.join(", ")} before start`,
|
|
96
|
+
)
|
|
97
|
+
} else {
|
|
98
|
+
nextBits.push("Planning artifacts are present; ask for review before `task.py start`")
|
|
99
|
+
}
|
|
100
|
+
if (!jsonlReady) {
|
|
101
|
+
nextBits.push("curate `implement.jsonl` and `check.jsonl` before sub-agent mode start")
|
|
102
|
+
}
|
|
103
|
+
return `Status: PLANNING\nTask: ${taskTitle}\nPresent: ${presentLine}\nNext-Action: ${nextBits.join("; ")}. Do not enter implementation until the user confirms start.`
|
|
85
104
|
}
|
|
86
105
|
|
|
87
106
|
return (
|
|
88
|
-
`Status:
|
|
89
|
-
`
|
|
90
|
-
"Next
|
|
91
|
-
"
|
|
92
|
-
"After implementation, dispatch `trellis-check` per Phase 2.2 before reporting completion.\n" +
|
|
93
|
-
"User override (per-turn escape hatch): if the user's CURRENT message explicitly tells the " +
|
|
94
|
-
"main session to handle it directly (\"你直接改\" / \"别派 sub-agent\" / \"main session 写就行\" / " +
|
|
95
|
-
"\"do it inline\" / \"不用 sub-agent\"), honor it for this turn and edit code directly. " +
|
|
96
|
-
"Per-turn only; do NOT invent an override the user did not say."
|
|
107
|
+
`Status: ${String(taskStatus).toUpperCase()}\nTask: ${taskTitle}\n` +
|
|
108
|
+
`Present: ${presentLine}\n` +
|
|
109
|
+
"Next-Action: Follow the matching per-turn workflow-state. " +
|
|
110
|
+
"Implementation/check context order is jsonl entries -> `prd.md` -> `design.md if present` -> `implement.md if present`."
|
|
97
111
|
)
|
|
98
112
|
}
|
|
99
113
|
|
|
@@ -201,22 +215,163 @@ function resolveSpecScope(config) {
|
|
|
201
215
|
return null
|
|
202
216
|
}
|
|
203
217
|
|
|
218
|
+
function collectSpecIndexPaths(directory, allowedPkgs) {
|
|
219
|
+
const specDir = join(directory, ".trellis", "spec")
|
|
220
|
+
const paths = []
|
|
221
|
+
|
|
222
|
+
const guidesIndex = join(specDir, "guides", "index.md")
|
|
223
|
+
if (existsSync(guidesIndex)) {
|
|
224
|
+
paths.push(".trellis/spec/guides/index.md")
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (!existsSync(specDir)) return paths
|
|
228
|
+
|
|
229
|
+
try {
|
|
230
|
+
const subs = readdirSync(specDir).filter(name => {
|
|
231
|
+
if (name.startsWith(".") || name === "guides") return false
|
|
232
|
+
try {
|
|
233
|
+
return statSync(join(specDir, name)).isDirectory()
|
|
234
|
+
} catch {
|
|
235
|
+
return false
|
|
236
|
+
}
|
|
237
|
+
}).sort()
|
|
238
|
+
|
|
239
|
+
for (const sub of subs) {
|
|
240
|
+
const indexFile = join(specDir, sub, "index.md")
|
|
241
|
+
if (existsSync(indexFile)) {
|
|
242
|
+
paths.push(`.trellis/spec/${sub}/index.md`)
|
|
243
|
+
} else {
|
|
244
|
+
if (allowedPkgs !== null && !allowedPkgs.has(sub)) continue
|
|
245
|
+
try {
|
|
246
|
+
const nested = readdirSync(join(specDir, sub)).filter(name => {
|
|
247
|
+
try {
|
|
248
|
+
return statSync(join(specDir, sub, name)).isDirectory()
|
|
249
|
+
} catch {
|
|
250
|
+
return false
|
|
251
|
+
}
|
|
252
|
+
}).sort()
|
|
253
|
+
for (const layer of nested) {
|
|
254
|
+
const nestedIndex = join(specDir, sub, layer, "index.md")
|
|
255
|
+
if (existsSync(nestedIndex)) {
|
|
256
|
+
paths.push(`.trellis/spec/${sub}/${layer}/index.md`)
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
} catch {
|
|
260
|
+
// Ignore directory read errors
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
} catch {
|
|
265
|
+
// Ignore spec directory read errors
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return paths
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function readDeveloper(directory) {
|
|
272
|
+
try {
|
|
273
|
+
const content = readFileSync(join(directory, ".trellis", ".developer"), "utf-8")
|
|
274
|
+
for (const line of content.split(/\r?\n/)) {
|
|
275
|
+
if (line.startsWith("name=")) return line.slice("name=".length).trim()
|
|
276
|
+
}
|
|
277
|
+
} catch {
|
|
278
|
+
// Ignore missing developer file
|
|
279
|
+
}
|
|
280
|
+
return "(not initialized)"
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function runGit(directory, args) {
|
|
284
|
+
try {
|
|
285
|
+
return execFileSync("git", args, {
|
|
286
|
+
cwd: directory,
|
|
287
|
+
timeout: 3000,
|
|
288
|
+
encoding: "utf-8",
|
|
289
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
290
|
+
}).trim()
|
|
291
|
+
} catch {
|
|
292
|
+
return ""
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function buildCompactCurrentState(ctx, platformInput, specIndexPaths) {
|
|
297
|
+
const directory = ctx.directory
|
|
298
|
+
const lines = []
|
|
299
|
+
lines.push(`Developer: ${readDeveloper(directory)}`)
|
|
300
|
+
|
|
301
|
+
const branch = runGit(directory, ["branch", "--show-current"]) || "(detached)"
|
|
302
|
+
const dirtyCount = runGit(directory, ["status", "--porcelain"])
|
|
303
|
+
.split(/\r?\n/)
|
|
304
|
+
.filter(line => line.trim()).length
|
|
305
|
+
lines.push(`Git: branch ${branch}; ${dirtyCount === 0 ? "clean" : `dirty ${dirtyCount} paths`}.`)
|
|
306
|
+
|
|
307
|
+
const active = ctx.getActiveTask(platformInput)
|
|
308
|
+
if (active.taskPath) {
|
|
309
|
+
const taskDir = ctx.resolveTaskDir(active.taskPath)
|
|
310
|
+
let status = "unknown"
|
|
311
|
+
if (taskDir) {
|
|
312
|
+
try {
|
|
313
|
+
const data = JSON.parse(readFileSync(join(taskDir, "task.json"), "utf-8"))
|
|
314
|
+
status = data.status || "unknown"
|
|
315
|
+
} catch {
|
|
316
|
+
// Ignore parse errors
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
lines.push(`Current task: ${active.taskPath}; status=${status}.`)
|
|
320
|
+
} else {
|
|
321
|
+
lines.push("Current task: none.")
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const tasksDir = join(directory, ".trellis", "tasks")
|
|
325
|
+
if (existsSync(tasksDir)) {
|
|
326
|
+
try {
|
|
327
|
+
const activeTasks = readdirSync(tasksDir, { withFileTypes: true })
|
|
328
|
+
.filter(entry => entry.isDirectory() && entry.name !== "archive" && existsSync(join(tasksDir, entry.name, "task.json")))
|
|
329
|
+
lines.push(`Active tasks: ${activeTasks.length} total. Use \`python3 ./.trellis/scripts/task.py list --mine\` only if needed.`)
|
|
330
|
+
} catch {
|
|
331
|
+
// Ignore task list errors
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const developer = readDeveloper(directory)
|
|
336
|
+
const workspaceDir = join(directory, ".trellis", "workspace", developer)
|
|
337
|
+
if (developer !== "(not initialized)" && existsSync(workspaceDir)) {
|
|
338
|
+
try {
|
|
339
|
+
const journals = readdirSync(workspaceDir)
|
|
340
|
+
.filter(name => /^journal-\d+\.md$/.test(name))
|
|
341
|
+
.sort((a, b) => Number(a.match(/\d+/)?.[0] || 0) - Number(b.match(/\d+/)?.[0] || 0))
|
|
342
|
+
const journal = journals[journals.length - 1]
|
|
343
|
+
if (journal) {
|
|
344
|
+
const journalPath = join(workspaceDir, journal)
|
|
345
|
+
const lineCount = readFileSync(journalPath, "utf-8").split(/\r?\n/).length
|
|
346
|
+
lines.push(`Journal: .trellis/workspace/${developer}/${journal}, ${lineCount} / 2000 lines.`)
|
|
347
|
+
}
|
|
348
|
+
} catch {
|
|
349
|
+
// Ignore journal errors
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (specIndexPaths.length > 0) {
|
|
354
|
+
lines.push(`Spec indexes: ${specIndexPaths.length} available.`)
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return lines.join("\n")
|
|
358
|
+
}
|
|
359
|
+
|
|
204
360
|
export function buildSessionContext(ctx, platformInput = null) {
|
|
205
361
|
const directory = ctx.directory
|
|
206
|
-
const trellisDir = join(directory, ".trellis")
|
|
207
362
|
const contextKey = typeof ctx.getContextKey === "function"
|
|
208
363
|
? ctx.getContextKey(platformInput)
|
|
209
364
|
: null
|
|
210
365
|
|
|
211
366
|
const config = loadTrellisConfig(directory, contextKey)
|
|
212
367
|
const allowedPkgs = resolveSpecScope(config)
|
|
368
|
+
const paths = collectSpecIndexPaths(directory, allowedPkgs)
|
|
213
369
|
|
|
214
370
|
const parts = []
|
|
215
371
|
|
|
216
|
-
parts.push(`<
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
</trellis-context>`)
|
|
372
|
+
parts.push(`<session-context>
|
|
373
|
+
Trellis compact SessionStart context. Use it to orient the session; load details on demand.
|
|
374
|
+
</session-context>`)
|
|
220
375
|
parts.push(FIRST_REPLY_NOTICE)
|
|
221
376
|
|
|
222
377
|
const legacyWarning = checkLegacySpec(directory, config)
|
|
@@ -224,29 +379,18 @@ Read and follow all instructions below carefully.
|
|
|
224
379
|
parts.push(`<migration-warning>\n${legacyWarning}\n</migration-warning>`)
|
|
225
380
|
}
|
|
226
381
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
if (output) {
|
|
231
|
-
parts.push("<current-state>")
|
|
232
|
-
parts.push(output)
|
|
233
|
-
parts.push("</current-state>")
|
|
234
|
-
}
|
|
235
|
-
}
|
|
382
|
+
parts.push("<current-state>")
|
|
383
|
+
parts.push(buildCompactCurrentState(ctx, platformInput, paths))
|
|
384
|
+
parts.push("</current-state>")
|
|
236
385
|
|
|
237
386
|
const workflowContent = ctx.readProjectFile(".trellis/workflow.md")
|
|
238
387
|
if (workflowContent) {
|
|
239
388
|
const allLines = workflowContent.split("\n")
|
|
240
389
|
const overviewLines = [
|
|
241
|
-
"# Development Workflow
|
|
242
|
-
"Full guide: .trellis/workflow.md
|
|
390
|
+
"# Development Workflow - Session Summary",
|
|
391
|
+
"Full guide: .trellis/workflow.md. Step detail: `python3 ./.trellis/scripts/get_context.py --mode phase --step <X.Y>`.",
|
|
243
392
|
"",
|
|
244
|
-
"## Table of Contents",
|
|
245
393
|
]
|
|
246
|
-
for (const line of allLines) {
|
|
247
|
-
if (line.startsWith("## ")) overviewLines.push(line)
|
|
248
|
-
}
|
|
249
|
-
overviewLines.push("", "---", "")
|
|
250
394
|
|
|
251
395
|
let rangeStart = -1
|
|
252
396
|
let rangeEnd = allLines.length
|
|
@@ -254,89 +398,36 @@ Read and follow all instructions below carefully.
|
|
|
254
398
|
const stripped = allLines[i].trim()
|
|
255
399
|
if (rangeStart === -1 && stripped === "## Phase Index") {
|
|
256
400
|
rangeStart = i
|
|
257
|
-
} else if (rangeStart !== -1 && stripped === "##
|
|
401
|
+
} else if (rangeStart !== -1 && stripped === "## Phase 1: Plan") {
|
|
258
402
|
rangeEnd = i
|
|
259
403
|
break
|
|
260
404
|
}
|
|
261
405
|
}
|
|
262
406
|
if (rangeStart !== -1) {
|
|
263
|
-
|
|
407
|
+
const strippedStateBlocks = allLines
|
|
408
|
+
.slice(rangeStart, rangeEnd)
|
|
409
|
+
.join("\n")
|
|
410
|
+
.replace(/\[workflow-state:([A-Za-z0-9_-]+)\]\s*\n[\s\S]*?\n\s*\[\/workflow-state:\1\]\n?/g, "")
|
|
411
|
+
.replace(/<!--[\s\S]*?-->/g, "")
|
|
412
|
+
.replace(/^\[(?!\/?workflow-state:)\/?[^\]\n]+\]\s*\n?/gm, "")
|
|
413
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
414
|
+
overviewLines.push(strippedStateBlocks.trimEnd())
|
|
264
415
|
}
|
|
265
416
|
|
|
266
|
-
parts.push("<workflow>")
|
|
417
|
+
parts.push("<trellis-workflow>")
|
|
267
418
|
parts.push(overviewLines.join("\n").trimEnd())
|
|
268
|
-
parts.push("</workflow>")
|
|
419
|
+
parts.push("</trellis-workflow>")
|
|
269
420
|
}
|
|
270
421
|
|
|
271
422
|
parts.push("<guidelines>")
|
|
272
423
|
parts.push(
|
|
273
|
-
"
|
|
274
|
-
"
|
|
275
|
-
"
|
|
276
|
-
"- If you're spawning an implement/check sub-agent, context is injected " +
|
|
277
|
-
"automatically via `{task}/implement.jsonl` / `check.jsonl`. You do NOT " +
|
|
278
|
-
"need to read these indexes yourself.\n" +
|
|
279
|
-
"- For agent-capable platforms, do NOT edit code directly in the main " +
|
|
280
|
-
"session; dispatch `trellis-implement` and `trellis-check` so JSONL " +
|
|
281
|
-
"context is loaded by the sub-agents.\n"
|
|
424
|
+
"Task context order for implementation/check: jsonl entries -> `prd.md` -> " +
|
|
425
|
+
"`design.md if present` -> `implement.md if present`. Missing optional artifacts " +
|
|
426
|
+
"are skipped for lightweight tasks.\n"
|
|
282
427
|
)
|
|
283
428
|
|
|
284
|
-
const specDir = join(directory, ".trellis", "spec")
|
|
285
|
-
|
|
286
|
-
const guidesIndex = join(specDir, "guides", "index.md")
|
|
287
|
-
if (existsSync(guidesIndex)) {
|
|
288
|
-
const content = ctx.readFile(guidesIndex)
|
|
289
|
-
if (content) {
|
|
290
|
-
parts.push(`## guides (inlined — cross-package thinking guides)\n${content}\n`)
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
const paths = []
|
|
295
|
-
if (existsSync(specDir)) {
|
|
296
|
-
try {
|
|
297
|
-
const subs = readdirSync(specDir).filter(name => {
|
|
298
|
-
if (name.startsWith(".")) return false
|
|
299
|
-
try {
|
|
300
|
-
return statSync(join(specDir, name)).isDirectory()
|
|
301
|
-
} catch {
|
|
302
|
-
return false
|
|
303
|
-
}
|
|
304
|
-
}).sort()
|
|
305
|
-
|
|
306
|
-
for (const sub of subs) {
|
|
307
|
-
if (sub === "guides") continue
|
|
308
|
-
|
|
309
|
-
const indexFile = join(specDir, sub, "index.md")
|
|
310
|
-
if (existsSync(indexFile)) {
|
|
311
|
-
paths.push(`.trellis/spec/${sub}/index.md`)
|
|
312
|
-
} else {
|
|
313
|
-
if (allowedPkgs !== null && !allowedPkgs.has(sub)) continue
|
|
314
|
-
try {
|
|
315
|
-
const nested = readdirSync(join(specDir, sub)).filter(name => {
|
|
316
|
-
try {
|
|
317
|
-
return statSync(join(specDir, sub, name)).isDirectory()
|
|
318
|
-
} catch {
|
|
319
|
-
return false
|
|
320
|
-
}
|
|
321
|
-
}).sort()
|
|
322
|
-
for (const layer of nested) {
|
|
323
|
-
const nestedIndex = join(specDir, sub, layer, "index.md")
|
|
324
|
-
if (existsSync(nestedIndex)) {
|
|
325
|
-
paths.push(`.trellis/spec/${sub}/${layer}/index.md`)
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
} catch {
|
|
329
|
-
// Ignore directory read errors
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
} catch {
|
|
334
|
-
// Ignore spec directory read errors
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
429
|
if (paths.length > 0) {
|
|
339
|
-
parts.push("## Available
|
|
430
|
+
parts.push("## Available indexes (read on demand)")
|
|
340
431
|
for (const p of paths) {
|
|
341
432
|
parts.push(`- ${p}`)
|
|
342
433
|
}
|
|
@@ -353,9 +444,7 @@ Read and follow all instructions below carefully.
|
|
|
353
444
|
parts.push(`<task-status>\n${taskStatus}\n</task-status>`)
|
|
354
445
|
|
|
355
446
|
parts.push(`<ready>
|
|
356
|
-
Context loaded.
|
|
357
|
-
When the user sends the first message, follow <task-status> and the workflow guide.
|
|
358
|
-
If a task is READY, execute its Next required action without asking whether to continue.
|
|
447
|
+
Context loaded. Follow <task-status>. Load workflow/spec/task details only when needed.
|
|
359
448
|
</ready>`)
|
|
360
449
|
|
|
361
450
|
return parts.join("\n\n")
|
|
@@ -31,9 +31,14 @@ function getImplementContext(ctx, taskDir) {
|
|
|
31
31
|
parts.push(`=== ${taskDir}/prd.md (Requirements) ===\n${prd}`)
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
const
|
|
35
|
-
if (
|
|
36
|
-
parts.push(`=== ${taskDir}/
|
|
34
|
+
const design = ctx.readProjectFile(join(taskDir, "design.md"))
|
|
35
|
+
if (design) {
|
|
36
|
+
parts.push(`=== ${taskDir}/design.md (Technical Design) ===\n${design}`)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const implementPlan = ctx.readProjectFile(join(taskDir, "implement.md"))
|
|
40
|
+
if (implementPlan) {
|
|
41
|
+
parts.push(`=== ${taskDir}/implement.md (Execution Plan) ===\n${implementPlan}`)
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
return parts.join("\n\n")
|
|
@@ -56,6 +61,16 @@ function getCheckContext(ctx, taskDir) {
|
|
|
56
61
|
parts.push(`=== ${taskDir}/prd.md (Requirements) ===\n${prd}`)
|
|
57
62
|
}
|
|
58
63
|
|
|
64
|
+
const design = ctx.readProjectFile(join(taskDir, "design.md"))
|
|
65
|
+
if (design) {
|
|
66
|
+
parts.push(`=== ${taskDir}/design.md (Technical Design) ===\n${design}`)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const implementPlan = ctx.readProjectFile(join(taskDir, "implement.md"))
|
|
70
|
+
if (implementPlan) {
|
|
71
|
+
parts.push(`=== ${taskDir}/implement.md (Execution Plan) ===\n${implementPlan}`)
|
|
72
|
+
}
|
|
73
|
+
|
|
59
74
|
return parts.join("\n\n")
|
|
60
75
|
}
|
|
61
76
|
|
|
@@ -147,8 +162,8 @@ ${originalPrompt}
|
|
|
147
162
|
## Workflow
|
|
148
163
|
|
|
149
164
|
1. **Understand specs** - All dev specs are injected above
|
|
150
|
-
2. **Understand
|
|
151
|
-
3. **Implement feature** - Follow specs and
|
|
165
|
+
2. **Understand task artifacts** - Read requirements, technical design if present, and execution plan if present
|
|
166
|
+
3. **Implement feature** - Follow specs and task artifacts
|
|
152
167
|
4. **Self-check** - Ensure code quality
|
|
153
168
|
|
|
154
169
|
## Important Constraints
|
|
@@ -176,7 +191,7 @@ ${originalPrompt}
|
|
|
176
191
|
## Workflow
|
|
177
192
|
|
|
178
193
|
1. **Review changes** - Run \`git diff --name-only\` to see all changed files
|
|
179
|
-
2. **Verify
|
|
194
|
+
2. **Verify task artifacts** - Check prd.md and, when present, design.md / implement.md
|
|
180
195
|
3. **Spec sync** - Analyze whether changes introduce new patterns, contracts, or conventions
|
|
181
196
|
- If new pattern/convention found: read target spec file → update it → update index.md if needed
|
|
182
197
|
- If infra/cross-layer change: follow the 7-section mandatory template from update-spec.md
|
|
@@ -190,7 +205,8 @@ ${originalPrompt}
|
|
|
190
205
|
- MUST read the target spec file BEFORE editing (avoid duplicating existing content)
|
|
191
206
|
- Do NOT update specs for trivial changes (typos, formatting, obvious fixes)
|
|
192
207
|
- If critical CODE issues found, report them clearly (fix specs, not code)
|
|
193
|
-
- Verify all acceptance criteria in prd.md are met
|
|
208
|
+
- Verify all acceptance criteria in prd.md are met
|
|
209
|
+
- Verify design.md and implement.md constraints when those files are present` :
|
|
194
210
|
`# Check Agent Task
|
|
195
211
|
|
|
196
212
|
You are the Check Agent in the Multi-Agent Pipeline.
|
|
@@ -89,15 +89,12 @@ function getActiveTask(ctx, platformInput = null) {
|
|
|
89
89
|
* "Refer to workflow.md for current step." line
|
|
90
90
|
* - no_task pseudo-status (id === null) → header omits task info
|
|
91
91
|
*/
|
|
92
|
-
function buildBreadcrumb(id, status, templates
|
|
92
|
+
function buildBreadcrumb(id, status, templates) {
|
|
93
93
|
let body = templates[status]
|
|
94
94
|
if (body === undefined) {
|
|
95
95
|
body = "Refer to workflow.md for current step."
|
|
96
96
|
}
|
|
97
97
|
let header = id === null ? `Status: ${status}` : `Task: ${id} (${status})`
|
|
98
|
-
if (source) {
|
|
99
|
-
header = `${header}\nSource: ${source}`
|
|
100
|
-
}
|
|
101
98
|
return `<workflow-state>\n${header}\n${body}\n</workflow-state>`
|
|
102
99
|
}
|
|
103
100
|
|
|
@@ -615,7 +615,8 @@ function buildTrellisContext(
|
|
|
615
615
|
}
|
|
616
616
|
|
|
617
617
|
const prd = readText(join(taskDir, "prd.md"));
|
|
618
|
-
const
|
|
618
|
+
const design = readText(join(taskDir, "design.md"));
|
|
619
|
+
const implementPlan = readText(join(taskDir, "implement.md"));
|
|
619
620
|
const jsonlName = TRELLIS_AGENT_JSONL[agent] ?? "";
|
|
620
621
|
const specContext = jsonlName
|
|
621
622
|
? readJsonlFiles(projectRoot, taskDir, jsonlName)
|
|
@@ -627,7 +628,8 @@ function buildTrellisContext(
|
|
|
627
628
|
"",
|
|
628
629
|
"### prd.md",
|
|
629
630
|
prd || "(missing)",
|
|
630
|
-
|
|
631
|
+
design ? "\n### design.md\n" + design : "",
|
|
632
|
+
implementPlan ? "\n### implement.md\n" + implementPlan : "",
|
|
631
633
|
specContext ? "\n### Curated Spec / Research Context\n" + specContext : "",
|
|
632
634
|
].join("\n");
|
|
633
635
|
}
|
|
@@ -690,15 +692,15 @@ function buildWorkflowStateBreadcrumb(
|
|
|
690
692
|
let header: string;
|
|
691
693
|
let lookupKey: string;
|
|
692
694
|
if (!taskDir) {
|
|
693
|
-
header = "Status: no_task
|
|
695
|
+
header = "Status: no_task";
|
|
694
696
|
lookupKey = "no_task";
|
|
695
697
|
} else {
|
|
696
698
|
const info = readActiveTaskStatus(projectRoot, taskDir);
|
|
697
699
|
if (!info) {
|
|
698
|
-
header = "Status: no_task
|
|
700
|
+
header = "Status: no_task";
|
|
699
701
|
lookupKey = "no_task";
|
|
700
702
|
} else {
|
|
701
|
-
header = `Task: ${info.taskId} (${info.status})
|
|
703
|
+
header = `Task: ${info.taskId} (${info.status})`;
|
|
702
704
|
lookupKey = info.status;
|
|
703
705
|
}
|
|
704
706
|
}
|
|
@@ -22,13 +22,14 @@ Before implementing, read:
|
|
|
22
22
|
- `.trellis/workflow.md` - Project workflow
|
|
23
23
|
- `.trellis/spec/` - Development guidelines
|
|
24
24
|
- Task `prd.md` - Requirements document
|
|
25
|
-
- Task `
|
|
25
|
+
- Task `design.md` - Technical design (if exists)
|
|
26
|
+
- Task `implement.md` - Execution plan (if exists)
|
|
26
27
|
|
|
27
28
|
## Core Responsibilities
|
|
28
29
|
|
|
29
30
|
1. **Understand specs** - Read relevant spec files in `.trellis/spec/`
|
|
30
|
-
2. **Understand
|
|
31
|
-
3. **Implement features** - Write code following specs and
|
|
31
|
+
2. **Understand task artifacts** - Read prd.md, design.md if present, and implement.md if present
|
|
32
|
+
3. **Implement features** - Write code following specs and task artifacts
|
|
32
33
|
4. **Self-check** - Ensure code quality
|
|
33
34
|
5. **Report results** - Report completion status
|
|
34
35
|
|
|
@@ -53,15 +54,15 @@ Read relevant specs based on task type:
|
|
|
53
54
|
|
|
54
55
|
### 2. Understand Requirements
|
|
55
56
|
|
|
56
|
-
Read the task's prd.md and
|
|
57
|
+
Read the task's prd.md, design.md if present, and implement.md if present:
|
|
57
58
|
|
|
58
59
|
- What are the core requirements
|
|
59
60
|
- Key points of technical design
|
|
60
|
-
-
|
|
61
|
+
- Implementation order, validation commands, and rollback points
|
|
61
62
|
|
|
62
63
|
### 3. Implement Features
|
|
63
64
|
|
|
64
|
-
- Write code following specs and
|
|
65
|
+
- Write code following specs and task artifacts
|
|
65
66
|
- Follow existing code patterns
|
|
66
67
|
- Only do what's required, no over-engineering
|
|
67
68
|
|