@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.
Files changed (64) hide show
  1. package/dist/commands/update.d.ts.map +1 -1
  2. package/dist/commands/update.js +8 -108
  3. package/dist/commands/update.js.map +1 -1
  4. package/dist/configurators/codex.d.ts.map +1 -1
  5. package/dist/configurators/codex.js +5 -3
  6. package/dist/configurators/codex.js.map +1 -1
  7. package/dist/configurators/shared.js +4 -4
  8. package/dist/configurators/shared.js.map +1 -1
  9. package/dist/migrations/manifests/0.5.12.json +9 -0
  10. package/dist/migrations/manifests/0.6.0-beta.7.json +9 -0
  11. package/dist/migrations/manifests/0.6.0-beta.8.json +9 -0
  12. package/dist/templates/claude/agents/trellis-check.md +2 -2
  13. package/dist/templates/claude/agents/trellis-implement.md +8 -7
  14. package/dist/templates/codebuddy/agents/trellis-check.md +2 -2
  15. package/dist/templates/codebuddy/agents/trellis-implement.md +8 -7
  16. package/dist/templates/codex/agents/trellis-check.toml +4 -4
  17. package/dist/templates/codex/agents/trellis-implement.toml +4 -4
  18. package/dist/templates/codex/hooks/session-start.py +183 -119
  19. package/dist/templates/codex/skills/before-dev/SKILL.md +12 -6
  20. package/dist/templates/codex/skills/brainstorm/SKILL.md +113 -51
  21. package/dist/templates/codex/skills/check/SKILL.md +86 -18
  22. package/dist/templates/codex/skills/start/SKILL.md +33 -323
  23. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-context-loading.md +7 -4
  24. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-spec-structure.md +1 -1
  25. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-workflow.md +3 -2
  26. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/context-injection.md +5 -5
  27. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/spec-system.md +1 -1
  28. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/task-system.md +8 -6
  29. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/agents.md +5 -4
  30. package/dist/templates/common/commands/continue.md +6 -5
  31. package/dist/templates/common/commands/start.md +7 -6
  32. package/dist/templates/common/skills/before-dev.md +12 -6
  33. package/dist/templates/common/skills/brainstorm.md +56 -42
  34. package/dist/templates/common/skills/check.md +7 -1
  35. package/dist/templates/copilot/hooks/session-start.py +183 -90
  36. package/dist/templates/copilot/prompts/before-dev.prompt.md +12 -6
  37. package/dist/templates/copilot/prompts/brainstorm.prompt.md +146 -84
  38. package/dist/templates/copilot/prompts/check.prompt.md +86 -18
  39. package/dist/templates/copilot/prompts/parallel.prompt.md +16 -8
  40. package/dist/templates/copilot/prompts/start.prompt.md +33 -367
  41. package/dist/templates/cursor/agents/trellis-check.md +2 -2
  42. package/dist/templates/cursor/agents/trellis-implement.md +8 -7
  43. package/dist/templates/droid/droids/trellis-check.md +2 -2
  44. package/dist/templates/droid/droids/trellis-implement.md +8 -7
  45. package/dist/templates/gemini/agents/trellis-implement.md +7 -6
  46. package/dist/templates/kiro/agents/trellis-check.json +1 -1
  47. package/dist/templates/kiro/agents/trellis-implement.json +1 -1
  48. package/dist/templates/markdown/spec/guides/cross-layer-thinking-guide.md.txt +29 -0
  49. package/dist/templates/opencode/agents/trellis-check.md +2 -2
  50. package/dist/templates/opencode/agents/trellis-implement.md +9 -8
  51. package/dist/templates/opencode/lib/session-utils.js +212 -123
  52. package/dist/templates/opencode/plugins/inject-subagent-context.js +23 -7
  53. package/dist/templates/opencode/plugins/inject-workflow-state.js +1 -4
  54. package/dist/templates/pi/extensions/trellis/index.ts.txt +7 -5
  55. package/dist/templates/qoder/agents/trellis-implement.md +7 -6
  56. package/dist/templates/shared-hooks/inject-subagent-context.py +36 -14
  57. package/dist/templates/shared-hooks/inject-workflow-state.py +18 -42
  58. package/dist/templates/shared-hooks/session-start.py +197 -163
  59. package/dist/templates/trellis/scripts/common/task_context.py +3 -3
  60. package/dist/templates/trellis/scripts/common/task_store.py +39 -7
  61. package/dist/templates/trellis/scripts/common/workflow_phase.py +7 -10
  62. package/dist/templates/trellis/scripts/task.py +3 -3
  63. package/dist/templates/trellis/workflow.md +98 -98
  64. 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
- On the first visible assistant reply in this session, begin with exactly one short Chinese sentence:
72
- Trellis SessionStart 已注入:workflow、当前任务状态、开发者身份、git 状态、active tasks、spec 索引已加载。
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
- """Check current task status and return structured status string with explicit next action.
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
- f"Source: {active.source}\n"
291
- "Next-Action: After the user describes their intent, load skill `trellis-brainstorm` "
292
- "to clarify requirements and create a task via `python3 ./.trellis/scripts/task.py create`.\n"
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"Source: {active.source}\n"
331
- f"Next-Action: Load skill `trellis-update-spec` to capture learnings, "
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
- # Case 4: No PRD still in Plan phase
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"Source: {active.source}\n"
342
- "Next-Action: Load skill `trellis-brainstorm` to clarify requirements with the user "
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
- # Case 4b: PRD exists but implement.jsonl has only seed (no curated entries) — Phase 1.3 gate
350
- implement_jsonl = task_dir / "implement.jsonl"
351
- if implement_jsonl.is_file() and not _has_curated_jsonl_entry(implement_jsonl):
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 (Phase 1.3)\nTask: {task_title}\n"
354
- f"Source: {active.source}\n"
355
- "Next-Action: Curate `implement.jsonl` and `check.jsonl` with the spec + research files "
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: READY\nTask: {task_title}\n"
366
- f"Source: {active.source}\n"
367
- "Next required action: dispatch `trellis-implement` per Phase 2.1. "
368
- "For agent-capable platforms, the default is to NOT edit code in the main session. "
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
- return _BREADCRUMB_TAG_RE.sub("", content)
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 workflow guide for the session.
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 Section Index",
600
- "Full guide: .trellis/workflow.md (read on demand)",
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
- for line in content.splitlines():
605
- if line.startswith("## "):
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
- You are starting a new session in a Trellis-managed project.
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
- context_script = trellis_dir / "scripts" / "get_context.py"
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
- "Project spec indexes are listed by path below. Each index contains a "
694
- "**Pre-Development Checklist** listing the specific guideline files to "
695
- "read before coding.\n\n"
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
- # guides/ is cross-package thinking — always include inline (small, broadly useful)
711
- guides_index = trellis_dir / "spec" / "guides" / "index.md"
712
- if guides_index.is_file():
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. Workflow index, project state, and guidelines are already injected above — do NOT re-read them.
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 Phase 1.3 of
14
- the workflow. See ``.trellis/workflow.md`` Phase 1.3 for the current
15
- instructions.
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 in Phase 1.3 (see .trellis/workflow.md).
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(" 1. Create prd.md with requirements", file=sys.stderr)
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
- " 2. Curate implement.jsonl / check.jsonl (spec + research files only "
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
- print(" 3. Run: python3 task.py start <dir>", file=sys.stderr)
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 Phase Index + Phase 1/2/3 step bodies from workflow.md.
64
-
65
- Matches what the SessionStart hook injects into the `<workflow>` block:
66
- starts at `## Phase Index`, continues through `## Phase 1: Plan`,
67
- `## Phase 2: Execute`, `## Phase 3: Finish`, stops at
68
- `## Customizing Trellis (for forks)` (the docs-for-forks footer).
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 == "## Customizing Trellis (for forks)":
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 Phase 1.3.",
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 Phase 1.3 or run:", file=sys.stderr)
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.3",
377
+ " python3 ./.trellis/scripts/get_context.py --mode phase --step 1",
378
378
  file=sys.stderr,
379
379
  )
380
380
  print(