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

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.
@@ -0,0 +1,9 @@
1
+ {
2
+ "version": "0.5.0-beta.14",
3
+ "description": "Patch follow-up to beta.13: SessionStart hooks now emit a one-shot `<first-reply-notice>` block so the first visible assistant reply announces that Trellis context has been injected (proof-of-load for noisy or summarized hosts). READY-state breadcrumbs across shared/opencode/codex/copilot hooks + `trellis/workflow.md` replace the soft \"Continue with implement or check\" wording with an explicit \"Next required action: dispatch `trellis-implement` … do NOT edit code in the main session\" directive, closing a loophole where agent-capable sessions would still hand-edit code in the main thread. Windows statusline no longer crashes on typed stdout/stderr streams: `sys.stdout.detach() + TextIOWrapper(...)` is replaced with `reconfigure(encoding='utf-8', errors='replace')`. No src/ runtime-code changes outside templates; `trellis update` is a straight content refresh.",
4
+ "breaking": false,
5
+ "recommendMigrate": false,
6
+ "changelog": "**Enhancements:**\n- feat(hooks): SessionStart emits a one-shot `<first-reply-notice>` block instructing the model to open its first visible reply with one short Chinese sentence confirming Trellis context has been injected (workflow, task status, developer identity, git status, active tasks, spec index). Applied to `shared-hooks/session-start.py`, `codex/hooks/session-start.py`, `opencode/plugins/session-start.js`. Notice is explicitly one-shot — not repeated on later turns in the same session. Closes the visibility gap on hosts that collapse or summarize hook output.\n- feat(hooks): READY-state breadcrumbs now say \"Next required action: dispatch `trellis-implement` per Phase 2.1. For agent-capable platforms, do NOT edit code in the main session. After implementation, dispatch `trellis-check` per Phase 2.2 before reporting completion.\" Replaces the old \"Next: Continue with implement or check\" wording that let the main thread silently hand-edit code. Applied uniformly across `shared-hooks/session-start.py` + `inject-workflow-state.py`, `opencode/plugins/session-start.js` + `inject-workflow-state.js`, `codex/hooks/session-start.py`, `copilot/hooks/session-start.py`, and the `[workflow-state:in_progress]` block in `trellis/workflow.md`. `<ready>` directive also drops the \"ask whether to continue\" prompt in favor of \"execute its Next required action\".\n\n**Bug Fixes:**\n- fix(statusline): Windows UTF-8 setup replaces `sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding='utf-8')` with `stream.reconfigure(encoding='utf-8', errors='replace')`. The old pattern crashed on hosts that wrap stdout/stderr with typed streams (Copilot / certain Codex environments) because `detach()` is not available on typed wrappers. `reconfigure` is the supported API on Python 3.7+ text streams and is a no-op when unavailable. Fix applied to `shared-hooks/statusline.py`.\n- fix(hooks): Codex and Copilot SessionStart hooks now invoke a shared `configure_project_encoding(project_dir)` helper before emitting JSON, loading `.trellis/scripts/common.configure_encoding()` when present. Prevents stray mojibake in hook stdout on Windows Codex/Copilot runs that haven't already wrapped stdout.\n- docs(copilot): `copilot/hooks/session-start.py` docstring + `systemMessage` text now state plainly that GitHub Copilot currently ignores `sessionStart` hook output. The JSON shape is kept for protocol parity and future host support, but users should rely on `UserPromptSubmit` breadcrumbs and hook logs for Copilot today.",
7
+ "migrations": [],
8
+ "notes": "Pure content refresh. Existing tasks, jsonl files, and `.current-task` are preserved. After `trellis update`, fresh sessions across Claude Code / Codex / Copilot / OpenCode will prepend a one-line Chinese confirmation of SessionStart context on the first assistant reply, and the main thread will no longer be nudged to hand-edit code when a task is READY. Windows statusline stops crashing on Copilot/Codex typed stdout. No action needed beyond `trellis update`."
9
+ }
@@ -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
 
@@ -145,7 +165,12 @@ def _get_task_status(trellis_dir: Path) -> str:
145
165
  if not has_context:
146
166
  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
167
 
148
- return f"Status: READY\nTask: {task_title}\nNext: Continue with implement or check"
168
+ return (
169
+ f"Status: READY\nTask: {task_title}\n"
170
+ "Next required action: dispatch `trellis-implement` per Phase 2.1. "
171
+ "For agent-capable platforms, do NOT edit code in the main session. "
172
+ "After implementation, dispatch `trellis-check` per Phase 2.2 before reporting completion."
173
+ )
149
174
 
150
175
 
151
176
  def _extract_range(content: str, start_header: str, end_header: str) -> str:
@@ -203,6 +228,8 @@ def main() -> None:
203
228
  except (json.JSONDecodeError, KeyError):
204
229
  project_dir = Path(".").resolve()
205
230
 
231
+ configure_project_encoding(project_dir)
232
+
206
233
  trellis_dir = project_dir / ".trellis"
207
234
 
208
235
  output = StringIO()
@@ -213,6 +240,8 @@ Read and follow all instructions below carefully.
213
240
  </session-context>
214
241
 
215
242
  """)
243
+ output.write(FIRST_REPLY_NOTICE)
244
+ output.write("\n\n")
216
245
 
217
246
  output.write("<current-state>\n")
218
247
  context_script = trellis_dir / "scripts" / "get_context.py"
@@ -231,8 +260,9 @@ Read and follow all instructions below carefully.
231
260
  "- If you're spawning an implement/check sub-agent, context is injected "
232
261
  "automatically via `{task}/implement.jsonl` / `check.jsonl`. You do NOT "
233
262
  "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"
263
+ "- For agent-capable platforms, do NOT edit code directly in the main "
264
+ "session; dispatch `trellis-implement` and `trellis-check` so JSONL "
265
+ "context is loaded by the sub-agents.\n\n"
236
266
  )
237
267
 
238
268
  # guides/ inlined (cross-package thinking, broadly useful)
@@ -281,8 +311,8 @@ Read and follow all instructions below carefully.
281
311
 
282
312
  output.write("""<ready>
283
313
  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.
314
+ When the user sends the first message, follow <task-status> and the workflow guide.
315
+ If a task is READY, execute its Next required action without asking whether to continue.
286
316
  </ready>""")
287
317
 
288
318
  context = output.getvalue()
@@ -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
 
@@ -145,7 +162,12 @@ def _get_task_status(trellis_dir: Path) -> str:
145
162
  if not has_context:
146
163
  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
164
 
148
- return f"Status: READY\nTask: {task_title}\nNext: Continue with implement or check"
165
+ return (
166
+ f"Status: READY\nTask: {task_title}\n"
167
+ "Next required action: dispatch `trellis-implement` per Phase 2.1. "
168
+ "For agent-capable platforms, do NOT edit code in the main session. "
169
+ "After implementation, dispatch `trellis-check` per Phase 2.2 before reporting completion."
170
+ )
149
171
 
150
172
 
151
173
  def _extract_range(content: str, start_header: str, end_header: str) -> str:
@@ -203,6 +225,8 @@ def main() -> None:
203
225
  except (json.JSONDecodeError, KeyError):
204
226
  project_dir = Path(".").resolve()
205
227
 
228
+ configure_project_encoding(project_dir)
229
+
206
230
  trellis_dir = project_dir / ".trellis"
207
231
 
208
232
  output = StringIO()
@@ -231,8 +255,9 @@ Read and follow all instructions below carefully.
231
255
  "- If you're spawning an implement/check sub-agent, context is injected "
232
256
  "automatically via `{task}/implement.jsonl` / `check.jsonl`. You do NOT "
233
257
  "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"
258
+ "- For agent-capable platforms, do NOT edit code directly in the main "
259
+ "session; dispatch `trellis-implement` and `trellis-check` so JSONL "
260
+ "context is loaded by the sub-agents.\n\n"
236
261
  )
237
262
 
238
263
  # guides/ inlined (cross-package thinking, broadly useful)
@@ -281,14 +306,17 @@ Read and follow all instructions below carefully.
281
306
 
282
307
  output.write("""<ready>
283
308
  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.
309
+ When the user sends the first message, follow <task-status> and the workflow guide.
310
+ If a task is READY, execute its Next required action without asking whether to continue.
286
311
  </ready>""")
287
312
 
288
313
  context = output.getvalue()
289
314
  result = {
290
315
  "suppressOutput": True,
291
- "systemMessage": f"Trellis context injected ({len(context)} chars)",
316
+ "systemMessage": (
317
+ f"Trellis SessionStart diagnostics emitted ({len(context)} chars); "
318
+ "Copilot currently ignores sessionStart hook output."
319
+ ),
292
320
  "hookSpecificOutput": {
293
321
  "hookEventName": "SessionStart",
294
322
  "additionalContext": context,
@@ -58,7 +58,11 @@ const FALLBACK_BREADCRUMBS = {
58
58
  "main session — PRD only links to research files.",
59
59
  in_progress:
60
60
  "Flow: trellis-implement → trellis-check → trellis-update-spec → finish\n" +
61
- "Check conversation history + git status to determine current step; do NOT skip trellis-check.",
61
+ "Next required action: inspect conversation history + git status, then " +
62
+ "execute the next uncompleted step in that sequence.\n" +
63
+ "For agent-capable platforms, do NOT edit code in the main session; " +
64
+ "dispatch `trellis-implement` for implementation and dispatch " +
65
+ "`trellis-check` before reporting completion.",
62
66
  completed:
63
67
  "User commits changes; then run task.py archive.",
64
68
  }
@@ -14,6 +14,12 @@ import { TrellisContext, contextCollector, debugLog } from "../lib/trellis-conte
14
14
 
15
15
  const PYTHON_CMD = platform() === "win32" ? "python" : "python3"
16
16
 
17
+ const FIRST_REPLY_NOTICE = `<first-reply-notice>
18
+ On the first visible assistant reply in this session, begin with exactly one short Chinese sentence:
19
+ Trellis SessionStart 已注入:workflow、当前任务状态、开发者身份、git 状态、active tasks、spec 索引已加载。
20
+ 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.
21
+ </first-reply-notice>`
22
+
17
23
 
18
24
  /**
19
25
  * Return true iff jsonl has at least one row with a `file` field.
@@ -95,7 +101,12 @@ function getTaskStatus(ctx) {
95
101
  return `Status: NOT READY\nTask: ${taskTitle}\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\``
96
102
  }
97
103
 
98
- return `Status: READY\nTask: ${taskTitle}\nNext: Continue with implement or check`
104
+ return (
105
+ `Status: READY\nTask: ${taskTitle}\n` +
106
+ "Next required action: dispatch `trellis-implement` per Phase 2.1. " +
107
+ "For agent-capable platforms, do NOT edit code in the main session. " +
108
+ "After implementation, dispatch `trellis-check` per Phase 2.2 before reporting completion."
109
+ )
99
110
  }
100
111
 
101
112
  /**
@@ -215,7 +226,7 @@ function resolveSpecScope(config) {
215
226
  /**
216
227
  * Build session context for injection
217
228
  */
218
- function buildSessionContext(ctx) {
229
+ export function buildSessionContext(ctx) {
219
230
  const directory = ctx.directory
220
231
  const trellisDir = join(directory, ".trellis")
221
232
 
@@ -229,6 +240,7 @@ function buildSessionContext(ctx) {
229
240
  You are starting a new session in a Trellis-managed project.
230
241
  Read and follow all instructions below carefully.
231
242
  </trellis-context>`)
243
+ parts.push(FIRST_REPLY_NOTICE)
232
244
 
233
245
  // Legacy migration warning
234
246
  const legacyWarning = checkLegacySpec(directory, config)
@@ -288,8 +300,7 @@ Read and follow all instructions below carefully.
288
300
  }
289
301
 
290
302
  // 4. Guidelines — paths-only for most indexes; guides/ inlined (cross-package,
291
- // broadly useful). Sub-agents get their specific specs via jsonl injection
292
- // main agent reads paths on demand when editing code directly.
303
+ // broadly useful). Sub-agents get their specific specs via jsonl injection.
293
304
  parts.push("<guidelines>")
294
305
  parts.push(
295
306
  "Project spec indexes are listed by path below. Each index contains a " +
@@ -298,8 +309,9 @@ Read and follow all instructions below carefully.
298
309
  "- If you're spawning an implement/check sub-agent, context is injected " +
299
310
  "automatically via `{task}/implement.jsonl` / `check.jsonl`. You do NOT " +
300
311
  "need to read these indexes yourself.\n" +
301
- "- If you're editing code directly in the main session, Read the relevant " +
302
- "index(es) on-demand and follow their Pre-Dev Checklist.\n"
312
+ "- For agent-capable platforms, do NOT edit code directly in the main " +
313
+ "session; dispatch `trellis-implement` and `trellis-check` so JSONL " +
314
+ "context is loaded by the sub-agents.\n"
303
315
  )
304
316
 
305
317
  const specDir = join(directory, ".trellis", "spec")
@@ -379,8 +391,8 @@ Read and follow all instructions below carefully.
379
391
  // 7. Final directive
380
392
  parts.push(`<ready>
381
393
  Context loaded. Workflow index, project state, and guidelines are already injected above — do NOT re-read them.
382
- Wait for the user's first message, then handle it following the workflow guide.
383
- If there is an active task, ask whether to continue it.
394
+ When the user sends the first message, follow <task-status> and the workflow guide.
395
+ If a task is READY, execute its Next required action without asking whether to continue.
384
396
  </ready>`)
385
397
 
386
398
  return parts.join("\n\n")
@@ -153,8 +153,11 @@ _FALLBACK_BREADCRUMBS = {
153
153
  ),
154
154
  "in_progress": (
155
155
  "Flow: trellis-implement → trellis-check → trellis-update-spec → finish\n"
156
- "Check conversation history + git status to determine current step; "
157
- "do NOT skip trellis-check."
156
+ "Next required action: inspect conversation history + git status, then "
157
+ "execute the next uncompleted step in that sequence.\n"
158
+ "For agent-capable platforms, do NOT edit code in the main session; "
159
+ "dispatch `trellis-implement` for implementation and dispatch "
160
+ "`trellis-check` before reporting completion."
158
161
  ),
159
162
  "completed": (
160
163
  "User commits changes; then run task.py archive."
@@ -16,6 +16,12 @@ import sys
16
16
  from io import StringIO
17
17
  from pathlib import Path
18
18
 
19
+ FIRST_REPLY_NOTICE = """<first-reply-notice>
20
+ On the first visible assistant reply in this session, begin with exactly one short Chinese sentence:
21
+ Trellis SessionStart 已注入:workflow、当前任务状态、开发者身份、git 状态、active tasks、spec 索引已加载。
22
+ 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.
23
+ </first-reply-notice>"""
24
+
19
25
  # IMPORTANT: Force stdout to use UTF-8 on Windows
20
26
  # This fixes UnicodeEncodeError when outputting non-ASCII characters
21
27
  if sys.platform.startswith("win"):
@@ -209,10 +215,9 @@ def _get_task_status(trellis_dir: Path) -> str:
209
215
  # Case 5: PRD + curated jsonl (or agent-less platform with no jsonl) — enter Execute phase
210
216
  return (
211
217
  f"Status: READY\nTask: {task_title}\n"
212
- "Next-Action: Follow Phase 2.1 for your platform. For agent-capable platforms, "
213
- "spawn `trellis-implement` via the Task tool; if you stay in the main session, "
214
- "load `trellis-before-dev` before writing code. "
215
- "After implementation, spawn `trellis-check` sub-agent for quality verification.\n"
218
+ "Next required action: dispatch `trellis-implement` per Phase 2.1. "
219
+ "For agent-capable platforms, do NOT edit code in the main session. "
220
+ "After implementation, dispatch `trellis-check` per Phase 2.2 before reporting completion.\n"
216
221
  "Sub-agent roster: `trellis-implement` (writes code), `trellis-check` (verifies + self-fixes), "
217
222
  "`trellis-research` (persists findings to `research/*.md` — use when you'd otherwise do "
218
223
  "multiple WebFetch/WebSearch inline)."
@@ -469,6 +474,8 @@ Read and follow all instructions below carefully.
469
474
  </session-context>
470
475
 
471
476
  """)
477
+ output.write(FIRST_REPLY_NOTICE)
478
+ output.write("\n\n")
472
479
 
473
480
  # Legacy migration warning
474
481
  legacy_warning = _check_legacy_spec(trellis_dir, is_mono, packages)
@@ -492,8 +499,9 @@ Read and follow all instructions below carefully.
492
499
  "- If you're spawning an implement/check sub-agent, context is injected "
493
500
  "automatically via `{task}/implement.jsonl` / `check.jsonl`. You do NOT "
494
501
  "need to read these indexes yourself.\n"
495
- "- If you're editing code directly in the main session, Read the relevant "
496
- "index(es) on-demand and follow their Pre-Dev Checklist.\n\n"
502
+ "- For agent-capable platforms, do NOT edit code directly in the main "
503
+ "session; dispatch `trellis-implement` and `trellis-check` so JSONL "
504
+ "context is loaded by the sub-agents.\n\n"
497
505
  )
498
506
 
499
507
  # guides/ is cross-package thinking — always include inline (small, broadly useful)
@@ -550,8 +558,8 @@ Read and follow all instructions below carefully.
550
558
 
551
559
  output.write("""<ready>
552
560
  Context loaded. Workflow index, project state, and guidelines are already injected above — do NOT re-read them.
553
- Wait for the user's first message, then handle it following the workflow guide.
554
- If there is an active task, ask whether to continue it.
561
+ When the user sends the first message, follow <task-status> and the workflow guide.
562
+ If a task is READY, execute its Next required action without asking whether to continue.
555
563
  </ready>""")
556
564
 
557
565
  result = {
@@ -11,7 +11,6 @@ Info line: model · ctx% · branch · duration · developer · tasks · rate lim
11
11
  """
12
12
  from __future__ import annotations
13
13
 
14
- import io
15
14
  import json
16
15
  import re
17
16
  import subprocess
@@ -21,8 +20,10 @@ from pathlib import Path
21
20
  # Fix: Windows Python defaults to GBK encoding, which corrupts UTF-8
22
21
  # characters like the middle dot (·). Wrap stdout/stderr with UTF-8.
23
22
  if sys.platform == "win32":
24
- sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding="utf-8")
25
- sys.stderr = io.TextIOWrapper(sys.stderr.detach(), encoding="utf-8")
23
+ for stream in (sys.stdout, sys.stderr):
24
+ reconfigure = getattr(stream, "reconfigure", None)
25
+ if callable(reconfigure):
26
+ reconfigure(encoding="utf-8", errors="replace")
26
27
 
27
28
 
28
29
  def _read_text(path: Path) -> str:
@@ -471,7 +471,8 @@ Research belongs in `{task_dir}/research/*.md`, written by `trellis-research` su
471
471
 
472
472
  [workflow-state:in_progress]
473
473
  Flow: trellis-implement → trellis-check → trellis-update-spec → finish
474
- Check conversation history + git status to determine current step; do NOT skip trellis-check.
474
+ Next required action: inspect conversation history + git status, then execute the next uncompleted step in that sequence.
475
+ For agent-capable platforms, do NOT edit code in the main session; dispatch `trellis-implement` for implementation and dispatch `trellis-check` before reporting completion.
475
476
  [/workflow-state:in_progress]
476
477
 
477
478
  [workflow-state:completed]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindfoldhq/trellis",
3
- "version": "0.5.0-beta.13",
3
+ "version": "0.5.0-beta.14",
4
4
  "description": "AI capabilities grow like ivy — Trellis provides the structure to guide them along a disciplined path",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",