@mindfoldhq/trellis 0.5.0-beta.8 → 0.5.0-rc.0

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 (210) hide show
  1. package/README.md +60 -95
  2. package/dist/cli/index.js +7 -0
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/commands/init.d.ts +13 -0
  5. package/dist/commands/init.d.ts.map +1 -1
  6. package/dist/commands/init.js +474 -210
  7. package/dist/commands/init.js.map +1 -1
  8. package/dist/commands/update.d.ts.map +1 -1
  9. package/dist/commands/update.js +295 -54
  10. package/dist/commands/update.js.map +1 -1
  11. package/dist/configurators/antigravity.d.ts.map +1 -1
  12. package/dist/configurators/antigravity.js +2 -8
  13. package/dist/configurators/antigravity.js.map +1 -1
  14. package/dist/configurators/claude.d.ts.map +1 -1
  15. package/dist/configurators/claude.js +4 -10
  16. package/dist/configurators/claude.js.map +1 -1
  17. package/dist/configurators/codebuddy.d.ts.map +1 -1
  18. package/dist/configurators/codebuddy.js +3 -3
  19. package/dist/configurators/codebuddy.js.map +1 -1
  20. package/dist/configurators/codex.d.ts.map +1 -1
  21. package/dist/configurators/codex.js +5 -13
  22. package/dist/configurators/codex.js.map +1 -1
  23. package/dist/configurators/copilot.d.ts.map +1 -1
  24. package/dist/configurators/copilot.js +5 -19
  25. package/dist/configurators/copilot.js.map +1 -1
  26. package/dist/configurators/cursor.d.ts.map +1 -1
  27. package/dist/configurators/cursor.js +3 -3
  28. package/dist/configurators/cursor.js.map +1 -1
  29. package/dist/configurators/droid.d.ts.map +1 -1
  30. package/dist/configurators/droid.js +3 -3
  31. package/dist/configurators/droid.js.map +1 -1
  32. package/dist/configurators/gemini.d.ts.map +1 -1
  33. package/dist/configurators/gemini.js +3 -5
  34. package/dist/configurators/gemini.js.map +1 -1
  35. package/dist/configurators/index.d.ts.map +1 -1
  36. package/dist/configurators/index.js +44 -55
  37. package/dist/configurators/index.js.map +1 -1
  38. package/dist/configurators/kilo.d.ts.map +1 -1
  39. package/dist/configurators/kilo.js +2 -8
  40. package/dist/configurators/kilo.js.map +1 -1
  41. package/dist/configurators/kiro.d.ts.map +1 -1
  42. package/dist/configurators/kiro.js +3 -3
  43. package/dist/configurators/kiro.js.map +1 -1
  44. package/dist/configurators/opencode.d.ts.map +1 -1
  45. package/dist/configurators/opencode.js +7 -4
  46. package/dist/configurators/opencode.js.map +1 -1
  47. package/dist/configurators/pi.d.ts +3 -0
  48. package/dist/configurators/pi.d.ts.map +1 -0
  49. package/dist/configurators/pi.js +44 -0
  50. package/dist/configurators/pi.js.map +1 -0
  51. package/dist/configurators/qoder.d.ts +7 -6
  52. package/dist/configurators/qoder.d.ts.map +1 -1
  53. package/dist/configurators/qoder.js +18 -12
  54. package/dist/configurators/qoder.js.map +1 -1
  55. package/dist/configurators/shared.d.ts +30 -6
  56. package/dist/configurators/shared.d.ts.map +1 -1
  57. package/dist/configurators/shared.js +65 -15
  58. package/dist/configurators/shared.js.map +1 -1
  59. package/dist/configurators/windsurf.d.ts.map +1 -1
  60. package/dist/configurators/windsurf.js +2 -8
  61. package/dist/configurators/windsurf.js.map +1 -1
  62. package/dist/constants/paths.d.ts +2 -0
  63. package/dist/constants/paths.d.ts.map +1 -1
  64. package/dist/constants/paths.js +2 -0
  65. package/dist/constants/paths.js.map +1 -1
  66. package/dist/migrations/manifests/0.5.0-beta.0.json +2 -0
  67. package/dist/migrations/manifests/0.5.0-beta.10.json +9 -0
  68. package/dist/migrations/manifests/0.5.0-beta.11.json +9 -0
  69. package/dist/migrations/manifests/0.5.0-beta.12.json +9 -0
  70. package/dist/migrations/manifests/0.5.0-beta.13.json +9 -0
  71. package/dist/migrations/manifests/0.5.0-beta.14.json +9 -0
  72. package/dist/migrations/manifests/0.5.0-beta.15.json +116 -0
  73. package/dist/migrations/manifests/0.5.0-beta.16.json +9 -0
  74. package/dist/migrations/manifests/0.5.0-beta.17.json +9 -0
  75. package/dist/migrations/manifests/0.5.0-beta.18.json +9 -0
  76. package/dist/migrations/manifests/0.5.0-beta.19.json +9 -0
  77. package/dist/migrations/manifests/0.5.0-beta.5.json +2 -0
  78. package/dist/migrations/manifests/0.5.0-beta.9.json +48 -0
  79. package/dist/migrations/manifests/0.5.0-rc.0.json +9 -0
  80. package/dist/templates/claude/agents/trellis-research.md +1 -1
  81. package/dist/templates/claude/settings.json +0 -4
  82. package/dist/templates/codebuddy/agents/trellis-research.md +1 -1
  83. package/dist/templates/codex/agents/trellis-research.toml +3 -2
  84. package/dist/templates/codex/hooks/session-start.py +126 -26
  85. package/dist/templates/codex/skills/finish-work/SKILL.md +41 -109
  86. package/dist/templates/codex/skills/start/SKILL.md +12 -9
  87. package/dist/templates/common/bundled-skills/trellis-meta/SKILL.md +73 -0
  88. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/add-project-local-conventions.md +83 -0
  89. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-agents.md +54 -0
  90. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-context-loading.md +81 -0
  91. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-hooks.md +57 -0
  92. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-skills-or-commands.md +78 -0
  93. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-spec-structure.md +83 -0
  94. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-task-lifecycle.md +90 -0
  95. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-workflow.md +64 -0
  96. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/overview.md +55 -0
  97. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/context-injection.md +68 -0
  98. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/generated-files.md +80 -0
  99. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/overview.md +51 -0
  100. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/spec-system.md +102 -0
  101. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/task-system.md +101 -0
  102. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/workflow.md +75 -0
  103. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/workspace-memory.md +71 -0
  104. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/agents.md +79 -0
  105. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/hooks-and-settings.md +69 -0
  106. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/overview.md +59 -0
  107. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/platform-map.md +74 -0
  108. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/skills-and-commands.md +83 -0
  109. package/dist/templates/common/commands/continue.md +9 -5
  110. package/dist/templates/common/commands/finish-work.md +34 -10
  111. package/dist/templates/common/index.d.ts +22 -2
  112. package/dist/templates/common/index.d.ts.map +1 -1
  113. package/dist/templates/common/index.js +53 -4
  114. package/dist/templates/common/index.js.map +1 -1
  115. package/dist/templates/common/skills/brainstorm.md +50 -4
  116. package/dist/templates/copilot/hooks/session-start.py +127 -30
  117. package/dist/templates/copilot/prompts/finish-work.prompt.md +44 -112
  118. package/dist/templates/copilot/prompts/start.prompt.md +12 -9
  119. package/dist/templates/cursor/agents/trellis-check.md +1 -1
  120. package/dist/templates/cursor/agents/trellis-implement.md +1 -1
  121. package/dist/templates/cursor/agents/trellis-research.md +2 -2
  122. package/dist/templates/cursor/hooks.json +7 -1
  123. package/dist/templates/droid/droids/trellis-research.md +1 -1
  124. package/dist/templates/extract.d.ts +6 -0
  125. package/dist/templates/extract.d.ts.map +1 -1
  126. package/dist/templates/extract.js +14 -0
  127. package/dist/templates/extract.js.map +1 -1
  128. package/dist/templates/gemini/agents/trellis-research.md +1 -1
  129. package/dist/templates/kiro/agents/trellis-research.json +1 -1
  130. package/dist/templates/markdown/agents.md +19 -12
  131. package/dist/templates/markdown/gitignore.txt +3 -0
  132. package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +24 -0
  133. package/dist/templates/opencode/agents/trellis-check.md +1 -1
  134. package/dist/templates/opencode/agents/trellis-implement.md +7 -4
  135. package/dist/templates/opencode/agents/trellis-research.md +2 -2
  136. package/dist/templates/opencode/lib/trellis-context.js +100 -13
  137. package/dist/templates/opencode/plugins/inject-subagent-context.js +70 -5
  138. package/dist/templates/opencode/plugins/inject-workflow-state.js +38 -44
  139. package/dist/templates/opencode/plugins/session-start.js +76 -31
  140. package/dist/templates/pi/agents/trellis-check.md +28 -0
  141. package/dist/templates/pi/agents/trellis-implement.md +33 -0
  142. package/dist/templates/pi/agents/trellis-research.md +25 -0
  143. package/dist/templates/pi/extensions/trellis/index.ts.txt +997 -0
  144. package/dist/templates/pi/index.d.ts +5 -0
  145. package/dist/templates/pi/index.d.ts.map +1 -0
  146. package/dist/templates/pi/index.js +12 -0
  147. package/dist/templates/pi/index.js.map +1 -0
  148. package/dist/templates/pi/settings.json +12 -0
  149. package/dist/templates/qoder/agents/trellis-research.md +1 -1
  150. package/dist/templates/shared-hooks/index.d.ts +31 -0
  151. package/dist/templates/shared-hooks/index.d.ts.map +1 -1
  152. package/dist/templates/shared-hooks/index.js +59 -0
  153. package/dist/templates/shared-hooks/index.js.map +1 -1
  154. package/dist/templates/shared-hooks/inject-shell-session-context.py +180 -0
  155. package/dist/templates/shared-hooks/inject-subagent-context.py +156 -27
  156. package/dist/templates/shared-hooks/inject-workflow-state.py +85 -92
  157. package/dist/templates/shared-hooks/session-start.py +232 -36
  158. package/dist/templates/trellis/config.yaml +6 -0
  159. package/dist/templates/trellis/gitignore.txt +3 -0
  160. package/dist/templates/trellis/index.d.ts +1 -1
  161. package/dist/templates/trellis/index.d.ts.map +1 -1
  162. package/dist/templates/trellis/index.js +2 -2
  163. package/dist/templates/trellis/index.js.map +1 -1
  164. package/dist/templates/trellis/scripts/common/__init__.py +8 -0
  165. package/dist/templates/trellis/scripts/common/active_task.py +593 -0
  166. package/dist/templates/trellis/scripts/common/cli_adapter.py +72 -14
  167. package/dist/templates/trellis/scripts/common/paths.py +61 -58
  168. package/dist/templates/trellis/scripts/common/session_context.py +12 -0
  169. package/dist/templates/trellis/scripts/common/task_context.py +27 -194
  170. package/dist/templates/trellis/scripts/common/task_store.py +102 -26
  171. package/dist/templates/trellis/scripts/common/tasks.py +4 -1
  172. package/dist/templates/trellis/scripts/common/types.py +0 -2
  173. package/dist/templates/trellis/scripts/common/workflow_phase.py +15 -3
  174. package/dist/templates/trellis/scripts/task.py +99 -34
  175. package/dist/templates/trellis/workflow.md +332 -64
  176. package/dist/types/ai-tools.d.ts +12 -3
  177. package/dist/types/ai-tools.d.ts.map +1 -1
  178. package/dist/types/ai-tools.js +29 -0
  179. package/dist/types/ai-tools.js.map +1 -1
  180. package/dist/utils/file-writer.d.ts.map +1 -1
  181. package/dist/utils/file-writer.js +7 -2
  182. package/dist/utils/file-writer.js.map +1 -1
  183. package/dist/utils/posix.d.ts +13 -0
  184. package/dist/utils/posix.d.ts.map +1 -0
  185. package/dist/utils/posix.js +15 -0
  186. package/dist/utils/posix.js.map +1 -0
  187. package/dist/utils/project-detector.d.ts +2 -0
  188. package/dist/utils/project-detector.d.ts.map +1 -1
  189. package/dist/utils/project-detector.js +120 -11
  190. package/dist/utils/project-detector.js.map +1 -1
  191. package/dist/utils/task-json.d.ts +46 -0
  192. package/dist/utils/task-json.d.ts.map +1 -0
  193. package/dist/utils/task-json.js +49 -0
  194. package/dist/utils/task-json.js.map +1 -0
  195. package/dist/utils/template-fetcher.d.ts +22 -6
  196. package/dist/utils/template-fetcher.d.ts.map +1 -1
  197. package/dist/utils/template-fetcher.js +405 -27
  198. package/dist/utils/template-fetcher.js.map +1 -1
  199. package/dist/utils/template-hash.d.ts +22 -3
  200. package/dist/utils/template-hash.d.ts.map +1 -1
  201. package/dist/utils/template-hash.js +99 -19
  202. package/dist/utils/template-hash.js.map +1 -1
  203. package/package.json +7 -7
  204. package/dist/templates/markdown/spec/backend/directory-structure.md +0 -292
  205. package/dist/templates/markdown/spec/backend/index.md +0 -40
  206. package/dist/templates/markdown/spec/backend/script-conventions.md +0 -742
  207. package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md +0 -118
  208. package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md +0 -394
  209. package/dist/templates/shared-hooks/statusline.py +0 -218
  210. package/dist/templates/trellis/scripts/create_bootstrap.py +0 -298
@@ -3,210 +3,29 @@
3
3
  Task JSONL context management.
4
4
 
5
5
  Provides:
6
- cmd_init_context - Initialize JSONL context files for a task
7
6
  cmd_add_context - Add entry to JSONL context file
8
7
  cmd_validate - Validate JSONL context files
9
8
  cmd_list_context - List JSONL context entries
9
+
10
+ Note:
11
+ ``cmd_init_context`` was removed in v0.5.0-beta.12. JSONL context files
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.
10
16
  """
11
17
 
12
18
  from __future__ import annotations
13
19
 
14
20
  import argparse
15
21
  import json
16
- import sys
17
22
  from pathlib import Path
18
23
 
19
- from .cli_adapter import get_cli_adapter_auto
20
- from .config import (
21
- get_packages,
22
- is_monorepo,
23
- resolve_package,
24
- validate_package,
25
- )
26
- from .io import read_json, write_json
27
24
  from .log import Colors, colored
28
- from .paths import (
29
- DIR_SPEC,
30
- DIR_WORKFLOW,
31
- FILE_TASK_JSON,
32
- get_repo_root,
33
- )
25
+ from .paths import get_repo_root
34
26
  from .task_utils import resolve_task_dir
35
27
 
36
28
 
37
- # =============================================================================
38
- # JSONL Default Content Generators
39
- # =============================================================================
40
-
41
- def get_implement_base() -> list[dict]:
42
- """Get base implement context entries."""
43
- return [
44
- {"file": f"{DIR_WORKFLOW}/workflow.md", "reason": "Project workflow and conventions"},
45
- ]
46
-
47
-
48
- def get_implement_backend(package: str | None = None) -> list[dict]:
49
- """Get backend implement context entries."""
50
- spec_base = f"{DIR_SPEC}/{package}" if package else DIR_SPEC
51
- return [
52
- {"file": f"{DIR_WORKFLOW}/{spec_base}/backend/index.md", "reason": "Backend development guide"},
53
- ]
54
-
55
-
56
- def get_implement_frontend(package: str | None = None) -> list[dict]:
57
- """Get frontend implement context entries."""
58
- spec_base = f"{DIR_SPEC}/{package}" if package else DIR_SPEC
59
- return [
60
- {"file": f"{DIR_WORKFLOW}/{spec_base}/frontend/index.md", "reason": "Frontend development guide"},
61
- ]
62
-
63
-
64
- def get_check_context(repo_root: Path) -> list[dict]:
65
- """Get check context entries."""
66
- adapter = get_cli_adapter_auto(repo_root)
67
-
68
- return [
69
- {"file": adapter.get_trellis_command_path("finish-work"), "reason": "Finish work checklist"},
70
- {"file": adapter.get_trellis_command_path("check"), "reason": "Code quality check spec"},
71
- ]
72
-
73
-
74
- def _write_jsonl(path: Path, entries: list[dict]) -> None:
75
- """Write entries to JSONL file."""
76
- lines = [json.dumps(entry, ensure_ascii=False) for entry in entries]
77
- path.write_text("\n".join(lines) + "\n", encoding="utf-8")
78
-
79
-
80
- # =============================================================================
81
- # Command: init-context
82
- # =============================================================================
83
-
84
- def cmd_init_context(args: argparse.Namespace) -> int:
85
- """Initialize JSONL context files for a task."""
86
- repo_root = get_repo_root()
87
- target_dir = resolve_task_dir(args.dir, repo_root)
88
- dev_type = args.type
89
-
90
- if not dev_type:
91
- print(colored("Error: Missing arguments", Colors.RED))
92
- print("Usage: python3 task.py init-context <task-dir> <dev_type>")
93
- print(" dev_type: backend | frontend | fullstack | test | docs")
94
- return 1
95
-
96
- if not target_dir.is_dir():
97
- print(colored(f"Error: Directory not found: {target_dir}", Colors.RED))
98
- return 1
99
-
100
- # Resolve package: --package CLI → task.json.package → default_package
101
- cli_package: str | None = getattr(args, "package", None)
102
- package: str | None = None
103
- if not is_monorepo(repo_root):
104
- # Single-repo: ignore --package, no package prefix
105
- if cli_package:
106
- print(colored("Warning: --package ignored in single-repo project", Colors.YELLOW), file=sys.stderr)
107
- elif cli_package:
108
- if not validate_package(cli_package, repo_root):
109
- packages = get_packages(repo_root)
110
- available = ", ".join(sorted(packages.keys())) if packages else "(none)"
111
- print(colored(f"Error: unknown package '{cli_package}'. Available: {available}", Colors.RED), file=sys.stderr)
112
- return 1
113
- package = cli_package
114
- else:
115
- # Read task.json.package as inferred source
116
- task_json_path = target_dir / FILE_TASK_JSON
117
- task_pkg_value = None
118
- if task_json_path.is_file():
119
- task_data = read_json(task_json_path)
120
- if isinstance(task_data, dict):
121
- task_pkg_value = task_data.get("package")
122
- # Only pass string values to resolve_package (guard against malformed JSON)
123
- task_package = task_pkg_value if isinstance(task_pkg_value, str) else None
124
- package = resolve_package(task_package=task_package, repo_root=repo_root)
125
-
126
- # Monorepo fallback prohibition
127
- if package is None:
128
- packages = get_packages(repo_root)
129
- available = ", ".join(sorted(packages.keys())) if packages else "(none)"
130
- print(colored(
131
- f"Error: monorepo project requires --package (or set default_package in config.yaml). Available: {available}",
132
- Colors.RED,
133
- ), file=sys.stderr)
134
- return 1
135
-
136
- print(colored("=== Initializing Agent Context Files ===", Colors.BLUE))
137
- print(f"Target dir: {target_dir}")
138
- print(f"Dev type: {dev_type}")
139
- if package:
140
- print(f"Package: {package}")
141
- print()
142
-
143
- # implement.jsonl
144
- print(colored("Creating implement.jsonl...", Colors.CYAN))
145
- implement_entries = get_implement_base()
146
- if dev_type in ("backend", "test"):
147
- implement_entries.extend(get_implement_backend(package))
148
- elif dev_type == "frontend":
149
- implement_entries.extend(get_implement_frontend(package))
150
- elif dev_type == "fullstack":
151
- implement_entries.extend(get_implement_backend(package))
152
- implement_entries.extend(get_implement_frontend(package))
153
-
154
- implement_file = target_dir / "implement.jsonl"
155
- _write_jsonl(implement_file, implement_entries)
156
- print(f" {colored('✓', Colors.GREEN)} {len(implement_entries)} entries")
157
-
158
- # check.jsonl
159
- print(colored("Creating check.jsonl...", Colors.CYAN))
160
- check_entries = get_check_context(repo_root)
161
- check_file = target_dir / "check.jsonl"
162
- _write_jsonl(check_file, check_entries)
163
- print(f" {colored('✓', Colors.GREEN)} {len(check_entries)} entries")
164
-
165
- # Update task.json dev_type and package
166
- task_json_path = target_dir / FILE_TASK_JSON
167
- if task_json_path.is_file():
168
- task_data = read_json(task_json_path)
169
- if isinstance(task_data, dict):
170
- task_data["dev_type"] = dev_type
171
- task_data["package"] = package # Always sync to match resolved value
172
- write_json(task_json_path, task_data)
173
-
174
- print()
175
- print(colored("✓ All context files created", Colors.GREEN))
176
- print()
177
-
178
- # Show what was auto-injected
179
- all_injected = [e["file"] for e in implement_entries]
180
- print(colored("Auto-injected (defaults only):", Colors.YELLOW))
181
- for f in all_injected:
182
- print(f" - {f}")
183
- print()
184
-
185
- # Scan spec directory for available spec files the AI should consider
186
- spec_base = repo_root / DIR_WORKFLOW / DIR_SPEC
187
- if package:
188
- spec_base = spec_base / package
189
- available_specs: list[str] = []
190
- if spec_base.is_dir():
191
- for md_file in sorted(spec_base.rglob("*.md")):
192
- rel = str(md_file.relative_to(repo_root))
193
- if rel not in all_injected:
194
- available_specs.append(rel)
195
-
196
- if available_specs:
197
- print(colored("Available spec files (not yet injected):", Colors.BLUE))
198
- for spec in available_specs:
199
- print(f" - {spec}")
200
- print()
201
-
202
- print(colored("Next steps:", Colors.BLUE))
203
- print(" 1. Review the spec files above and add relevant ones for your task:")
204
- print(f" python3 task.py add-context <dir> implement <spec-path> \"<reason>\"")
205
- print(" 2. Set as current: python3 task.py start <dir>")
206
-
207
- return 0
208
-
209
-
210
29
  # =============================================================================
211
30
  # Command: add-context
212
31
  # =============================================================================
@@ -294,7 +113,11 @@ def cmd_validate(args: argparse.Namespace) -> int:
294
113
 
295
114
 
296
115
  def _validate_jsonl(jsonl_file: Path, repo_root: Path) -> int:
297
- """Validate a single JSONL file."""
116
+ """Validate a single JSONL file.
117
+
118
+ Seed rows (no ``file`` field — typically ``{"_example": "..."}``) are
119
+ skipped silently; they are self-describing comments, not real entries.
120
+ """
298
121
  file_name = jsonl_file.name
299
122
  errors = 0
300
123
 
@@ -303,6 +126,7 @@ def _validate_jsonl(jsonl_file: Path, repo_root: Path) -> int:
303
126
  return 0
304
127
 
305
128
  line_num = 0
129
+ real_entries = 0
306
130
  for line in jsonl_file.read_text(encoding="utf-8").splitlines():
307
131
  line_num += 1
308
132
  if not line.strip():
@@ -319,10 +143,10 @@ def _validate_jsonl(jsonl_file: Path, repo_root: Path) -> int:
319
143
  entry_type = data.get("type", "file")
320
144
 
321
145
  if not file_path:
322
- print(f" {colored(f'{file_name}:{line_num}: Missing file field', Colors.RED)}")
323
- errors += 1
146
+ # Seed / comment row — skip silently
324
147
  continue
325
148
 
149
+ real_entries += 1
326
150
  full_path = repo_root / file_path
327
151
  if entry_type == "directory":
328
152
  if not full_path.is_dir():
@@ -334,7 +158,7 @@ def _validate_jsonl(jsonl_file: Path, repo_root: Path) -> int:
334
158
  errors += 1
335
159
 
336
160
  if errors == 0:
337
- print(f" {colored(f'{file_name}: ✓ ({line_num} entries)', Colors.GREEN)}")
161
+ print(f" {colored(f'{file_name}: ✓ ({real_entries} entries)', Colors.GREEN)}")
338
162
  else:
339
163
  print(f" {colored(f'{file_name}: ✗ ({errors} errors)', Colors.RED)}")
340
164
 
@@ -365,6 +189,7 @@ def cmd_list_context(args: argparse.Namespace) -> int:
365
189
  print(colored(f"[{jsonl_name}]", Colors.CYAN))
366
190
 
367
191
  count = 0
192
+ seed_only = True
368
193
  for line in jsonl_file.read_text(encoding="utf-8").splitlines():
369
194
  if not line.strip():
370
195
  continue
@@ -374,8 +199,13 @@ def cmd_list_context(args: argparse.Namespace) -> int:
374
199
  except json.JSONDecodeError:
375
200
  continue
376
201
 
202
+ file_path = data.get("file")
203
+ if not file_path:
204
+ # Seed / comment row — don't count as a real entry
205
+ continue
206
+ seed_only = False
207
+
377
208
  count += 1
378
- file_path = data.get("file", "?")
379
209
  entry_type = data.get("type", "file")
380
210
  reason = data.get("reason", "-")
381
211
 
@@ -385,6 +215,9 @@ def cmd_list_context(args: argparse.Namespace) -> int:
385
215
  print(f" {colored(f'{count}.', Colors.GREEN)} {file_path}")
386
216
  print(f" {colored('→', Colors.YELLOW)} {reason}")
387
217
 
218
+ if seed_only:
219
+ print(f" {colored('(no curated entries yet — only seed row)', Colors.YELLOW)}")
220
+
388
221
  print()
389
222
 
390
223
  return 0
@@ -16,6 +16,7 @@ Provides:
16
16
  from __future__ import annotations
17
17
 
18
18
  import argparse
19
+ import json
19
20
  import re
20
21
  import sys
21
22
  from datetime import datetime
@@ -35,9 +36,7 @@ from .paths import (
35
36
  DIR_TASKS,
36
37
  DIR_WORKFLOW,
37
38
  FILE_TASK_JSON,
38
- clear_current_task,
39
39
  generate_task_date_prefix,
40
- get_current_task,
41
40
  get_developer,
42
41
  get_repo_root,
43
42
  get_tasks_dir,
@@ -78,6 +77,61 @@ def ensure_tasks_dir(repo_root: Path) -> Path:
78
77
  return tasks_dir
79
78
 
80
79
 
80
+ # =============================================================================
81
+ # Sub-agent platform detection + JSONL seeding
82
+ # =============================================================================
83
+
84
+ # Config directories of platforms that consume implement.jsonl / check.jsonl.
85
+ # Keep in sync with src/types/ai-tools.ts AI_TOOLS entries — these are the
86
+ # platforms listed in workflow.md's "agent-capable" Skill Routing block
87
+ # (Class-1 hook-inject + Class-2 pull-based preludes). Kilo / Antigravity /
88
+ # Windsurf are NOT in this list: they do not consume JSONL.
89
+ _SUBAGENT_CONFIG_DIRS: tuple[str, ...] = (
90
+ ".claude",
91
+ ".cursor",
92
+ ".codex",
93
+ ".kiro",
94
+ ".gemini",
95
+ ".opencode",
96
+ ".qoder",
97
+ ".codebuddy",
98
+ ".factory", # Factory Droid
99
+ ".github/copilot",
100
+ ".pi", # Pi Agent
101
+ )
102
+
103
+ _SEED_EXAMPLE = (
104
+ "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. "
105
+ "Put spec/research files only — no code paths. "
106
+ "Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. "
107
+ "Delete this line once real entries are added."
108
+ )
109
+
110
+
111
+ def _has_subagent_platform(repo_root: Path) -> bool:
112
+ """Return True if any sub-agent-capable platform is configured.
113
+
114
+ Detected by probing well-known config directories at the repo root. Used
115
+ only to decide whether ``task.py create`` should seed empty
116
+ ``implement.jsonl`` / ``check.jsonl`` files.
117
+ """
118
+ for config_dir in _SUBAGENT_CONFIG_DIRS:
119
+ if (repo_root / config_dir).is_dir():
120
+ return True
121
+ return False
122
+
123
+
124
+ def _write_seed_jsonl(path: Path) -> None:
125
+ """Write a one-line seed JSONL file with a self-describing ``_example``.
126
+
127
+ The seed row has no ``file`` field, so downstream consumers (hooks +
128
+ preludes) that iterate entries via ``item.get("file")`` naturally skip
129
+ it. The row exists purely as an in-file prompt for the AI curator.
130
+ """
131
+ seed = {"_example": _SEED_EXAMPLE}
132
+ path.write_text(json.dumps(seed, ensure_ascii=False) + "\n", encoding="utf-8")
133
+
134
+
81
135
  # =============================================================================
82
136
  # Command: create
83
137
  # =============================================================================
@@ -173,6 +227,18 @@ def cmd_create(args: argparse.Namespace) -> int:
173
227
 
174
228
  write_json(task_json_path, task_data)
175
229
 
230
+ # Seed implement.jsonl / check.jsonl for sub-agent-capable platforms.
231
+ # Agent curates real entries in Phase 1.3 (see .trellis/workflow.md).
232
+ # Agent-less platforms (Kilo / Antigravity / Windsurf) skip this — they
233
+ # load specs via the trellis-before-dev skill instead of JSONL.
234
+ seeded_jsonl = False
235
+ if _has_subagent_platform(repo_root):
236
+ for jsonl_name in ("implement.jsonl", "check.jsonl"):
237
+ jsonl_path = task_dir / jsonl_name
238
+ if not jsonl_path.exists():
239
+ _write_seed_jsonl(jsonl_path)
240
+ seeded_jsonl = True
241
+
176
242
  # Handle --parent: establish bidirectional link
177
243
  if args.parent:
178
244
  parent_dir = resolve_task_dir(args.parent, repo_root)
@@ -195,12 +261,35 @@ def cmd_create(args: argparse.Namespace) -> int:
195
261
 
196
262
  print(colored(f"Linked as child of: {parent_dir.name}", Colors.GREEN), file=sys.stderr)
197
263
 
264
+ # Auto-activate the new task so the per-turn breadcrumb fires planning
265
+ # state. Best-effort: gracefully degrade if no session identity (CLI run
266
+ # outside an AI session) — the task is still created, the user can run
267
+ # task.py start later. Pointer is session-scoped so this never affects
268
+ # other AI sessions.
269
+ try:
270
+ from .active_task import resolve_context_key, set_active_task
271
+ if resolve_context_key():
272
+ try:
273
+ rel_dir = task_dir.relative_to(repo_root).as_posix()
274
+ except ValueError:
275
+ rel_dir = str(task_dir)
276
+ set_active_task(rel_dir, repo_root)
277
+ except Exception:
278
+ pass
279
+
198
280
  print(colored(f"Created task: {dir_name}", Colors.GREEN), file=sys.stderr)
199
281
  print("", file=sys.stderr)
200
282
  print(colored("Next steps:", Colors.BLUE), file=sys.stderr)
201
283
  print(" 1. Create prd.md with requirements", file=sys.stderr)
202
- print(" 2. Run: python3 task.py init-context <dir> <dev_type>", file=sys.stderr)
203
- print(" 3. Run: python3 task.py start <dir>", file=sys.stderr)
284
+ if seeded_jsonl:
285
+ print(
286
+ " 2. Curate implement.jsonl / check.jsonl (spec + research files only — "
287
+ "see .trellis/workflow.md Phase 1.3)",
288
+ file=sys.stderr,
289
+ )
290
+ print(" 3. Run: python3 task.py start <dir>", file=sys.stderr)
291
+ else:
292
+ print(" 2. Run: python3 task.py start <dir>", file=sys.stderr)
204
293
  print("", file=sys.stderr)
205
294
 
206
295
  # Output relative path for script chaining
@@ -225,8 +314,8 @@ def cmd_archive(args: argparse.Namespace) -> int:
225
314
 
226
315
  tasks_dir = get_tasks_dir(repo_root)
227
316
 
228
- # Find task directory
229
- task_dir = find_task_by_name(task_name, tasks_dir)
317
+ # Resolve task directory (supports task name, relative path, or absolute path)
318
+ task_dir = resolve_task_dir(task_name, repo_root)
230
319
 
231
320
  if not task_dir or not task_dir.is_dir():
232
321
  print(colored(f"Error: Task not found: {task_name}", Colors.RED), file=sys.stderr)
@@ -249,24 +338,12 @@ def cmd_archive(args: argparse.Namespace) -> int:
249
338
  data["completedAt"] = today
250
339
  write_json(task_json_path, data)
251
340
 
252
- # Handle subtask relationships on archive
253
- task_parent = data.get("parent")
341
+ # Handle subtask relationships on archive.
342
+ # Keep this task in its parent's children list so progress
343
+ # counters (children_progress) stay consistent — children
344
+ # missing from the active set are treated as completed.
254
345
  task_children = data.get("children", [])
255
346
 
256
- # If this is a child, remove from parent's children list
257
- if task_parent:
258
- parent_dir = find_task_by_name(task_parent, tasks_dir)
259
- if parent_dir:
260
- parent_json = parent_dir / FILE_TASK_JSON
261
- if parent_json.is_file():
262
- parent_data = read_json(parent_json)
263
- if parent_data:
264
- parent_children = parent_data.get("children", [])
265
- if dir_name in parent_children:
266
- parent_children.remove(dir_name)
267
- parent_data["children"] = parent_children
268
- write_json(parent_json, parent_data)
269
-
270
347
  # If this is a parent, clear parent field in all children
271
348
  if task_children:
272
349
  for child_name in task_children:
@@ -279,10 +356,9 @@ def cmd_archive(args: argparse.Namespace) -> int:
279
356
  child_data["parent"] = None
280
357
  write_json(child_json, child_data)
281
358
 
282
- # Clear if current task
283
- current = get_current_task(repo_root)
284
- if current and dir_name in current:
285
- clear_current_task(repo_root)
359
+ # Clear any session that still points at this task before the path moves.
360
+ from .active_task import clear_task_from_sessions
361
+ clear_task_from_sessions(str(task_dir), repo_root)
286
362
 
287
363
  # Archive
288
364
  result = archive_task_complete(task_dir, repo_root)
@@ -102,8 +102,11 @@ def children_progress(
102
102
  """
103
103
  if not children:
104
104
  return ""
105
+ # A child missing from active statuses has been archived (cmd_archive
106
+ # sets status=completed before moving the dir). Count it as done so
107
+ # parent progress doesn't regress when children are archived.
105
108
  done = sum(
106
109
  1 for c in children
107
- if all_statuses.get(c) in ("completed", "done")
110
+ if c not in all_statuses or all_statuses.get(c) in ("completed", "done")
108
111
  )
109
112
  return f" [{done}/{len(children)} done]"
@@ -41,8 +41,6 @@ class TaskData(TypedDict, total=False):
41
41
  branch: str | None
42
42
  base_branch: str | None
43
43
  worktree_path: str | None
44
- current_phase: int
45
- next_action: list[dict]
46
44
  commit: str | None
47
45
  pr_url: str | None
48
46
  subtasks: list[str]
@@ -65,7 +65,10 @@ def get_phase_index() -> str:
65
65
  Matches what the SessionStart hook injects into the `<workflow>` block:
66
66
  starts at `## Phase Index`, continues through `## Phase 1: Plan`,
67
67
  `## Phase 2: Execute`, `## Phase 3: Finish`, stops at
68
- `## Workflow State Breadcrumbs` (consumed by UserPromptSubmit hook).
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.
69
72
  """
70
73
  text = _read_workflow()
71
74
  lines = text.splitlines()
@@ -77,7 +80,7 @@ def get_phase_index() -> str:
77
80
  if start is None and stripped == _PHASE_INDEX_HEADING:
78
81
  start = i
79
82
  continue
80
- if start is not None and stripped == "## Workflow State Breadcrumbs":
83
+ if start is not None and stripped == "## Customizing Trellis (for forks)":
81
84
  end = i
82
85
  break
83
86
 
@@ -85,7 +88,16 @@ def get_phase_index() -> str:
85
88
  return ""
86
89
  if end is None:
87
90
  end = len(lines)
88
- return "\n".join(lines[start:end]).rstrip() + "\n"
91
+
92
+ section = "\n".join(lines[start:end]).rstrip()
93
+ # Strip [workflow-state:STATUS]...[/workflow-state:STATUS] blocks since
94
+ # they're injected separately by inject-workflow-state.py per-turn.
95
+ import re as _re
96
+ tag_re = _re.compile(
97
+ r"\[workflow-state:([A-Za-z0-9_-]+)\]\s*\n.*?\n\s*\[/workflow-state:\1\]\n?",
98
+ _re.DOTALL,
99
+ )
100
+ return tag_re.sub("", section).rstrip() + "\n"
89
101
 
90
102
 
91
103
  def get_step(step_id: str) -> str: