@mindfoldhq/trellis 0.5.0-beta.13 → 0.5.0-beta.15

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 (119) hide show
  1. package/README.md +5 -5
  2. package/dist/cli/index.js +1 -0
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/commands/init.d.ts +1 -0
  5. package/dist/commands/init.d.ts.map +1 -1
  6. package/dist/commands/init.js +24 -20
  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 +15 -12
  10. package/dist/commands/update.js.map +1 -1
  11. package/dist/configurators/claude.js +1 -1
  12. package/dist/configurators/claude.js.map +1 -1
  13. package/dist/configurators/codebuddy.js +1 -1
  14. package/dist/configurators/codebuddy.js.map +1 -1
  15. package/dist/configurators/codex.d.ts.map +1 -1
  16. package/dist/configurators/codex.js +3 -6
  17. package/dist/configurators/codex.js.map +1 -1
  18. package/dist/configurators/copilot.d.ts.map +1 -1
  19. package/dist/configurators/copilot.js +4 -11
  20. package/dist/configurators/copilot.js.map +1 -1
  21. package/dist/configurators/cursor.js +1 -1
  22. package/dist/configurators/cursor.js.map +1 -1
  23. package/dist/configurators/droid.js +1 -1
  24. package/dist/configurators/droid.js.map +1 -1
  25. package/dist/configurators/gemini.d.ts.map +1 -1
  26. package/dist/configurators/gemini.js +1 -3
  27. package/dist/configurators/gemini.js.map +1 -1
  28. package/dist/configurators/index.d.ts.map +1 -1
  29. package/dist/configurators/index.js +24 -38
  30. package/dist/configurators/index.js.map +1 -1
  31. package/dist/configurators/kiro.js +1 -1
  32. package/dist/configurators/kiro.js.map +1 -1
  33. package/dist/configurators/pi.d.ts +3 -0
  34. package/dist/configurators/pi.d.ts.map +1 -0
  35. package/dist/configurators/pi.js +39 -0
  36. package/dist/configurators/pi.js.map +1 -0
  37. package/dist/configurators/qoder.d.ts.map +1 -1
  38. package/dist/configurators/qoder.js +1 -3
  39. package/dist/configurators/qoder.js.map +1 -1
  40. package/dist/configurators/shared.d.ts +2 -4
  41. package/dist/configurators/shared.d.ts.map +1 -1
  42. package/dist/configurators/shared.js +6 -9
  43. package/dist/configurators/shared.js.map +1 -1
  44. package/dist/migrations/manifests/0.5.0-beta.14.json +9 -0
  45. package/dist/migrations/manifests/0.5.0-beta.15.json +126 -0
  46. package/dist/templates/claude/agents/trellis-research.md +1 -1
  47. package/dist/templates/claude/settings.json +0 -4
  48. package/dist/templates/codebuddy/agents/trellis-research.md +1 -1
  49. package/dist/templates/codex/agents/trellis-check.toml +0 -16
  50. package/dist/templates/codex/agents/trellis-implement.toml +0 -16
  51. package/dist/templates/codex/agents/trellis-research.toml +3 -2
  52. package/dist/templates/codex/hooks/session-start.py +82 -22
  53. package/dist/templates/codex/skills/start/SKILL.md +1 -1
  54. package/dist/templates/copilot/hooks/session-start.py +84 -26
  55. package/dist/templates/copilot/prompts/start.prompt.md +1 -1
  56. package/dist/templates/cursor/agents/trellis-check.md +1 -1
  57. package/dist/templates/cursor/agents/trellis-implement.md +1 -1
  58. package/dist/templates/cursor/agents/trellis-research.md +2 -2
  59. package/dist/templates/cursor/hooks.json +7 -1
  60. package/dist/templates/droid/droids/trellis-research.md +1 -1
  61. package/dist/templates/extract.d.ts +6 -0
  62. package/dist/templates/extract.d.ts.map +1 -1
  63. package/dist/templates/extract.js +14 -0
  64. package/dist/templates/extract.js.map +1 -1
  65. package/dist/templates/gemini/agents/trellis-research.md +1 -1
  66. package/dist/templates/kiro/agents/trellis-research.json +1 -1
  67. package/dist/templates/markdown/agents.md +11 -12
  68. package/dist/templates/markdown/gitignore.txt +3 -0
  69. package/dist/templates/opencode/agents/trellis-check.md +1 -1
  70. package/dist/templates/opencode/agents/trellis-implement.md +1 -1
  71. package/dist/templates/opencode/agents/trellis-research.md +2 -2
  72. package/dist/templates/opencode/lib/trellis-context.js +100 -13
  73. package/dist/templates/opencode/plugins/inject-subagent-context.js +54 -4
  74. package/dist/templates/opencode/plugins/inject-workflow-state.js +50 -23
  75. package/dist/templates/opencode/plugins/session-start.js +46 -21
  76. package/dist/templates/pi/agents/trellis-check.md +28 -0
  77. package/dist/templates/pi/agents/trellis-implement.md +33 -0
  78. package/dist/templates/pi/agents/trellis-research.md +25 -0
  79. package/dist/templates/pi/extensions/trellis/index.ts.txt +549 -0
  80. package/dist/templates/pi/index.d.ts +5 -0
  81. package/dist/templates/pi/index.d.ts.map +1 -0
  82. package/dist/templates/pi/index.js +12 -0
  83. package/dist/templates/pi/index.js.map +1 -0
  84. package/dist/templates/pi/settings.json +12 -0
  85. package/dist/templates/qoder/agents/trellis-research.md +1 -1
  86. package/dist/templates/shared-hooks/index.d.ts +31 -0
  87. package/dist/templates/shared-hooks/index.d.ts.map +1 -1
  88. package/dist/templates/shared-hooks/index.js +59 -0
  89. package/dist/templates/shared-hooks/index.js.map +1 -1
  90. package/dist/templates/shared-hooks/inject-shell-session-context.py +180 -0
  91. package/dist/templates/shared-hooks/inject-subagent-context.py +128 -26
  92. package/dist/templates/shared-hooks/inject-workflow-state.py +101 -61
  93. package/dist/templates/shared-hooks/session-start.py +151 -28
  94. package/dist/templates/trellis/gitignore.txt +3 -0
  95. package/dist/templates/trellis/index.d.ts +1 -0
  96. package/dist/templates/trellis/index.d.ts.map +1 -1
  97. package/dist/templates/trellis/index.js +2 -0
  98. package/dist/templates/trellis/index.js.map +1 -1
  99. package/dist/templates/trellis/scripts/common/__init__.py +8 -0
  100. package/dist/templates/trellis/scripts/common/active_task.py +593 -0
  101. package/dist/templates/trellis/scripts/common/cli_adapter.py +43 -8
  102. package/dist/templates/trellis/scripts/common/paths.py +61 -58
  103. package/dist/templates/trellis/scripts/common/session_context.py +12 -0
  104. package/dist/templates/trellis/scripts/common/task_store.py +4 -6
  105. package/dist/templates/trellis/scripts/task.py +56 -14
  106. package/dist/templates/trellis/workflow.md +31 -26
  107. package/dist/types/ai-tools.d.ts +3 -3
  108. package/dist/types/ai-tools.d.ts.map +1 -1
  109. package/dist/types/ai-tools.js +16 -0
  110. package/dist/types/ai-tools.js.map +1 -1
  111. package/dist/utils/template-fetcher.d.ts +22 -6
  112. package/dist/utils/template-fetcher.d.ts.map +1 -1
  113. package/dist/utils/template-fetcher.js +405 -27
  114. package/dist/utils/template-fetcher.js.map +1 -1
  115. package/dist/utils/template-hash.d.ts.map +1 -1
  116. package/dist/utils/template-hash.js +3 -2
  117. package/dist/utils/template-hash.js.map +1 -1
  118. package/package.json +1 -1
  119. package/dist/templates/shared-hooks/statusline.py +0 -218
@@ -3,22 +3,6 @@ description = "Workspace-write Trellis reviewer that self-fixes spec drift, lint
3
3
  sandbox_mode = "workspace-write"
4
4
 
5
5
  developer_instructions = """
6
- ## Required: Load Trellis Context First
7
-
8
- This platform does NOT auto-inject task context via hook. Before doing anything else, you MUST load context yourself:
9
-
10
- 1. Read `.trellis/.current-task` to find the current task path (e.g. `.trellis/tasks/04-17-foo/`).
11
- 2. Read the task's `prd.md` (requirements) and `info.md` if it exists (technical design).
12
- 3. Read `<task-path>/check.jsonl` — JSONL list of dev spec files relevant to this agent.
13
- 4. For each entry in the JSONL, Read its `file` path — these are the dev specs you must follow.
14
- **Skip rows without a `"file"` field** (e.g. `{"_example": "..."}` seed rows left over from `task.py create` before the curator ran).
15
-
16
- If `check.jsonl` has no curated entries (only a seed row, or the file is missing), fall back to: read `prd.md`, list available specs with `python3 ./.trellis/scripts/get_context.py --mode packages`, and pick the specs that match the task domain yourself. Do NOT block on the missing jsonl — proceed with prd-only context plus your spec judgment.
17
-
18
- If `.current-task` is missing or the task has no `prd.md`, ask the user what to work on; do NOT proceed without context.
19
-
20
- ---
21
-
22
6
  You are the Trellis reviewer agent.
23
7
 
24
8
  Your job is to review code changes against specs AND fix issues directly — not just report them. You have write access; use it.
@@ -3,22 +3,6 @@ description = "Workspace-write Trellis implementer that follows specs and keeps
3
3
  sandbox_mode = "workspace-write"
4
4
 
5
5
  developer_instructions = """
6
- ## Required: Load Trellis Context First
7
-
8
- This platform does NOT auto-inject task context via hook. Before doing anything else, you MUST load context yourself:
9
-
10
- 1. Read `.trellis/.current-task` to find the current task path (e.g. `.trellis/tasks/04-17-foo/`).
11
- 2. Read the task's `prd.md` (requirements) and `info.md` if it exists (technical design).
12
- 3. Read `<task-path>/implement.jsonl` — JSONL list of dev spec files relevant to this agent.
13
- 4. For each entry in the JSONL, Read its `file` path — these are the dev specs you must follow.
14
- **Skip rows without a `"file"` field** (e.g. `{"_example": "..."}` seed rows left over from `task.py create` before the curator ran).
15
-
16
- If `implement.jsonl` has no curated entries (only a seed row, or the file is missing), fall back to: read `prd.md`, list available specs with `python3 ./.trellis/scripts/get_context.py --mode packages`, and pick the specs that match the task domain yourself. Do NOT block on the missing jsonl — proceed with prd-only context plus your spec judgment.
17
-
18
- If `.current-task` is missing or the task has no `prd.md`, ask the user what to work on; do NOT proceed without context.
19
-
20
- ---
21
-
22
6
  You are the Trellis implementer agent.
23
7
 
24
8
  Rules:
@@ -13,8 +13,9 @@ through the chat reply is a failure.
13
13
 
14
14
  ## Workflow
15
15
 
16
- 1. Read `.trellis/.current-task` to get the task directory. If empty,
17
- ask the user where to write output; do not guess.
16
+ 1. Run `python3 ./.trellis/scripts/task.py current --source` to get the
17
+ active task path and source. If no active task is set, ask the user
18
+ where to write output; do not guess.
18
19
  2. Run `mkdir -p <TASK_DIR>/research` to ensure the directory exists.
19
20
  3. Read `.trellis/workflow.md`, relevant `.trellis/spec/` files, and
20
21
  target code before forming an opinion.
@@ -19,11 +19,31 @@ from pathlib import Path
19
19
 
20
20
  warnings.filterwarnings("ignore")
21
21
 
22
+ FIRST_REPLY_NOTICE = """<first-reply-notice>
23
+ On the first visible assistant reply in this session, begin with exactly one short Chinese sentence:
24
+ Trellis SessionStart 已注入:workflow、当前任务状态、开发者身份、git 状态、active tasks、spec 索引已加载。
25
+ 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.
26
+ </first-reply-notice>"""
27
+
22
28
 
23
29
  def should_skip_injection() -> bool:
24
30
  return os.environ.get("CODEX_NON_INTERACTIVE") == "1"
25
31
 
26
32
 
33
+ def configure_project_encoding(project_dir: Path) -> None:
34
+ """Reuse Trellis' shared Windows stdio encoding helper before JSON output."""
35
+ scripts_dir = project_dir / ".trellis" / "scripts"
36
+ if str(scripts_dir) not in sys.path:
37
+ sys.path.insert(0, str(scripts_dir))
38
+
39
+ try:
40
+ from common import configure_encoding # type: ignore[import-not-found]
41
+
42
+ configure_encoding()
43
+ except Exception:
44
+ pass
45
+
46
+
27
47
  def _has_curated_jsonl_entry(jsonl_path: Path) -> bool:
28
48
  """Return True iff jsonl has at least one row with a ``file`` field.
29
49
 
@@ -54,10 +74,32 @@ def read_file(path: Path, fallback: str = "") -> str:
54
74
  return fallback
55
75
 
56
76
 
57
- def run_script(script_path: Path) -> str:
77
+ def _resolve_context_key(project_dir: Path, hook_input: dict) -> str | None:
78
+ scripts_dir = project_dir / ".trellis" / "scripts"
79
+ if str(scripts_dir) not in sys.path:
80
+ sys.path.insert(0, str(scripts_dir))
81
+ try:
82
+ from common.active_task import resolve_context_key # type: ignore[import-not-found]
83
+ except Exception:
84
+ return None
85
+ return resolve_context_key(hook_input, platform="codex")
86
+
87
+
88
+ def _resolve_active_task(trellis_dir: Path, hook_input: dict):
89
+ scripts_dir = trellis_dir / "scripts"
90
+ if str(scripts_dir) not in sys.path:
91
+ sys.path.insert(0, str(scripts_dir))
92
+ from common.active_task import resolve_active_task # type: ignore[import-not-found]
93
+
94
+ return resolve_active_task(trellis_dir.parent, hook_input, platform="codex")
95
+
96
+
97
+ def run_script(script_path: Path, context_key: str | None = None) -> str:
58
98
  try:
59
99
  env = os.environ.copy()
60
100
  env["PYTHONIOENCODING"] = "utf-8"
101
+ if context_key:
102
+ env["TRELLIS_CONTEXT_ID"] = context_key
61
103
  cmd = [sys.executable, "-W", "ignore", str(script_path)]
62
104
  result = subprocess.run(
63
105
  cmd,
@@ -103,18 +145,15 @@ def _resolve_task_dir(trellis_dir: Path, task_ref: str) -> Path:
103
145
  return trellis_dir / "tasks" / path_obj
104
146
 
105
147
 
106
- def _get_task_status(trellis_dir: Path) -> str:
107
- current_task_file = trellis_dir / ".current-task"
108
- if not current_task_file.is_file():
109
- return "Status: NO ACTIVE TASK\nNext: Describe what you want to work on"
110
-
111
- task_ref = _normalize_task_ref(current_task_file.read_text(encoding="utf-8").strip())
112
- if not task_ref:
113
- return "Status: NO ACTIVE TASK\nNext: Describe what you want to work on"
148
+ def _get_task_status(trellis_dir: Path, hook_input: dict) -> str:
149
+ active = _resolve_active_task(trellis_dir, hook_input)
150
+ if not active.task_path:
151
+ return f"Status: NO ACTIVE TASK\nSource: {active.source}\nNext: Describe what you want to work on"
114
152
 
153
+ task_ref = active.task_path
115
154
  task_dir = _resolve_task_dir(trellis_dir, task_ref)
116
- if not task_dir.is_dir():
117
- return f"Status: STALE POINTER\nTask: {task_ref}\nNext: Task directory not found. Run: python3 ./.trellis/scripts/task.py finish"
155
+ if active.stale or not task_dir.is_dir():
156
+ return f"Status: STALE POINTER\nTask: {task_ref}\nSource: {active.source}\nNext: Task directory not found. Run: python3 ./.trellis/scripts/task.py finish"
118
157
 
119
158
  task_json_path = task_dir / "task.json"
120
159
  task_data: dict = {}
@@ -128,7 +167,7 @@ def _get_task_status(trellis_dir: Path) -> str:
128
167
  task_status = task_data.get("status", "unknown")
129
168
 
130
169
  if task_status == "completed":
131
- return f"Status: COMPLETED\nTask: {task_title}\nNext: Archive with `python3 ./.trellis/scripts/task.py archive {task_dir.name}` or start a new task"
170
+ return f"Status: COMPLETED\nTask: {task_title}\nSource: {active.source}\nNext: Archive with `python3 ./.trellis/scripts/task.py archive {task_dir.name}` or start a new task"
132
171
 
133
172
  has_context = False
134
173
  for jsonl_name in ("implement.jsonl", "check.jsonl", "spec.jsonl"):
@@ -140,12 +179,22 @@ def _get_task_status(trellis_dir: Path) -> str:
140
179
  has_prd = (task_dir / "prd.md").is_file()
141
180
 
142
181
  if not has_prd:
143
- return f"Status: NOT READY\nTask: {task_title}\nMissing: prd.md not created\nNext: Write PRD (see workflow.md Phase 1.1) then curate implement.jsonl per Phase 1.3"
182
+ return f"Status: NOT READY\nTask: {task_title}\nSource: {active.source}\nMissing: prd.md not created\nNext: Write PRD (see workflow.md Phase 1.1) then curate implement.jsonl per Phase 1.3"
144
183
 
145
184
  if not has_context:
146
- return f"Status: NOT READY\nTask: {task_title}\nMissing: implement.jsonl / check.jsonl missing or empty\nNext: Curate entries per workflow.md Phase 1.3 (spec + research files only), then `task.py start`"
147
-
148
- return f"Status: READY\nTask: {task_title}\nNext: Continue with implement or check"
185
+ return f"Status: NOT READY\nTask: {task_title}\nSource: {active.source}\nMissing: implement.jsonl / check.jsonl missing or empty\nNext: Curate entries per workflow.md Phase 1.3 (spec + research files only), then `task.py start`"
186
+
187
+ return (
188
+ f"Status: READY\nTask: {task_title}\n"
189
+ f"Source: {active.source}\n"
190
+ "Next required action: dispatch `trellis-implement` per Phase 2.1. "
191
+ "For agent-capable platforms, the default is to NOT edit code in the main session. "
192
+ "After implementation, dispatch `trellis-check` per Phase 2.2 before reporting completion.\n"
193
+ "User override (per-turn escape hatch): if the user's CURRENT message explicitly tells the "
194
+ "main session to handle it directly (\"你直接改\" / \"别派 sub-agent\" / \"main session 写就行\" / "
195
+ "\"do it inline\" / \"不用 sub-agent\"), honor it for this turn and edit code directly. "
196
+ "Per-turn only; do NOT invent an override the user did not say."
197
+ )
149
198
 
150
199
 
151
200
  def _extract_range(content: str, start_header: str, end_header: str) -> str:
@@ -199,11 +248,17 @@ def main() -> None:
199
248
  # Read hook input from stdin
200
249
  try:
201
250
  hook_input = json.loads(sys.stdin.read())
251
+ if not isinstance(hook_input, dict):
252
+ hook_input = {}
202
253
  project_dir = Path(hook_input.get("cwd", ".")).resolve()
203
254
  except (json.JSONDecodeError, KeyError):
255
+ hook_input = {}
204
256
  project_dir = Path(".").resolve()
205
257
 
258
+ configure_project_encoding(project_dir)
259
+
206
260
  trellis_dir = project_dir / ".trellis"
261
+ context_key = _resolve_context_key(project_dir, hook_input)
207
262
 
208
263
  output = StringIO()
209
264
 
@@ -213,10 +268,12 @@ Read and follow all instructions below carefully.
213
268
  </session-context>
214
269
 
215
270
  """)
271
+ output.write(FIRST_REPLY_NOTICE)
272
+ output.write("\n\n")
216
273
 
217
274
  output.write("<current-state>\n")
218
275
  context_script = trellis_dir / "scripts" / "get_context.py"
219
- output.write(run_script(context_script))
276
+ output.write(run_script(context_script, context_key))
220
277
  output.write("\n</current-state>\n\n")
221
278
 
222
279
  output.write("<workflow>\n")
@@ -231,8 +288,11 @@ Read and follow all instructions below carefully.
231
288
  "- If you're spawning an implement/check sub-agent, context is injected "
232
289
  "automatically via `{task}/implement.jsonl` / `check.jsonl`. You do NOT "
233
290
  "need to read these indexes yourself.\n"
234
- "- If you're editing code directly in the main session, Read the relevant "
235
- "index(es) on-demand and follow their Pre-Dev Checklist.\n\n"
291
+ "- For agent-capable platforms, the default is to dispatch "
292
+ "`trellis-implement` and `trellis-check` (so JSONL context is loaded by "
293
+ "the sub-agents) rather than editing code in the main session. "
294
+ "Honor a per-turn user override only if the user's current message "
295
+ "explicitly opts out (see <task-status> below for override phrases).\n\n"
236
296
  )
237
297
 
238
298
  # guides/ inlined (cross-package thinking, broadly useful)
@@ -276,13 +336,13 @@ Read and follow all instructions below carefully.
276
336
  )
277
337
  output.write("</guidelines>\n\n")
278
338
 
279
- task_status = _get_task_status(trellis_dir)
339
+ task_status = _get_task_status(trellis_dir, hook_input)
280
340
  output.write(f"<task-status>\n{task_status}\n</task-status>\n\n")
281
341
 
282
342
  output.write("""<ready>
283
343
  Context loaded. Workflow index, project state, and guidelines are already injected above — do NOT re-read them.
284
- Wait for the user's first message, then handle it following the workflow guide.
285
- If there is an active task, ask whether to continue it.
344
+ When the user sends the first message, follow <task-status> and the workflow guide.
345
+ If a task is READY, execute its Next required action without asking whether to continue.
286
346
  </ready>""")
287
347
 
288
348
  context = output.getvalue()
@@ -269,7 +269,7 @@ python3 ./.trellis/scripts/task.py add-context "$TASK_DIR" check "<path>" "<reas
269
269
  python3 ./.trellis/scripts/task.py start "$TASK_DIR"
270
270
  ```
271
271
 
272
- This sets `.current-task` so hooks can inject context.
272
+ This sets the active task through Trellis' session resolver so hooks can inject context for this AI session. If the command fails because no session identity is available, rerun it from an IDE/session that exposes session identity or set `TRELLIS_CONTEXT_ID`.
273
273
 
274
274
  ---
275
275
 
@@ -1,10 +1,13 @@
1
1
  #!/usr/bin/env python3
2
2
  # -*- coding: utf-8 -*-
3
3
  """
4
- Copilot Session Start Hook - Inject Trellis context into VS Code Copilot sessions.
4
+ Copilot Session Start Hook - Emit Trellis session-start diagnostics.
5
5
 
6
- Output format follows Copilot hook protocol:
7
- stdout JSON { hookSpecificOutput: { hookEventName: "SessionStart", additionalContext: "..." } }
6
+ GitHub Copilot's documented SessionStart behavior ignores hook output, so this
7
+ script must not be treated as proof that model-visible context was injected.
8
+ The JSON shape is kept for parity with other Trellis hooks and future host
9
+ support, but current Copilot users should rely on UserPromptSubmit breadcrumbs
10
+ and hook logs instead.
8
11
  """
9
12
 
10
13
  from __future__ import annotations
@@ -24,6 +27,20 @@ def should_skip_injection() -> bool:
24
27
  return os.environ.get("COPILOT_NON_INTERACTIVE") == "1"
25
28
 
26
29
 
30
+ def configure_project_encoding(project_dir: Path) -> None:
31
+ """Reuse Trellis' shared Windows stdio encoding helper before JSON output."""
32
+ scripts_dir = project_dir / ".trellis" / "scripts"
33
+ if str(scripts_dir) not in sys.path:
34
+ sys.path.insert(0, str(scripts_dir))
35
+
36
+ try:
37
+ from common import configure_encoding # type: ignore[import-not-found]
38
+
39
+ configure_encoding()
40
+ except Exception:
41
+ pass
42
+
43
+
27
44
  def _has_curated_jsonl_entry(jsonl_path: Path) -> bool:
28
45
  """Return True iff jsonl has at least one row with a ``file`` field.
29
46
 
@@ -54,10 +71,32 @@ def read_file(path: Path, fallback: str = "") -> str:
54
71
  return fallback
55
72
 
56
73
 
57
- def run_script(script_path: Path) -> str:
74
+ def _resolve_context_key(project_dir: Path, hook_input: dict) -> str | None:
75
+ scripts_dir = project_dir / ".trellis" / "scripts"
76
+ if str(scripts_dir) not in sys.path:
77
+ sys.path.insert(0, str(scripts_dir))
78
+ try:
79
+ from common.active_task import resolve_context_key # type: ignore[import-not-found]
80
+ except Exception:
81
+ return None
82
+ return resolve_context_key(hook_input, platform="copilot")
83
+
84
+
85
+ def _resolve_active_task(trellis_dir: Path, hook_input: dict):
86
+ scripts_dir = trellis_dir / "scripts"
87
+ if str(scripts_dir) not in sys.path:
88
+ sys.path.insert(0, str(scripts_dir))
89
+ from common.active_task import resolve_active_task # type: ignore[import-not-found]
90
+
91
+ return resolve_active_task(trellis_dir.parent, hook_input, platform="copilot")
92
+
93
+
94
+ def run_script(script_path: Path, context_key: str | None = None) -> str:
58
95
  try:
59
96
  env = os.environ.copy()
60
97
  env["PYTHONIOENCODING"] = "utf-8"
98
+ if context_key:
99
+ env["TRELLIS_CONTEXT_ID"] = context_key
61
100
  cmd = [sys.executable, "-W", "ignore", str(script_path)]
62
101
  result = subprocess.run(
63
102
  cmd,
@@ -103,18 +142,15 @@ def _resolve_task_dir(trellis_dir: Path, task_ref: str) -> Path:
103
142
  return trellis_dir / "tasks" / path_obj
104
143
 
105
144
 
106
- def _get_task_status(trellis_dir: Path) -> str:
107
- current_task_file = trellis_dir / ".current-task"
108
- if not current_task_file.is_file():
109
- return "Status: NO ACTIVE TASK\nNext: Describe what you want to work on"
110
-
111
- task_ref = _normalize_task_ref(current_task_file.read_text(encoding="utf-8").strip())
112
- if not task_ref:
113
- return "Status: NO ACTIVE TASK\nNext: Describe what you want to work on"
145
+ def _get_task_status(trellis_dir: Path, hook_input: dict) -> str:
146
+ active = _resolve_active_task(trellis_dir, hook_input)
147
+ if not active.task_path:
148
+ return f"Status: NO ACTIVE TASK\nSource: {active.source}\nNext: Describe what you want to work on"
114
149
 
150
+ task_ref = active.task_path
115
151
  task_dir = _resolve_task_dir(trellis_dir, task_ref)
116
- if not task_dir.is_dir():
117
- return f"Status: STALE POINTER\nTask: {task_ref}\nNext: Task directory not found. Run: python3 ./.trellis/scripts/task.py finish"
152
+ if active.stale or not task_dir.is_dir():
153
+ return f"Status: STALE POINTER\nTask: {task_ref}\nSource: {active.source}\nNext: Task directory not found. Run: python3 ./.trellis/scripts/task.py finish"
118
154
 
119
155
  task_json_path = task_dir / "task.json"
120
156
  task_data: dict = {}
@@ -128,7 +164,7 @@ def _get_task_status(trellis_dir: Path) -> str:
128
164
  task_status = task_data.get("status", "unknown")
129
165
 
130
166
  if task_status == "completed":
131
- return f"Status: COMPLETED\nTask: {task_title}\nNext: Archive with `python3 ./.trellis/scripts/task.py archive {task_dir.name}` or start a new task"
167
+ return f"Status: COMPLETED\nTask: {task_title}\nSource: {active.source}\nNext: Archive with `python3 ./.trellis/scripts/task.py archive {task_dir.name}` or start a new task"
132
168
 
133
169
  has_context = False
134
170
  for jsonl_name in ("implement.jsonl", "check.jsonl", "spec.jsonl"):
@@ -140,12 +176,22 @@ def _get_task_status(trellis_dir: Path) -> str:
140
176
  has_prd = (task_dir / "prd.md").is_file()
141
177
 
142
178
  if not has_prd:
143
- return f"Status: NOT READY\nTask: {task_title}\nMissing: prd.md not created\nNext: Write PRD (see workflow.md Phase 1.1) then curate implement.jsonl per Phase 1.3"
179
+ return f"Status: NOT READY\nTask: {task_title}\nSource: {active.source}\nMissing: prd.md not created\nNext: Write PRD (see workflow.md Phase 1.1) then curate implement.jsonl per Phase 1.3"
144
180
 
145
181
  if not has_context:
146
- return f"Status: NOT READY\nTask: {task_title}\nMissing: implement.jsonl / check.jsonl missing or empty\nNext: Curate entries per workflow.md Phase 1.3 (spec + research files only), then `task.py start`"
147
-
148
- return f"Status: READY\nTask: {task_title}\nNext: Continue with implement or check"
182
+ return f"Status: NOT READY\nTask: {task_title}\nSource: {active.source}\nMissing: implement.jsonl / check.jsonl missing or empty\nNext: Curate entries per workflow.md Phase 1.3 (spec + research files only), then `task.py start`"
183
+
184
+ return (
185
+ f"Status: READY\nTask: {task_title}\n"
186
+ f"Source: {active.source}\n"
187
+ "Next required action: dispatch `trellis-implement` per Phase 2.1. "
188
+ "For agent-capable platforms, the default is to NOT edit code in the main session. "
189
+ "After implementation, dispatch `trellis-check` per Phase 2.2 before reporting completion.\n"
190
+ "User override (per-turn escape hatch): if the user's CURRENT message explicitly tells the "
191
+ "main session to handle it directly (\"你直接改\" / \"别派 sub-agent\" / \"main session 写就行\" / "
192
+ "\"do it inline\" / \"不用 sub-agent\"), honor it for this turn and edit code directly. "
193
+ "Per-turn only; do NOT invent an override the user did not say."
194
+ )
149
195
 
150
196
 
151
197
  def _extract_range(content: str, start_header: str, end_header: str) -> str:
@@ -199,11 +245,17 @@ def main() -> None:
199
245
  # Read hook input from stdin
200
246
  try:
201
247
  hook_input = json.loads(sys.stdin.read())
248
+ if not isinstance(hook_input, dict):
249
+ hook_input = {}
202
250
  project_dir = Path(hook_input.get("cwd", ".")).resolve()
203
251
  except (json.JSONDecodeError, KeyError):
252
+ hook_input = {}
204
253
  project_dir = Path(".").resolve()
205
254
 
255
+ configure_project_encoding(project_dir)
256
+
206
257
  trellis_dir = project_dir / ".trellis"
258
+ context_key = _resolve_context_key(project_dir, hook_input)
207
259
 
208
260
  output = StringIO()
209
261
 
@@ -216,7 +268,7 @@ Read and follow all instructions below carefully.
216
268
 
217
269
  output.write("<current-state>\n")
218
270
  context_script = trellis_dir / "scripts" / "get_context.py"
219
- output.write(run_script(context_script))
271
+ output.write(run_script(context_script, context_key))
220
272
  output.write("\n</current-state>\n\n")
221
273
 
222
274
  output.write("<workflow>\n")
@@ -231,8 +283,11 @@ Read and follow all instructions below carefully.
231
283
  "- If you're spawning an implement/check sub-agent, context is injected "
232
284
  "automatically via `{task}/implement.jsonl` / `check.jsonl`. You do NOT "
233
285
  "need to read these indexes yourself.\n"
234
- "- If you're editing code directly in the main session, Read the relevant "
235
- "index(es) on-demand and follow their Pre-Dev Checklist.\n\n"
286
+ "- For agent-capable platforms, the default is to dispatch "
287
+ "`trellis-implement` and `trellis-check` (so JSONL context is loaded by "
288
+ "the sub-agents) rather than editing code in the main session. "
289
+ "Honor a per-turn user override only if the user's current message "
290
+ "explicitly opts out (see <task-status> below for override phrases).\n\n"
236
291
  )
237
292
 
238
293
  # guides/ inlined (cross-package thinking, broadly useful)
@@ -276,19 +331,22 @@ Read and follow all instructions below carefully.
276
331
  )
277
332
  output.write("</guidelines>\n\n")
278
333
 
279
- task_status = _get_task_status(trellis_dir)
334
+ task_status = _get_task_status(trellis_dir, hook_input)
280
335
  output.write(f"<task-status>\n{task_status}\n</task-status>\n\n")
281
336
 
282
337
  output.write("""<ready>
283
338
  Context loaded. Workflow index, project state, and guidelines are already injected above — do NOT re-read them.
284
- Wait for the user's first message, then handle it following the workflow guide.
285
- If there is an active task, ask whether to continue it.
339
+ When the user sends the first message, follow <task-status> and the workflow guide.
340
+ If a task is READY, execute its Next required action without asking whether to continue.
286
341
  </ready>""")
287
342
 
288
343
  context = output.getvalue()
289
344
  result = {
290
345
  "suppressOutput": True,
291
- "systemMessage": f"Trellis context injected ({len(context)} chars)",
346
+ "systemMessage": (
347
+ f"Trellis SessionStart diagnostics emitted ({len(context)} chars); "
348
+ "Copilot currently ignores sessionStart hook output."
349
+ ),
292
350
  "hookSpecificOutput": {
293
351
  "hookEventName": "SessionStart",
294
352
  "additionalContext": context,
@@ -298,7 +298,7 @@ python3 ./.trellis/scripts/task.py add-context "$TASK_DIR" check "<path>" "<reas
298
298
  python3 ./.trellis/scripts/task.py start "$TASK_DIR"
299
299
  ```
300
300
 
301
- This sets `.current-task` so hooks can inject context.
301
+ This sets the active task through Trellis' session resolver so hooks can inject context for this AI session. If the command fails because no session identity is available, rerun it from an IDE/session that exposes session identity or set `TRELLIS_CONTEXT_ID`.
302
302
 
303
303
  ---
304
304
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: trellis-check
3
3
  description: |
4
- Code quality check expert. Reviews code changes against specs and self-fixes issues.
4
+ Trellis quality check agent. Use this exact agent for Trellis task verification, check.jsonl context injection, and self-fixing code review. Do not use generic/default/generalPurpose agents for Trellis checks.
5
5
  tools: Read, Write, Edit, Bash, Glob, Grep, mcp__exa__web_search_exa, mcp__exa__get_code_context_exa
6
6
  ---
7
7
  # Check Agent
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: trellis-implement
3
3
  description: |
4
- Code implementation expert. Understands specs and requirements, then implements features. No git commit allowed.
4
+ Trellis implementation agent. Use this exact agent for Trellis task implementation, implement.jsonl context injection, and hook-injection tests. Do not use generic/default/generalPurpose agents for Trellis implementation. No git commit allowed.
5
5
  tools: Read, Write, Edit, Bash, Glob, Grep, mcp__exa__web_search_exa, mcp__exa__get_code_context_exa
6
6
  ---
7
7
  # Implement Agent
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: trellis-research
3
3
  description: |
4
- Code and tech search expert. Finds files, patterns, and tech solutions, and PERSISTS every finding to the current task's research/ directory. No code modifications outside that directory.
4
+ Trellis research agent. Use this exact agent for Trellis task research and research/ persistence. Do not use generic/default/generalPurpose agents for Trellis research.
5
5
  tools: Read, Write, Glob, Grep, Bash, mcp__exa__web_search_exa, mcp__exa__get_code_context_exa, Skill, mcp__chrome-devtools__*
6
6
  ---
7
7
  # Research Agent
@@ -29,7 +29,7 @@ Conversations get compacted; files don't. Every research output MUST end up as a
29
29
 
30
30
  ### Step 1: Resolve Current Task
31
31
 
32
- Read `.trellis/.current-task` → task directory (e.g. `.trellis/tasks/04-17-foo/`). If empty or missing, ask the user where to write output; do NOT guess.
32
+ Run `python3 ./.trellis/scripts/task.py current --source` → active task path. If no active task is set, ask the user where to write output; do NOT guess.
33
33
 
34
34
  Ensure `{TASK_DIR}/research/` exists:
35
35
 
@@ -4,7 +4,7 @@
4
4
  "preToolUse": [
5
5
  {
6
6
  "command": "{{PYTHON_CMD}} .cursor/hooks/inject-subagent-context.py",
7
- "matcher": "Task",
7
+ "matcher": "Task|Subagent",
8
8
  "timeout": 30
9
9
  }
10
10
  ],
@@ -19,6 +19,12 @@
19
19
  "command": "{{PYTHON_CMD}} .cursor/hooks/inject-workflow-state.py",
20
20
  "timeout": 5
21
21
  }
22
+ ],
23
+ "beforeShellExecution": [
24
+ {
25
+ "command": "{{PYTHON_CMD}} .cursor/hooks/inject-shell-session-context.py",
26
+ "timeout": 5
27
+ }
22
28
  ]
23
29
  }
24
30
  }
@@ -29,7 +29,7 @@ Conversations get compacted; files don't. Every research output MUST end up as a
29
29
 
30
30
  ### Step 1: Resolve Current Task
31
31
 
32
- Read `.trellis/.current-task` → task directory (e.g. `.trellis/tasks/04-17-foo/`). If empty or missing, ask the user where to write output; do NOT guess.
32
+ Run `python3 ./.trellis/scripts/task.py current --source` → active task path. If no active task is set, ask the user where to write output; do NOT guess.
33
33
 
34
34
  Ensure `{TASK_DIR}/research/` exists:
35
35
 
@@ -13,6 +13,12 @@ export declare function getClaudeTemplatePath(): string;
13
13
  * Get the path to the opencode templates directory (agents, plugins, lib).
14
14
  */
15
15
  export declare function getOpenCodeTemplatePath(): string;
16
+ /**
17
+ * Get the path to the Pi Agent templates directory (agents, extension, settings).
18
+ */
19
+ export declare function getPiTemplatePath(): string;
20
+ /** @deprecated Use getPiTemplatePath() instead. */
21
+ export declare function getPiSourcePath(): string;
16
22
  /**
17
23
  * Read a file from the trellis template directory.
18
24
  */
@@ -1 +1 @@
1
- {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/templates/extract.ts"],"names":[],"mappings":"AAQA,KAAK,gBAAgB,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;AAE5D;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAQ/C;AAED,wDAAwD;AACxD,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAQ9C;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAQhD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAI5D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,gBAAgB,EAC1B,QAAQ,EAAE,MAAM,GACf,MAAM,CAGR;AAED,wBAAgB,UAAU,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GACjC,OAAO,CAAC,IAAI,CAAC,CAIf"}
1
+ {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/templates/extract.ts"],"names":[],"mappings":"AAQA,KAAK,gBAAgB,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;AAE5D;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAQ/C;AAED,wDAAwD;AACxD,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAQ9C;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAQhD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAQ1C;AAED,mDAAmD;AACnD,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAI5D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,gBAAgB,EAC1B,QAAQ,EAAE,MAAM,GACf,MAAM,CAGR;AAED,wBAAgB,UAAU,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GACjC,OAAO,CAAC,IAAI,CAAC,CAIf"}
@@ -38,6 +38,20 @@ export function getOpenCodeTemplatePath() {
38
38
  }
39
39
  throw new Error("Could not find opencode templates directory. Expected at templates/opencode/");
40
40
  }
41
+ /**
42
+ * Get the path to the Pi Agent templates directory (agents, extension, settings).
43
+ */
44
+ export function getPiTemplatePath() {
45
+ const templatePath = path.join(__dirname, "pi");
46
+ if (fs.existsSync(templatePath)) {
47
+ return templatePath;
48
+ }
49
+ throw new Error("Could not find pi templates directory. Expected at templates/pi/");
50
+ }
51
+ /** @deprecated Use getPiTemplatePath() instead. */
52
+ export function getPiSourcePath() {
53
+ return getPiTemplatePath();
54
+ }
41
55
  /**
42
56
  * Read a file from the trellis template directory.
43
57
  */
@@ -1 +1 @@
1
- {"version":3,"file":"extract.js","sourceRoot":"","sources":["../../src/templates/extract.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"extract.js","sourceRoot":"","sources":["../../src/templates/extract.ts"],"names":[],"mappings":"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"}
@@ -29,7 +29,7 @@ Conversations get compacted; files don't. Every research output MUST end up as a
29
29
 
30
30
  ### Step 1: Resolve Current Task
31
31
 
32
- Read `.trellis/.current-task` → task directory (e.g. `.trellis/tasks/04-17-foo/`). If empty or missing, ask the user where to write output; do NOT guess.
32
+ Run `python3 ./.trellis/scripts/task.py current --source` → active task path. If no active task is set, ask the user where to write output; do NOT guess.
33
33
 
34
34
  Ensure `{TASK_DIR}/research/` exists:
35
35