@mindfoldhq/trellis 0.6.0-beta.6 → 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/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +8 -108
- package/dist/commands/update.js.map +1 -1
- 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.5.12.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.7.json +9 -0
- 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/markdown/spec/guides/cross-layer-thinking-guide.md.txt +29 -0
- 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
|
@@ -68,9 +68,8 @@ def _normalize_windows_shell_path(path_str: str) -> str:
|
|
|
68
68
|
|
|
69
69
|
|
|
70
70
|
FIRST_REPLY_NOTICE = """<first-reply-notice>
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
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.
|
|
71
|
+
First visible reply: say once in Chinese that Trellis SessionStart context is loaded, then answer directly.
|
|
72
|
+
This notice is one-shot: do not repeat it after the first assistant reply in the same session.
|
|
74
73
|
</first-reply-notice>"""
|
|
75
74
|
|
|
76
75
|
# IMPORTANT: Force stdout to use UTF-8 on Windows
|
|
@@ -136,6 +135,41 @@ def read_file(path: Path, fallback: str = "") -> str:
|
|
|
136
135
|
return fallback
|
|
137
136
|
|
|
138
137
|
|
|
138
|
+
def _repo_relative(repo_root: Path, path: Path) -> str:
|
|
139
|
+
try:
|
|
140
|
+
return path.relative_to(repo_root).as_posix()
|
|
141
|
+
except ValueError:
|
|
142
|
+
return str(path)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _run_git(repo_root: Path, args: list[str]) -> str:
|
|
146
|
+
try:
|
|
147
|
+
result = subprocess.run(
|
|
148
|
+
["git", *args],
|
|
149
|
+
capture_output=True,
|
|
150
|
+
text=True,
|
|
151
|
+
encoding="utf-8",
|
|
152
|
+
errors="replace",
|
|
153
|
+
timeout=3,
|
|
154
|
+
cwd=str(repo_root),
|
|
155
|
+
)
|
|
156
|
+
except (subprocess.TimeoutExpired, FileNotFoundError, PermissionError):
|
|
157
|
+
return ""
|
|
158
|
+
if result.returncode != 0:
|
|
159
|
+
return ""
|
|
160
|
+
return result.stdout.strip()
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _format_git_state(repo_root: Path) -> str:
|
|
164
|
+
branch = _run_git(repo_root, ["branch", "--show-current"]) or "(detached)"
|
|
165
|
+
dirty_lines = [
|
|
166
|
+
line for line in _run_git(repo_root, ["status", "--porcelain"]).splitlines()
|
|
167
|
+
if line.strip()
|
|
168
|
+
]
|
|
169
|
+
dirty_text = "clean" if not dirty_lines else f"dirty {len(dirty_lines)} paths"
|
|
170
|
+
return f"Git: branch {branch}; {dirty_text}."
|
|
171
|
+
|
|
172
|
+
|
|
139
173
|
def _detect_platform(input_data: dict) -> str | None:
|
|
140
174
|
if isinstance(input_data.get("cursor_version"), str):
|
|
141
175
|
return "cursor"
|
|
@@ -274,44 +308,26 @@ def _resolve_task_dir(trellis_dir: Path, task_ref: str) -> Path:
|
|
|
274
308
|
|
|
275
309
|
|
|
276
310
|
def _get_task_status(trellis_dir: Path, input_data: dict) -> str:
|
|
277
|
-
"""
|
|
278
|
-
|
|
279
|
-
Returns a block with three fields:
|
|
280
|
-
- Status: current state
|
|
281
|
-
- Task: task identifier (when applicable)
|
|
282
|
-
- Next-Action: explicit skill/command/tool call the AI should invoke
|
|
283
|
-
"""
|
|
311
|
+
"""Return compact active-task status, artifact presence, and next action."""
|
|
284
312
|
active = _resolve_active_task(trellis_dir, input_data)
|
|
285
313
|
|
|
286
|
-
# Case 1: No active task — waiting for user to describe intent
|
|
287
314
|
if not active.task_path:
|
|
288
315
|
return (
|
|
289
316
|
"Status: NO ACTIVE TASK\n"
|
|
290
|
-
|
|
291
|
-
"
|
|
292
|
-
"
|
|
293
|
-
"Research reminder: for research-heavy tasks (comparing tools, reading external docs, "
|
|
294
|
-
"cross-platform surveys), spawn `trellis-research` sub-agents via the Task tool — "
|
|
295
|
-
"they persist findings to `{TASK_DIR}/research/*.md` and keep main context clean. "
|
|
296
|
-
"Do NOT do 10+ inline WebFetch/WebSearch in the main conversation.\n"
|
|
297
|
-
"User override (per-turn escape hatch): if the user's first message explicitly opts "
|
|
298
|
-
"out of the workflow (\"跳过 trellis\" / \"别走流程\" / \"小修一下\" / \"直接改\" / "
|
|
299
|
-
"\"skip trellis\" / \"no task\" / \"just do it\"), honor it for this turn — "
|
|
300
|
-
"acknowledge briefly and proceed without creating a task. Per-turn only."
|
|
317
|
+
"Next-Action: Classify the current turn before creating any Trellis task. "
|
|
318
|
+
"Simple conversation / small task asks only whether this turn should create a Trellis task. "
|
|
319
|
+
"Complex task asks whether task creation and planning are allowed."
|
|
301
320
|
)
|
|
302
321
|
|
|
303
|
-
# Case 2: Stale pointer — task dir was deleted
|
|
304
322
|
task_ref = active.task_path
|
|
305
323
|
task_dir = _resolve_task_dir(trellis_dir, task_ref)
|
|
306
324
|
if active.stale or not task_dir.is_dir():
|
|
307
325
|
return (
|
|
308
326
|
f"Status: STALE POINTER\nTask: {task_ref}\n"
|
|
309
|
-
f"Source: {active.source}\n"
|
|
310
327
|
f"Next-Action: Run `python3 ./.trellis/scripts/task.py finish` to clear the stale pointer, "
|
|
311
328
|
"then ask the user what to work on next."
|
|
312
329
|
)
|
|
313
330
|
|
|
314
|
-
# Read task.json
|
|
315
331
|
task_json_path = task_dir / "task.json"
|
|
316
332
|
task_data = {}
|
|
317
333
|
if task_json_path.is_file():
|
|
@@ -322,62 +338,65 @@ def _get_task_status(trellis_dir: Path, input_data: dict) -> str:
|
|
|
322
338
|
|
|
323
339
|
task_title = task_data.get("title", task_ref)
|
|
324
340
|
task_status = task_data.get("status", "unknown")
|
|
341
|
+
artifact_names = ("prd.md", "design.md", "implement.md", "implement.jsonl", "check.jsonl")
|
|
342
|
+
present = [name for name in artifact_names if (task_dir / name).is_file()]
|
|
343
|
+
if (task_dir / "research").is_dir():
|
|
344
|
+
present.append("research/")
|
|
345
|
+
present_line = ", ".join(present) if present else "(none)"
|
|
325
346
|
|
|
326
|
-
# Case 3: Task completed — time to archive
|
|
327
347
|
if task_status == "completed":
|
|
328
348
|
return (
|
|
329
349
|
f"Status: COMPLETED\nTask: {task_title}\n"
|
|
330
|
-
f"
|
|
331
|
-
|
|
332
|
-
f"then archive with `python3 ./.trellis/scripts/task.py archive {task_dir.name}`."
|
|
350
|
+
f"Present: {present_line}\n"
|
|
351
|
+
"Next-Action: Run `/trellis:finish-work`. If the working tree is dirty, return to Phase 3.4 first."
|
|
333
352
|
)
|
|
334
353
|
|
|
335
354
|
has_prd = (task_dir / "prd.md").is_file()
|
|
355
|
+
has_design = (task_dir / "design.md").is_file()
|
|
356
|
+
has_implement_plan = (task_dir / "implement.md").is_file()
|
|
357
|
+
implement_jsonl = task_dir / "implement.jsonl"
|
|
358
|
+
check_jsonl = task_dir / "check.jsonl"
|
|
359
|
+
jsonl_ready = (
|
|
360
|
+
(not implement_jsonl.is_file() or _has_curated_jsonl_entry(implement_jsonl))
|
|
361
|
+
and (not check_jsonl.is_file() or _has_curated_jsonl_entry(check_jsonl))
|
|
362
|
+
)
|
|
336
363
|
|
|
337
|
-
|
|
338
|
-
if not has_prd:
|
|
364
|
+
if task_status == "planning" and not has_prd:
|
|
339
365
|
return (
|
|
340
366
|
f"Status: PLANNING\nTask: {task_title}\n"
|
|
341
|
-
f"
|
|
342
|
-
"Next-Action: Load
|
|
343
|
-
"and produce prd.md in the task directory.\n"
|
|
344
|
-
"Research reminder: when the task needs external research (tool comparison, docs, "
|
|
345
|
-
"conventions survey), spawn `trellis-research` sub-agents — don't WebFetch/WebSearch "
|
|
346
|
-
"inline in the main session. Findings go to `{task_dir}/research/*.md`; PRD only links to them."
|
|
367
|
+
f"Present: {present_line}\n"
|
|
368
|
+
"Next-Action: Load `trellis-brainstorm` and write `prd.md`. Stay in planning."
|
|
347
369
|
)
|
|
348
370
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
371
|
+
if task_status == "planning":
|
|
372
|
+
missing_complex = [
|
|
373
|
+
name for name, exists in (
|
|
374
|
+
("design.md", has_design),
|
|
375
|
+
("implement.md", has_implement_plan),
|
|
376
|
+
)
|
|
377
|
+
if not exists
|
|
378
|
+
]
|
|
379
|
+
next_bits: list[str] = []
|
|
380
|
+
if missing_complex:
|
|
381
|
+
next_bits.append(
|
|
382
|
+
"Lightweight task can request start review with PRD-only; "
|
|
383
|
+
f"complex task must add {', '.join(missing_complex)} before start"
|
|
384
|
+
)
|
|
385
|
+
else:
|
|
386
|
+
next_bits.append("Planning artifacts are present; ask for review before `task.py start`")
|
|
387
|
+
if not jsonl_ready:
|
|
388
|
+
next_bits.append("curate `implement.jsonl` and `check.jsonl` before sub-agent mode start")
|
|
352
389
|
return (
|
|
353
|
-
f"Status: PLANNING
|
|
354
|
-
f"
|
|
355
|
-
"Next-Action:
|
|
356
|
-
"the Phase 2 sub-agents will need. Only spec paths (`.trellis/spec/**/*.md`) and research "
|
|
357
|
-
"files (`{TASK_DIR}/research/*.md`) — no code paths. Run "
|
|
358
|
-
"`python3 ./.trellis/scripts/get_context.py --mode packages` to list available specs, "
|
|
359
|
-
"then edit the jsonl files or use `python3 ./.trellis/scripts/task.py add-context`. "
|
|
360
|
-
"See `.trellis/workflow.md` Phase 1.3 for details."
|
|
390
|
+
f"Status: PLANNING\nTask: {task_title}\n"
|
|
391
|
+
f"Present: {present_line}\n"
|
|
392
|
+
f"Next-Action: {'; '.join(next_bits)}. Do not enter implementation until the user confirms start."
|
|
361
393
|
)
|
|
362
394
|
|
|
363
|
-
# Case 5: PRD + curated jsonl (or agent-less platform with no jsonl) — enter Execute phase
|
|
364
395
|
return (
|
|
365
|
-
f"Status:
|
|
366
|
-
f"
|
|
367
|
-
"Next
|
|
368
|
-
"
|
|
369
|
-
"After implementation, dispatch `trellis-check` per Phase 2.2 before reporting completion.\n"
|
|
370
|
-
"Sub-agent roster: `trellis-implement` (writes code), `trellis-check` (verifies + self-fixes), "
|
|
371
|
-
"`trellis-research` (persists findings to `research/*.md` — use when you'd otherwise do "
|
|
372
|
-
"multiple WebFetch/WebSearch inline).\n"
|
|
373
|
-
"Sub-agent self-exemption: if you are reading this as a `trellis-implement` or "
|
|
374
|
-
"`trellis-check` sub-agent (your own role / agent name reflects that), this dispatch "
|
|
375
|
-
"instruction does NOT apply to you — you are already the dispatched sub-agent. "
|
|
376
|
-
"Implement / check directly without spawning another sub-agent of the same kind.\n"
|
|
377
|
-
"User override (per-turn escape hatch): if the user's CURRENT message explicitly tells the "
|
|
378
|
-
"main session to handle it directly (\"你直接改\" / \"别派 sub-agent\" / \"main session 写就行\" / "
|
|
379
|
-
"\"do it inline\" / \"不用 sub-agent\"), honor it for this turn and edit code directly. "
|
|
380
|
-
"Per-turn only; do NOT invent an override the user did not say."
|
|
396
|
+
f"Status: {str(task_status).upper()}\nTask: {task_title}\n"
|
|
397
|
+
f"Present: {present_line}\n"
|
|
398
|
+
"Next-Action: Follow the matching per-turn workflow-state. "
|
|
399
|
+
"Implementation/check context order is jsonl entries -> `prd.md` -> `design.md if present` -> `implement.md if present`."
|
|
381
400
|
)
|
|
382
401
|
|
|
383
402
|
|
|
@@ -530,6 +549,97 @@ def _resolve_spec_scope(
|
|
|
530
549
|
return None # Unknown scope type: full scan
|
|
531
550
|
|
|
532
551
|
|
|
552
|
+
def _collect_spec_index_paths(trellis_dir: Path, allowed_pkgs: set | None) -> list[str]:
|
|
553
|
+
paths: list[str] = []
|
|
554
|
+
guides_index = trellis_dir / "spec" / "guides" / "index.md"
|
|
555
|
+
if guides_index.is_file():
|
|
556
|
+
paths.append(".trellis/spec/guides/index.md")
|
|
557
|
+
|
|
558
|
+
spec_dir = trellis_dir / "spec"
|
|
559
|
+
if not spec_dir.is_dir():
|
|
560
|
+
return paths
|
|
561
|
+
|
|
562
|
+
for sub in sorted(spec_dir.iterdir()):
|
|
563
|
+
if not sub.is_dir() or sub.name.startswith(".") or sub.name == "guides":
|
|
564
|
+
continue
|
|
565
|
+
|
|
566
|
+
index_file = sub / "index.md"
|
|
567
|
+
if index_file.is_file():
|
|
568
|
+
paths.append(f".trellis/spec/{sub.name}/index.md")
|
|
569
|
+
continue
|
|
570
|
+
|
|
571
|
+
if allowed_pkgs is not None and sub.name not in allowed_pkgs:
|
|
572
|
+
continue
|
|
573
|
+
for nested in sorted(sub.iterdir()):
|
|
574
|
+
if not nested.is_dir():
|
|
575
|
+
continue
|
|
576
|
+
nested_index = nested / "index.md"
|
|
577
|
+
if nested_index.is_file():
|
|
578
|
+
paths.append(f".trellis/spec/{sub.name}/{nested.name}/index.md")
|
|
579
|
+
|
|
580
|
+
return paths
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
def _build_compact_current_state(
|
|
584
|
+
trellis_dir: Path,
|
|
585
|
+
input_data: dict,
|
|
586
|
+
spec_index_paths: list[str],
|
|
587
|
+
) -> str:
|
|
588
|
+
repo_root = trellis_dir.parent
|
|
589
|
+
lines: list[str] = []
|
|
590
|
+
|
|
591
|
+
try:
|
|
592
|
+
from common.paths import get_active_journal_file, get_developer, get_tasks_dir, count_lines # type: ignore[import-not-found]
|
|
593
|
+
from common.tasks import iter_active_tasks # type: ignore[import-not-found]
|
|
594
|
+
except Exception:
|
|
595
|
+
get_active_journal_file = None # type: ignore[assignment]
|
|
596
|
+
get_developer = None # type: ignore[assignment]
|
|
597
|
+
get_tasks_dir = None # type: ignore[assignment]
|
|
598
|
+
count_lines = None # type: ignore[assignment]
|
|
599
|
+
iter_active_tasks = None # type: ignore[assignment]
|
|
600
|
+
|
|
601
|
+
developer = get_developer(repo_root) if get_developer else None
|
|
602
|
+
lines.append(f"Developer: {developer or '(not initialized)'}")
|
|
603
|
+
lines.append(_format_git_state(repo_root))
|
|
604
|
+
|
|
605
|
+
active = _resolve_active_task(trellis_dir, input_data)
|
|
606
|
+
if active.task_path:
|
|
607
|
+
task_dir = _resolve_task_dir(trellis_dir, active.task_path)
|
|
608
|
+
status = "unknown"
|
|
609
|
+
task_json = task_dir / "task.json"
|
|
610
|
+
if task_json.is_file():
|
|
611
|
+
try:
|
|
612
|
+
data = json.loads(task_json.read_text(encoding="utf-8"))
|
|
613
|
+
if isinstance(data, dict):
|
|
614
|
+
status = str(data.get("status") or "unknown")
|
|
615
|
+
except (json.JSONDecodeError, OSError):
|
|
616
|
+
pass
|
|
617
|
+
lines.append(f"Current task: {_repo_relative(repo_root, task_dir)}; status={status}.")
|
|
618
|
+
else:
|
|
619
|
+
lines.append("Current task: none.")
|
|
620
|
+
|
|
621
|
+
if get_tasks_dir and iter_active_tasks:
|
|
622
|
+
try:
|
|
623
|
+
task_count = sum(1 for _ in iter_active_tasks(get_tasks_dir(repo_root)))
|
|
624
|
+
lines.append(
|
|
625
|
+
f"Active tasks: {task_count} total. Use `python3 ./.trellis/scripts/task.py list --mine` only if needed."
|
|
626
|
+
)
|
|
627
|
+
except Exception:
|
|
628
|
+
pass
|
|
629
|
+
|
|
630
|
+
if get_active_journal_file and count_lines:
|
|
631
|
+
journal = get_active_journal_file(repo_root)
|
|
632
|
+
if journal:
|
|
633
|
+
lines.append(
|
|
634
|
+
f"Journal: {_repo_relative(repo_root, journal)}, {count_lines(journal)} / 2000 lines."
|
|
635
|
+
)
|
|
636
|
+
|
|
637
|
+
if spec_index_paths:
|
|
638
|
+
lines.append(f"Spec indexes: {len(spec_index_paths)} available.")
|
|
639
|
+
|
|
640
|
+
return "\n".join(lines)
|
|
641
|
+
|
|
642
|
+
|
|
533
643
|
def _extract_range(content: str, start_header: str, end_header: str) -> str:
|
|
534
644
|
"""Extract lines starting at `## start_header` up to (but excluding) `## end_header`.
|
|
535
645
|
|
|
@@ -570,51 +680,25 @@ def _strip_breadcrumb_tag_blocks(content: str) -> str:
|
|
|
570
680
|
payload already covers the full step bodies, so re-inlining the
|
|
571
681
|
breadcrumbs here would just duplicate context.
|
|
572
682
|
"""
|
|
573
|
-
|
|
683
|
+
stripped = _BREADCRUMB_TAG_RE.sub("", content)
|
|
684
|
+
stripped = re.sub(r"<!--.*?-->", "", stripped, flags=re.DOTALL)
|
|
685
|
+
stripped = re.sub(r"^\[(?!/?workflow-state:)/?[^\]\n]+\]\s*\n?", "", stripped, flags=re.MULTILINE)
|
|
686
|
+
return re.sub(r"\n{3,}", "\n\n", stripped).strip()
|
|
574
687
|
|
|
575
688
|
|
|
576
689
|
def _build_workflow_overview(workflow_path: Path) -> str:
|
|
577
|
-
"""Inject the
|
|
578
|
-
|
|
579
|
-
Contents:
|
|
580
|
-
1. Section index (all `## ` headings — navigation)
|
|
581
|
-
2. Phase Index section (rules, skill routing table, anti-rationalization table)
|
|
582
|
-
3. Phase 1/2/3 step-level details (the actual how-to for each step)
|
|
583
|
-
|
|
584
|
-
The meta sections (Core Principles / Trellis System / Customizing
|
|
585
|
-
Trellis) are NOT injected — Core Principles is short prose the AI can
|
|
586
|
-
Read on demand; Trellis System lists reference commands duplicated in
|
|
587
|
-
step bodies; Customizing Trellis is for forks. Workflow-state breadcrumb
|
|
588
|
-
tag blocks (which now live inside Phase Index since v0.5.0-rc.0) are
|
|
589
|
-
stripped from the extracted range — they're consumed by the
|
|
590
|
-
UserPromptSubmit hook, not the session-start preamble.
|
|
591
|
-
|
|
592
|
-
Total budget: Phase Index ~2 KB + Phase 1/2/3 ~7 KB = ~9 KB.
|
|
593
|
-
"""
|
|
690
|
+
"""Inject only the compact Phase Index summary for SessionStart."""
|
|
594
691
|
content = read_file(workflow_path)
|
|
595
692
|
if not content:
|
|
596
693
|
return "No workflow.md found"
|
|
597
694
|
|
|
598
695
|
out_lines = [
|
|
599
|
-
"# Development Workflow
|
|
600
|
-
"Full guide: .trellis/workflow.md
|
|
696
|
+
"# Development Workflow - Session Summary",
|
|
697
|
+
"Full guide: .trellis/workflow.md. Step detail: `python3 ./.trellis/scripts/get_context.py --mode phase --step <X.Y>`.",
|
|
601
698
|
"",
|
|
602
|
-
"## Table of Contents",
|
|
603
699
|
]
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
out_lines.append(line)
|
|
607
|
-
out_lines += ["", "---", ""]
|
|
608
|
-
|
|
609
|
-
# Extract Phase Index through the end of Phase 3 (before "Customizing
|
|
610
|
-
# Trellis" — the docs-for-forks footer added in v0.5.0-rc.0). Since
|
|
611
|
-
# sections appear in order Phase Index → Phase 1 → Phase 2 → Phase 3 →
|
|
612
|
-
# Customizing Trellis, a single range grab captures all four. The
|
|
613
|
-
# breadcrumb tag blocks now embedded inside Phase Index are stripped so
|
|
614
|
-
# they don't duplicate the per-turn UserPromptSubmit injection.
|
|
615
|
-
phases = _extract_range(
|
|
616
|
-
content, "Phase Index", "Customizing Trellis (for forks)"
|
|
617
|
-
)
|
|
700
|
+
|
|
701
|
+
phases = _extract_range(content, "Phase Index", "Phase 1: Plan")
|
|
618
702
|
if phases:
|
|
619
703
|
out_lines.append(_strip_breadcrumb_tag_blocks(phases).rstrip())
|
|
620
704
|
|
|
@@ -665,9 +749,10 @@ def main():
|
|
|
665
749
|
|
|
666
750
|
output = StringIO()
|
|
667
751
|
|
|
752
|
+
spec_index_paths = _collect_spec_index_paths(trellis_dir, allowed_pkgs)
|
|
753
|
+
|
|
668
754
|
output.write("""<session-context>
|
|
669
|
-
|
|
670
|
-
Read and follow all instructions below carefully.
|
|
755
|
+
Trellis compact SessionStart context. Use it to orient the session; load details on demand.
|
|
671
756
|
</session-context>
|
|
672
757
|
|
|
673
758
|
""")
|
|
@@ -680,72 +765,23 @@ Read and follow all instructions below carefully.
|
|
|
680
765
|
output.write(f"<migration-warning>\n{legacy_warning}\n</migration-warning>\n\n")
|
|
681
766
|
|
|
682
767
|
output.write("<current-state>\n")
|
|
683
|
-
|
|
684
|
-
output.write(run_script(context_script, context_key))
|
|
768
|
+
output.write(_build_compact_current_state(trellis_dir, hook_input, spec_index_paths))
|
|
685
769
|
output.write("\n</current-state>\n\n")
|
|
686
770
|
|
|
687
|
-
output.write("<workflow>\n")
|
|
771
|
+
output.write("<trellis-workflow>\n")
|
|
688
772
|
output.write(_build_workflow_overview(trellis_dir / "workflow.md"))
|
|
689
|
-
output.write("\n</workflow>\n\n")
|
|
773
|
+
output.write("\n</trellis-workflow>\n\n")
|
|
690
774
|
|
|
691
775
|
output.write("<guidelines>\n")
|
|
692
776
|
output.write(
|
|
693
|
-
"
|
|
694
|
-
"
|
|
695
|
-
"
|
|
696
|
-
"- If you're spawning an implement/check sub-agent, context is injected "
|
|
697
|
-
"or loaded by the sub-agent via `{task}/implement.jsonl` / `check.jsonl`. "
|
|
698
|
-
"You do NOT need to read these indexes yourself.\n"
|
|
699
|
-
"- For agent-capable platforms, the default is to dispatch "
|
|
700
|
-
"`trellis-implement` and `trellis-check` (so JSONL context is loaded by "
|
|
701
|
-
"the sub-agents) rather than editing code in the main session. "
|
|
702
|
-
"Honor a per-turn user override only if the user's current message "
|
|
703
|
-
"explicitly opts out (see <task-status> below for override phrases).\n"
|
|
704
|
-
"- Sub-agent self-exemption: if you are reading this as a `trellis-implement` "
|
|
705
|
-
"or `trellis-check` sub-agent, the \"dispatch trellis-implement / trellis-check\" "
|
|
706
|
-
"rule above does NOT apply to you — you are already the dispatched sub-agent. "
|
|
707
|
-
"Do NOT spawn another sub-agent of the same kind; implement / check directly.\n\n"
|
|
777
|
+
"Task context order for implementation/check: jsonl entries -> `prd.md` -> "
|
|
778
|
+
"`design.md if present` -> `implement.md if present`. Missing optional artifacts "
|
|
779
|
+
"are skipped for lightweight tasks.\n\n"
|
|
708
780
|
)
|
|
709
781
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
output.write("## guides (inlined — cross-package thinking guides)\n")
|
|
714
|
-
output.write(read_file(guides_index))
|
|
715
|
-
output.write("\n\n")
|
|
716
|
-
|
|
717
|
-
# Other spec indexes — paths only (main agent reads on demand;
|
|
718
|
-
# sub-agents get their specific specs via jsonl injection)
|
|
719
|
-
paths: list[str] = []
|
|
720
|
-
spec_dir = trellis_dir / "spec"
|
|
721
|
-
if spec_dir.is_dir():
|
|
722
|
-
for sub in sorted(spec_dir.iterdir()):
|
|
723
|
-
if not sub.is_dir() or sub.name.startswith("."):
|
|
724
|
-
continue
|
|
725
|
-
if sub.name == "guides":
|
|
726
|
-
continue # already inlined above
|
|
727
|
-
|
|
728
|
-
index_file = sub / "index.md"
|
|
729
|
-
if index_file.is_file():
|
|
730
|
-
# Flat spec dir (single-repo layer like spec/backend/)
|
|
731
|
-
paths.append(f".trellis/spec/{sub.name}/index.md")
|
|
732
|
-
else:
|
|
733
|
-
# Nested package dirs (monorepo: spec/<pkg>/<layer>/index.md)
|
|
734
|
-
# Apply scope filter
|
|
735
|
-
if allowed_pkgs is not None and sub.name not in allowed_pkgs:
|
|
736
|
-
continue
|
|
737
|
-
for nested in sorted(sub.iterdir()):
|
|
738
|
-
if not nested.is_dir():
|
|
739
|
-
continue
|
|
740
|
-
nested_index = nested / "index.md"
|
|
741
|
-
if nested_index.is_file():
|
|
742
|
-
paths.append(
|
|
743
|
-
f".trellis/spec/{sub.name}/{nested.name}/index.md"
|
|
744
|
-
)
|
|
745
|
-
|
|
746
|
-
if paths:
|
|
747
|
-
output.write("## Available spec indexes (read on demand)\n")
|
|
748
|
-
for p in paths:
|
|
782
|
+
if spec_index_paths:
|
|
783
|
+
output.write("## Available indexes (read on demand)\n")
|
|
784
|
+
for p in spec_index_paths:
|
|
749
785
|
output.write(f"- {p}\n")
|
|
750
786
|
output.write("\n")
|
|
751
787
|
|
|
@@ -760,9 +796,7 @@ Read and follow all instructions below carefully.
|
|
|
760
796
|
output.write(f"<task-status>\n{task_status}\n</task-status>\n\n")
|
|
761
797
|
|
|
762
798
|
output.write("""<ready>
|
|
763
|
-
Context loaded.
|
|
764
|
-
When the user sends the first message, follow <task-status> and the workflow guide.
|
|
765
|
-
If a task is READY, execute its Next required action without asking whether to continue.
|
|
799
|
+
Context loaded. Follow <task-status>. Load workflow/spec/task details only when needed.
|
|
766
800
|
</ready>""")
|
|
767
801
|
|
|
768
802
|
result = {
|
|
@@ -10,9 +10,9 @@ Provides:
|
|
|
10
10
|
Note:
|
|
11
11
|
``cmd_init_context`` was removed in v0.5.0-beta.12. JSONL context files
|
|
12
12
|
are now seeded at ``task.py create`` time with a self-describing
|
|
13
|
-
``_example`` line; the AI agent curates real entries during
|
|
14
|
-
the
|
|
15
|
-
|
|
13
|
+
``_example`` line; the AI agent curates real entries during planning when
|
|
14
|
+
the task needs sub-agent/spec context. See ``.trellis/workflow.md`` for the
|
|
15
|
+
current planning artifact contract.
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
18
|
from __future__ import annotations
|
|
@@ -138,6 +138,32 @@ def _write_seed_jsonl(path: Path) -> None:
|
|
|
138
138
|
path.write_text(json.dumps(seed, ensure_ascii=False) + "\n", encoding="utf-8")
|
|
139
139
|
|
|
140
140
|
|
|
141
|
+
def _default_prd_content(title: str, description: str | None = None) -> str:
|
|
142
|
+
"""Return the default PRD skeleton created with every task."""
|
|
143
|
+
goal = (description or "").strip() or "TBD."
|
|
144
|
+
heading = title.strip() or "Untitled task"
|
|
145
|
+
return f"""# {heading}
|
|
146
|
+
|
|
147
|
+
## Goal
|
|
148
|
+
|
|
149
|
+
{goal}
|
|
150
|
+
|
|
151
|
+
## Requirements
|
|
152
|
+
|
|
153
|
+
- TBD
|
|
154
|
+
|
|
155
|
+
## Acceptance Criteria
|
|
156
|
+
|
|
157
|
+
- [ ] TBD
|
|
158
|
+
|
|
159
|
+
## Notes
|
|
160
|
+
|
|
161
|
+
- Keep `prd.md` focused on requirements, constraints, and acceptance criteria.
|
|
162
|
+
- Lightweight tasks can remain PRD-only.
|
|
163
|
+
- For complex tasks, add `design.md` for technical design and `implement.md` for execution planning before `task.py start`.
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
|
|
141
167
|
# =============================================================================
|
|
142
168
|
# Command: create
|
|
143
169
|
# =============================================================================
|
|
@@ -233,8 +259,15 @@ def cmd_create(args: argparse.Namespace) -> int:
|
|
|
233
259
|
|
|
234
260
|
write_json(task_json_path, task_data)
|
|
235
261
|
|
|
262
|
+
prd_path = task_dir / "prd.md"
|
|
263
|
+
if not prd_path.exists():
|
|
264
|
+
prd_path.write_text(
|
|
265
|
+
_default_prd_content(args.title, args.description),
|
|
266
|
+
encoding="utf-8",
|
|
267
|
+
)
|
|
268
|
+
|
|
236
269
|
# Seed implement.jsonl / check.jsonl for sub-agent-capable platforms.
|
|
237
|
-
# Agent curates real entries
|
|
270
|
+
# Agent curates real entries during planning when the task needs them.
|
|
238
271
|
# Agent-less platforms (Kilo / Antigravity / Windsurf) skip this — they
|
|
239
272
|
# load specs via the trellis-before-dev skill instead of JSONL.
|
|
240
273
|
seeded_jsonl = False
|
|
@@ -286,16 +319,15 @@ def cmd_create(args: argparse.Namespace) -> int:
|
|
|
286
319
|
print(colored(f"Created task: {dir_name}", Colors.GREEN), file=sys.stderr)
|
|
287
320
|
print("", file=sys.stderr)
|
|
288
321
|
print(colored("Next steps:", Colors.BLUE), file=sys.stderr)
|
|
289
|
-
print("
|
|
322
|
+
print(" - Fill prd.md with requirements and acceptance criteria", file=sys.stderr)
|
|
323
|
+
print(" - Lightweight task: PRD-only is valid", file=sys.stderr)
|
|
324
|
+
print(" - Complex task: add design.md and implement.md before task.py start", file=sys.stderr)
|
|
290
325
|
if seeded_jsonl:
|
|
291
326
|
print(
|
|
292
|
-
"
|
|
293
|
-
"see .trellis/workflow.md Phase 1.3)",
|
|
327
|
+
" - Curate implement.jsonl / check.jsonl as spec/research manifests when sub-agents need context",
|
|
294
328
|
file=sys.stderr,
|
|
295
329
|
)
|
|
296
|
-
|
|
297
|
-
else:
|
|
298
|
-
print(" 2. Run: python3 task.py start <dir>", file=sys.stderr)
|
|
330
|
+
print(" - Use /trellis:continue or phase context to decide the next step", file=sys.stderr)
|
|
299
331
|
print("", file=sys.stderr)
|
|
300
332
|
|
|
301
333
|
# Output relative path for script chaining
|
|
@@ -60,15 +60,12 @@ def _parse_marker(line: str) -> tuple[bool, list[str]] | None:
|
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
def get_phase_index() -> str:
|
|
63
|
-
"""Return
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
`[workflow-state:STATUS]` tag blocks (now embedded in Phase Index since
|
|
70
|
-
v0.5.0-rc.0) are consumed by the UserPromptSubmit hook so they're
|
|
71
|
-
stripped from this output.
|
|
63
|
+
"""Return the compact Phase Index summary from workflow.md.
|
|
64
|
+
|
|
65
|
+
SessionStart and no-step phase context use this small summary as their
|
|
66
|
+
orientation payload. Detailed Phase 1/2/3 instructions are loaded with
|
|
67
|
+
``get_step`` on demand. ``[workflow-state:STATUS]`` tag blocks are
|
|
68
|
+
consumed by the per-turn hook, so they're stripped from this output.
|
|
72
69
|
"""
|
|
73
70
|
text = _read_workflow()
|
|
74
71
|
lines = text.splitlines()
|
|
@@ -80,7 +77,7 @@ def get_phase_index() -> str:
|
|
|
80
77
|
if start is None and stripped == _PHASE_INDEX_HEADING:
|
|
81
78
|
start = i
|
|
82
79
|
continue
|
|
83
|
-
if start is not None and stripped == "##
|
|
80
|
+
if start is not None and stripped == "## Phase 1: Plan":
|
|
84
81
|
end = i
|
|
85
82
|
break
|
|
86
83
|
|
|
@@ -369,12 +369,12 @@ def main() -> int:
|
|
|
369
369
|
file=sys.stderr,
|
|
370
370
|
)
|
|
371
371
|
print(
|
|
372
|
-
"sub-agent-capable platforms and curated by the AI during
|
|
372
|
+
"sub-agent-capable platforms and curated by the AI during planning when needed.",
|
|
373
373
|
file=sys.stderr,
|
|
374
374
|
)
|
|
375
|
-
print("See .trellis/workflow.md
|
|
375
|
+
print("See .trellis/workflow.md planning artifact guidance or run:", file=sys.stderr)
|
|
376
376
|
print(
|
|
377
|
-
" python3 ./.trellis/scripts/get_context.py --mode phase --step 1
|
|
377
|
+
" python3 ./.trellis/scripts/get_context.py --mode phase --step 1",
|
|
378
378
|
file=sys.stderr,
|
|
379
379
|
)
|
|
380
380
|
print(
|