@mindfoldhq/trellis 0.6.0-beta.10 → 0.6.0-beta.12

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 (112) hide show
  1. package/dist/commands/channel/create.d.ts +5 -0
  2. package/dist/commands/channel/create.d.ts.map +1 -1
  3. package/dist/commands/channel/create.js +19 -15
  4. package/dist/commands/channel/create.js.map +1 -1
  5. package/dist/commands/channel/index.d.ts.map +1 -1
  6. package/dist/commands/channel/index.js +90 -9
  7. package/dist/commands/channel/index.js.map +1 -1
  8. package/dist/commands/channel/kill.d.ts +1 -0
  9. package/dist/commands/channel/kill.d.ts.map +1 -1
  10. package/dist/commands/channel/kill.js +16 -13
  11. package/dist/commands/channel/kill.js.map +1 -1
  12. package/dist/commands/channel/list.d.ts +1 -0
  13. package/dist/commands/channel/list.d.ts.map +1 -1
  14. package/dist/commands/channel/list.js +18 -6
  15. package/dist/commands/channel/list.js.map +1 -1
  16. package/dist/commands/channel/messages.d.ts +3 -0
  17. package/dist/commands/channel/messages.d.ts.map +1 -1
  18. package/dist/commands/channel/messages.js +88 -58
  19. package/dist/commands/channel/messages.js.map +1 -1
  20. package/dist/commands/channel/rm.d.ts +2 -0
  21. package/dist/commands/channel/rm.d.ts.map +1 -1
  22. package/dist/commands/channel/rm.js +15 -5
  23. package/dist/commands/channel/rm.js.map +1 -1
  24. package/dist/commands/channel/run.js +1 -1
  25. package/dist/commands/channel/run.js.map +1 -1
  26. package/dist/commands/channel/send.d.ts +2 -0
  27. package/dist/commands/channel/send.d.ts.map +1 -1
  28. package/dist/commands/channel/send.js +9 -10
  29. package/dist/commands/channel/send.js.map +1 -1
  30. package/dist/commands/channel/spawn.d.ts +1 -0
  31. package/dist/commands/channel/spawn.d.ts.map +1 -1
  32. package/dist/commands/channel/spawn.js +14 -11
  33. package/dist/commands/channel/spawn.js.map +1 -1
  34. package/dist/commands/channel/store/events.d.ts +61 -6
  35. package/dist/commands/channel/store/events.d.ts.map +1 -1
  36. package/dist/commands/channel/store/events.js +53 -9
  37. package/dist/commands/channel/store/events.js.map +1 -1
  38. package/dist/commands/channel/store/filter.d.ts +33 -0
  39. package/dist/commands/channel/store/filter.d.ts.map +1 -0
  40. package/dist/commands/channel/store/filter.js +63 -0
  41. package/dist/commands/channel/store/filter.js.map +1 -0
  42. package/dist/commands/channel/store/paths.d.ts +7 -0
  43. package/dist/commands/channel/store/paths.d.ts.map +1 -1
  44. package/dist/commands/channel/store/paths.js +76 -17
  45. package/dist/commands/channel/store/paths.js.map +1 -1
  46. package/dist/commands/channel/store/schema.d.ts +36 -0
  47. package/dist/commands/channel/store/schema.d.ts.map +1 -0
  48. package/dist/commands/channel/store/schema.js +91 -0
  49. package/dist/commands/channel/store/schema.js.map +1 -0
  50. package/dist/commands/channel/store/thread-state.d.ts +19 -0
  51. package/dist/commands/channel/store/thread-state.d.ts.map +1 -0
  52. package/dist/commands/channel/store/thread-state.js +81 -0
  53. package/dist/commands/channel/store/thread-state.js.map +1 -0
  54. package/dist/commands/channel/store/watch.d.ts +4 -21
  55. package/dist/commands/channel/store/watch.d.ts.map +1 -1
  56. package/dist/commands/channel/store/watch.js +8 -54
  57. package/dist/commands/channel/store/watch.js.map +1 -1
  58. package/dist/commands/channel/supervisor.d.ts +1 -1
  59. package/dist/commands/channel/supervisor.d.ts.map +1 -1
  60. package/dist/commands/channel/supervisor.js +11 -10
  61. package/dist/commands/channel/supervisor.js.map +1 -1
  62. package/dist/commands/channel/threads.d.ts +28 -0
  63. package/dist/commands/channel/threads.d.ts.map +1 -0
  64. package/dist/commands/channel/threads.js +96 -0
  65. package/dist/commands/channel/threads.js.map +1 -0
  66. package/dist/commands/channel/wait.d.ts +3 -0
  67. package/dist/commands/channel/wait.d.ts.map +1 -1
  68. package/dist/commands/channel/wait.js +9 -8
  69. package/dist/commands/channel/wait.js.map +1 -1
  70. package/dist/commands/init.d.ts.map +1 -1
  71. package/dist/commands/init.js +70 -41
  72. package/dist/commands/init.js.map +1 -1
  73. package/dist/commands/uninstall.d.ts.map +1 -1
  74. package/dist/commands/uninstall.js +28 -2
  75. package/dist/commands/uninstall.js.map +1 -1
  76. package/dist/commands/update.d.ts.map +1 -1
  77. package/dist/commands/update.js +21 -1
  78. package/dist/commands/update.js.map +1 -1
  79. package/dist/configurators/claude.d.ts.map +1 -1
  80. package/dist/configurators/claude.js +1 -0
  81. package/dist/configurators/claude.js.map +1 -1
  82. package/dist/migrations/manifests/0.5.14.json +9 -0
  83. package/dist/migrations/manifests/0.5.15.json +9 -0
  84. package/dist/migrations/manifests/0.6.0-beta.11.json +9 -0
  85. package/dist/migrations/manifests/0.6.0-beta.12.json +9 -0
  86. package/dist/templates/codex/hooks/session-start.py +22 -0
  87. package/dist/templates/codex/hooks.json +1 -1
  88. package/dist/templates/copilot/hooks/session-start.py +24 -0
  89. package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md.txt +128 -9
  90. package/dist/templates/markdown/spec/guides/cross-layer-thinking-guide.md.txt +62 -0
  91. package/dist/templates/markdown/spec/guides/index.md.txt +18 -0
  92. package/dist/templates/shared-hooks/inject-workflow-state.py +22 -0
  93. package/dist/templates/shared-hooks/session-start.py +19 -6
  94. package/dist/templates/trellis/scripts/common/safe_commit.py +49 -19
  95. package/dist/templates/trellis/scripts/common/task_store.py +42 -12
  96. package/dist/utils/cwd-guard.d.ts +38 -0
  97. package/dist/utils/cwd-guard.d.ts.map +1 -0
  98. package/dist/utils/cwd-guard.js +62 -0
  99. package/dist/utils/cwd-guard.js.map +1 -0
  100. package/dist/utils/file-writer.d.ts +13 -0
  101. package/dist/utils/file-writer.d.ts.map +1 -1
  102. package/dist/utils/file-writer.js +59 -1
  103. package/dist/utils/file-writer.js.map +1 -1
  104. package/dist/utils/manifest-prune.d.ts +61 -0
  105. package/dist/utils/manifest-prune.d.ts.map +1 -0
  106. package/dist/utils/manifest-prune.js +136 -0
  107. package/dist/utils/manifest-prune.js.map +1 -0
  108. package/dist/utils/template-hash.d.ts +32 -6
  109. package/dist/utils/template-hash.d.ts.map +1 -1
  110. package/dist/utils/template-hash.js +53 -31
  111. package/dist/utils/template-hash.js.map +1 -1
  112. package/package.json +1 -1
@@ -71,6 +71,35 @@ For each boundary:
71
71
 
72
72
  **Good**: Each layer only knows its neighbors
73
73
 
74
+ ### Mistake 4: Every Consumer Parses The Same Payload
75
+
76
+ **Bad**: A command reads JSONL events and casts fields inline:
77
+
78
+ ```typescript
79
+ const thread = (ev as { thread?: string }).thread;
80
+ const labels = (ev as { labels?: string[] }).labels;
81
+ ```
82
+
83
+ This looks local, but it means every consumer owns a private version of the
84
+ event contract. The next field change will update one command and miss another.
85
+
86
+ **Good**: Decode once at the event boundary, then export typed projections:
87
+
88
+ ```typescript
89
+ if (!isThreadEvent(ev)) return false;
90
+ return ev.thread === filter.thread;
91
+ ```
92
+
93
+ **Rule**: For append-only logs, JSON streams, RPC payloads, or config files,
94
+ create one owner for:
95
+
96
+ - event / payload type definitions
97
+ - type guards and normalization from `unknown`
98
+ - metadata projections used by UI commands
99
+ - reducers that replay state from the source of truth
100
+
101
+ Rendering code may format fields, but it must not redefine the payload contract.
102
+
74
103
  ---
75
104
 
76
105
  ## Checklist for Cross-Layer Features
@@ -87,6 +116,10 @@ After implementation:
87
116
  - [ ] Tested with edge cases (null, empty, invalid)
88
117
  - [ ] Verified error handling at each boundary
89
118
  - [ ] Checked data survives round-trip
119
+ - [ ] Checked that consumers import shared decoders / projections instead of
120
+ casting payload fields locally
121
+ - [ ] Checked that derived state points back to the source event identifier
122
+ (`seq`, `id`, `version`) instead of inventing a second cursor
90
123
 
91
124
  ---
92
125
 
@@ -195,3 +228,32 @@ Create detailed flow docs when:
195
228
  - Multiple teams are involved
196
229
  - Data format is complex
197
230
  - Feature has caused bugs before
231
+
232
+ ---
233
+
234
+ ## Event Log / Projection Boundary
235
+
236
+ Append-only logs are cross-layer contracts. A single event travels through:
237
+
238
+ ```
239
+ CLI input → event writer → events.jsonl → reader → filter → reducer → display
240
+ ```
241
+
242
+ ### Checklist: After Adding A New Event Kind Or Field
243
+
244
+ - [ ] Add the event kind to the central event taxonomy
245
+ - [ ] Add a typed event variant or type guard at the event layer
246
+ - [ ] Add normalization helpers for array/object fields that come from
247
+ user input or JSON
248
+ - [ ] Keep `seq` / `id` assignment in the event writer only
249
+ - [ ] Make filters and reducers consume the typed event guard, not local casts
250
+ - [ ] Make display code consume reducer output or typed events, not raw JSON
251
+ - [ ] Add at least one regression that proves history replay and live filtering
252
+ use the same filter model
253
+
254
+ **Real-world example**: Thread channels added `kind: "thread"`, `description`,
255
+ `linkedContext`, labels, and `lastSeq`. The first implementation replayed state
256
+ correctly, but several commands still re-parsed event payload fields with local
257
+ casts. The fix was to make `store/events.ts` own `ThreadChannelEvent`,
258
+ `isThreadEvent`, and `metadataFromCreateEvent`, while `store/thread-state.ts`
259
+ became the only replay reducer.
@@ -34,6 +34,8 @@ These guides help you **ask the right questions before coding**.
34
34
  - [ ] Data format changes between layers
35
35
  - [ ] Multiple consumers need the same data
36
36
  - [ ] You're not sure where to put some logic
37
+ - [ ] You are adding an event kind, JSONL record, RPC payload, or config field
38
+ - [ ] UI / command code starts casting raw payload fields directly
37
39
 
38
40
  → Read [Cross-Layer Thinking Guide](./cross-layer-thinking-guide.md)
39
41
 
@@ -44,9 +46,25 @@ These guides help you **ask the right questions before coding**.
44
46
  - [ ] You're adding a new field to multiple places
45
47
  - [ ] **You're modifying any constant or config**
46
48
  - [ ] **You're creating a new utility/helper function** ← Search first!
49
+ - [ ] Two files read the same untyped payload field with local casts
50
+ - [ ] Multiple branches update the same derived state from `kind` / `action`
47
51
 
48
52
  → Read [Code Reuse Thinking Guide](./code-reuse-thinking-guide.md)
49
53
 
54
+ ### When Verifying AI Cross-Review Results
55
+
56
+ - [ ] Reviewer claims "user input can be malicious" → Check the actual data source (internal manifest? user config? external API?)
57
+ - [ ] Reviewer flags "missing validation" → Is the data from a trusted internal source?
58
+ - [ ] Reviewer says "behavior change" → Read the code comments — is it intentional design?
59
+ - [ ] Reviewer identifies a "bug" in test → Mentally delete the feature being tested — does the test still pass? If yes → tautological test
60
+
61
+ **Common AI reviewer false-positive patterns**:
62
+ 1. **Trust boundary confusion**: Treating internal data (bundled JSON manifests) as untrusted external input
63
+ 2. **Ignoring design comments**: Flagging intentional behavior documented in code comments as bugs
64
+ 3. **Variable misreading**: Not tracing a variable to its actual definition (e.g., Map keyed by path vs name)
65
+
66
+ **Verification rule**: Every CRITICAL/WARNING finding must be verified against the actual code before prioritizing. Budget ~35% false-positive rate for AI reviews.
67
+
50
68
  ---
51
69
 
52
70
  ## Pre-Modification Rule (CRITICAL)
@@ -33,6 +33,28 @@ import os
33
33
  import re
34
34
  import sys
35
35
  from pathlib import Path
36
+
37
+ # Force UTF-8 on stdin/stdout/stderr on Windows. Default codepage there is
38
+ # cp936 / cp1252 / etc. — non-ASCII content (Chinese task names, prd snippets)
39
+ # both in stdin (hook payload from host CLI) and stdout (our emitted blocks)
40
+ # raises UnicodeDecodeError / UnicodeEncodeError. Equivalent to `python -X utf8`
41
+ # but applied per-stream so we don't depend on host CLI's command wiring.
42
+ if sys.platform.startswith("win"):
43
+ import io as _io
44
+ for _stream_name in ("stdin", "stdout", "stderr"):
45
+ _stream = getattr(sys, _stream_name, None)
46
+ if _stream is None:
47
+ continue
48
+ if hasattr(_stream, "reconfigure"):
49
+ try:
50
+ _stream.reconfigure(encoding="utf-8", errors="replace") # type: ignore[union-attr]
51
+ except Exception:
52
+ pass
53
+ elif hasattr(_stream, "detach"):
54
+ try:
55
+ setattr(sys, _stream_name, _io.TextIOWrapper(_stream.detach(), encoding="utf-8", errors="replace"))
56
+ except Exception:
57
+ pass
36
58
  from typing import Optional
37
59
 
38
60
 
@@ -72,14 +72,27 @@ First visible reply: say once in Chinese that Trellis SessionStart context is lo
72
72
  This notice is one-shot: do not repeat it after the first assistant reply in the same session.
73
73
  </first-reply-notice>"""
74
74
 
75
- # IMPORTANT: Force stdout to use UTF-8 on Windows
76
- # This fixes UnicodeEncodeError when outputting non-ASCII characters
75
+ # Force UTF-8 on stdin/stdout/stderr on Windows. Default codepage there is
76
+ # cp936 / cp1252 / etc. non-ASCII content (Chinese task names, prd snippets)
77
+ # both in stdin (hook payload from host CLI) and stdout (our emitted blocks)
78
+ # raises UnicodeDecodeError / UnicodeEncodeError. Equivalent to `python -X utf8`
79
+ # but applied per-stream so we don't depend on host CLI's command wiring.
77
80
  if sys.platform.startswith("win"):
78
81
  import io as _io
79
- if hasattr(sys.stdout, "reconfigure"):
80
- sys.stdout.reconfigure(encoding="utf-8", errors="replace") # type: ignore[union-attr]
81
- elif hasattr(sys.stdout, "detach"):
82
- sys.stdout = _io.TextIOWrapper(sys.stdout.detach(), encoding="utf-8", errors="replace") # type: ignore[union-attr]
82
+ for _stream_name in ("stdin", "stdout", "stderr"):
83
+ _stream = getattr(sys, _stream_name, None)
84
+ if _stream is None:
85
+ continue
86
+ if hasattr(_stream, "reconfigure"):
87
+ try:
88
+ _stream.reconfigure(encoding="utf-8", errors="replace") # type: ignore[union-attr]
89
+ except Exception:
90
+ pass
91
+ elif hasattr(_stream, "detach"):
92
+ try:
93
+ setattr(sys, _stream_name, _io.TextIOWrapper(_stream.detach(), encoding="utf-8", errors="replace"))
94
+ except Exception:
95
+ pass
83
96
 
84
97
 
85
98
 
@@ -111,31 +111,61 @@ def safe_trellis_paths_to_add(repo_root: Path) -> list[str]:
111
111
  return paths
112
112
 
113
113
 
114
- def safe_archive_paths_to_add(repo_root: Path) -> list[str]:
114
+ def safe_archive_paths_to_add(
115
+ repo_root: Path,
116
+ task_name: str | None = None,
117
+ modified_children: list[str] | None = None,
118
+ ) -> list[str]:
115
119
  """Return paths to stage after `task.py archive`.
116
120
 
117
- Limited to the archive subtree (where the freshly-moved task lives) plus
118
- the source task directory's parent area to capture the deletion in the
119
- same commit. We pass the whole `.trellis/tasks/` path so deletions of the
120
- pre-move path are tracked, but only as a SPECIFIC subpath — not the whole
121
- `.trellis/` tree.
121
+ Scoped to ONLY the paths the archive operation actually touched:
122
+
123
+ - the archive subtree (where the freshly-moved task lives)
124
+ - the source task directory (for source-side deletes; caller pairs
125
+ this with `git rm --cached` since `git add` won't stage deletes
126
+ for a path that no longer exists in the working tree)
127
+ - any child task directories whose `task.json` was edited to drop
128
+ the archived parent (parent-children relationship update)
129
+
130
+ This narrow scope avoids "scope creep" — dirty changes in OTHER
131
+ active task dirs (parallel-window edits) are NOT bundled into the
132
+ archive commit. Callers handle each kind of change in its own
133
+ commit boundary.
134
+
135
+ Backwards-compat: with no arguments, the function walks the whole
136
+ `.trellis/tasks/` subtree the old way (active tasks + archive). New
137
+ callers should always pass `task_name`.
122
138
  """
123
139
  paths: list[str] = []
124
140
  tasks_dir = repo_root / DIR_WORKFLOW / DIR_TASKS
125
- if tasks_dir.is_dir():
126
- # The archive copy.
127
- archive_dir = tasks_dir / DIR_ARCHIVE
141
+ if not tasks_dir.is_dir():
142
+ return paths
143
+
144
+ archive_dir = tasks_dir / DIR_ARCHIVE
145
+
146
+ if task_name is not None:
147
+ # Narrow scope — only paths that still exist on disk (so
148
+ # `git add` doesn't choke on the moved-away source). The caller
149
+ # handles the source-side deletes via `git rm --cached`
150
+ # explicitly.
128
151
  if archive_dir.is_dir():
129
- paths.append(f"{DIR_WORKFLOW}/{DIR_TASKS}/{DIR_ARCHIVE}")
130
- # Active tasks (some may have been re-touched, e.g. parent's
131
- # children list). This captures the source-path deletion too because
132
- # `git add` on a directory records removals.
133
- for child in sorted(tasks_dir.iterdir()):
134
- if not child.is_dir():
135
- continue
136
- if child.name == DIR_ARCHIVE:
137
- continue
138
- paths.append(f"{DIR_WORKFLOW}/{DIR_TASKS}/{child.name}")
152
+ paths.append(
153
+ f"{DIR_WORKFLOW}/{DIR_TASKS}/{DIR_ARCHIVE}"
154
+ )
155
+ for child_name in modified_children or []:
156
+ paths.append(f"{DIR_WORKFLOW}/{DIR_TASKS}/{child_name}")
157
+ return paths
158
+
159
+ # Legacy wide scope (no task_name): preserve old behavior so callers
160
+ # that have not been updated keep working.
161
+ if archive_dir.is_dir():
162
+ paths.append(f"{DIR_WORKFLOW}/{DIR_TASKS}/{DIR_ARCHIVE}")
163
+ for child in sorted(tasks_dir.iterdir()):
164
+ if not child.is_dir():
165
+ continue
166
+ if child.name == DIR_ARCHIVE:
167
+ continue
168
+ paths.append(f"{DIR_WORKFLOW}/{DIR_TASKS}/{child.name}")
139
169
  return paths
140
170
 
141
171
 
@@ -369,6 +369,9 @@ def cmd_archive(args: argparse.Namespace) -> int:
369
369
 
370
370
  # Update status before archiving
371
371
  today = datetime.now().strftime("%Y-%m-%d")
372
+ # Names of child task dirs whose task.json gets modified below; passed
373
+ # into safe_archive_paths_to_add so they're staged in this commit.
374
+ modified_children: list[str] = []
372
375
  if task_json_path.is_file():
373
376
  data = read_json(task_json_path)
374
377
  if data:
@@ -393,6 +396,7 @@ def cmd_archive(args: argparse.Namespace) -> int:
393
396
  if child_data:
394
397
  child_data["parent"] = None
395
398
  write_json(child_json, child_data)
399
+ modified_children.append(child_dir_path.name)
396
400
 
397
401
  # Clear any session that still points at this task before the path moves.
398
402
  from .active_task import clear_task_from_sessions
@@ -407,7 +411,7 @@ def cmd_archive(args: argparse.Namespace) -> int:
407
411
 
408
412
  # Auto-commit unless --no-commit
409
413
  if not getattr(args, "no_commit", False):
410
- _auto_commit_archive(dir_name, repo_root)
414
+ _auto_commit_archive(dir_name, repo_root, modified_children)
411
415
 
412
416
  # Return the archive path
413
417
  print(f"{DIR_WORKFLOW}/{DIR_TASKS}/{DIR_ARCHIVE}/{year_month}/{dir_name}")
@@ -420,18 +424,26 @@ def cmd_archive(args: argparse.Namespace) -> int:
420
424
  return 1
421
425
 
422
426
 
423
- def _auto_commit_archive(task_name: str, repo_root: Path) -> None:
427
+ def _auto_commit_archive(
428
+ task_name: str,
429
+ repo_root: Path,
430
+ modified_children: list[str] | None = None,
431
+ ) -> None:
424
432
  """Stage Trellis-owned task paths and commit after archive.
425
433
 
426
- Only stages specific subpaths (the archive subtree and active task dirs),
427
- never the whole ``.trellis/`` tree. If ``.gitignore`` blocks the paths,
428
- we warn + skip we do NOT retry with ``git add -f``. The warning
429
- explicitly forbids ``git add -f .trellis/`` (which would fan out to
430
- caches/backups) and points users at ``session_auto_commit: false``.
434
+ Scoped narrowly to the archived task's source + destination paths
435
+ plus any child task dirs whose ``task.json`` was edited (parent →
436
+ children relationship update). Dirty changes in OTHER active task
437
+ dirs are NOT bundled into the archive commit.
431
438
 
432
- Honors ``session_auto_commit`` in ``.trellis/config.yaml``: when set to
433
- ``false``, this function returns immediately without touching git
434
- (the archive directory move on disk is unaffected).
439
+ If ``.gitignore`` blocks the paths, we warn + skip — we do NOT
440
+ retry with ``git add -f``. The warning explicitly forbids
441
+ ``git add -f .trellis/`` (which would fan out to caches/backups)
442
+ and points users at ``session_auto_commit: false``.
443
+
444
+ Honors ``session_auto_commit`` in ``.trellis/config.yaml``: when
445
+ set to ``false``, this function returns immediately without
446
+ touching git (the archive directory move on disk is unaffected).
435
447
  """
436
448
  if not get_session_auto_commit(repo_root):
437
449
  print(
@@ -440,7 +452,9 @@ def _auto_commit_archive(task_name: str, repo_root: Path) -> None:
440
452
  )
441
453
  return
442
454
 
443
- paths = safe_archive_paths_to_add(repo_root)
455
+ paths = safe_archive_paths_to_add(
456
+ repo_root, task_name=task_name, modified_children=modified_children
457
+ )
444
458
  if not paths:
445
459
  print("[OK] No task changes to commit.", file=sys.stderr)
446
460
  return
@@ -456,8 +470,24 @@ def _auto_commit_archive(task_name: str, repo_root: Path) -> None:
456
470
  )
457
471
  return
458
472
 
473
+ # Belt-and-suspenders for the phantom-delete bug: `safe_git_add` uses
474
+ # `git add` (no -A) which only stages additions/modifications. The
475
+ # source task directory was moved away by `shutil.move`, so its files
476
+ # need an explicit `git rm --cached` to stage the deletions in this
477
+ # same commit — otherwise they sit as uncommitted "phantom deletes"
478
+ # against HEAD until something later picks them up.
479
+ #
480
+ # `--ignore-unmatch` makes this a no-op when the task was never tracked
481
+ # (e.g. archiving a task that lived only in working tree).
482
+ source_rel = f"{DIR_WORKFLOW}/{DIR_TASKS}/{task_name}"
483
+ run_git(
484
+ ["rm", "-r", "--cached", "--ignore-unmatch", "--", source_rel],
485
+ cwd=repo_root,
486
+ )
487
+
459
488
  rc, _, _ = run_git(
460
- ["diff", "--cached", "--quiet", "--", *paths], cwd=repo_root
489
+ ["diff", "--cached", "--quiet", "--", *paths, source_rel],
490
+ cwd=repo_root,
461
491
  )
462
492
  if rc == 0:
463
493
  print("[OK] No task changes to commit.", file=sys.stderr)
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Homedir guard for destructive commands (init, uninstall).
3
+ *
4
+ * Running `trellis init` / `trellis uninstall` in `$HOME` is catastrophic:
5
+ * platforms like Claude Code, Codex, OpenCode all store global runtime data
6
+ * (`.claude/projects/<sanitized-cwd>/*.jsonl` chat history, `.codex/sessions/`,
7
+ * `.opencode/` caches, etc.) directly in the user's home directory. If
8
+ * trellis manages the same `.{platform}/` config dirs and the hash manifest
9
+ * picks up runtime data, uninstall would later unlink it.
10
+ *
11
+ * Subdirectories of home (`~/Documents/projects/foo/`) are NOT blocked — only
12
+ * exact-home match.
13
+ *
14
+ * Bypass: `TRELLIS_ALLOW_HOMEDIR=1`.
15
+ */
16
+ /**
17
+ * Returns true if `process.cwd()` is exactly the user's home directory.
18
+ *
19
+ * Uses `realpathSync.native()` on both sides so symlinks, `..` segments, and
20
+ * case differences (Windows) don't confuse the comparison. On Windows the
21
+ * comparison is also case-insensitive — `C:\Users\Alice` matches
22
+ * `c:\users\alice`.
23
+ *
24
+ * Permissive on lookup failure: if realpath fails for any reason (broken
25
+ * symlink, EACCES, etc.) we return false so a safety check doesn't crash
26
+ * the command.
27
+ */
28
+ export declare function isCwdHomedir(): boolean;
29
+ /**
30
+ * Error message printed by both `trellis init` and `trellis uninstall` when
31
+ * the homedir guard trips.
32
+ */
33
+ export declare function homedirGuardMessage(commandName: "init" | "uninstall"): string;
34
+ /**
35
+ * Returns true when the bypass env var is set.
36
+ */
37
+ export declare function homedirBypassEnabled(): boolean;
38
+ //# sourceMappingURL=cwd-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cwd-guard.d.ts","sourceRoot":"","sources":["../../src/utils/cwd-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAYtC;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,CAS7E;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Homedir guard for destructive commands (init, uninstall).
3
+ *
4
+ * Running `trellis init` / `trellis uninstall` in `$HOME` is catastrophic:
5
+ * platforms like Claude Code, Codex, OpenCode all store global runtime data
6
+ * (`.claude/projects/<sanitized-cwd>/*.jsonl` chat history, `.codex/sessions/`,
7
+ * `.opencode/` caches, etc.) directly in the user's home directory. If
8
+ * trellis manages the same `.{platform}/` config dirs and the hash manifest
9
+ * picks up runtime data, uninstall would later unlink it.
10
+ *
11
+ * Subdirectories of home (`~/Documents/projects/foo/`) are NOT blocked — only
12
+ * exact-home match.
13
+ *
14
+ * Bypass: `TRELLIS_ALLOW_HOMEDIR=1`.
15
+ */
16
+ import { realpathSync } from "node:fs";
17
+ import * as os from "node:os";
18
+ /**
19
+ * Returns true if `process.cwd()` is exactly the user's home directory.
20
+ *
21
+ * Uses `realpathSync.native()` on both sides so symlinks, `..` segments, and
22
+ * case differences (Windows) don't confuse the comparison. On Windows the
23
+ * comparison is also case-insensitive — `C:\Users\Alice` matches
24
+ * `c:\users\alice`.
25
+ *
26
+ * Permissive on lookup failure: if realpath fails for any reason (broken
27
+ * symlink, EACCES, etc.) we return false so a safety check doesn't crash
28
+ * the command.
29
+ */
30
+ export function isCwdHomedir() {
31
+ try {
32
+ let cwd = realpathSync.native(process.cwd());
33
+ let home = realpathSync.native(os.homedir());
34
+ if (process.platform === "win32") {
35
+ cwd = cwd.toLowerCase();
36
+ home = home.toLowerCase();
37
+ }
38
+ return cwd === home;
39
+ }
40
+ catch {
41
+ return false;
42
+ }
43
+ }
44
+ /**
45
+ * Error message printed by both `trellis init` and `trellis uninstall` when
46
+ * the homedir guard trips.
47
+ */
48
+ export function homedirGuardMessage(commandName) {
49
+ return (`✗ Refusing to run \`trellis ${commandName}\` in your home directory.\n\n` +
50
+ `Trellis manages platform config dirs like .claude/, .codex/, .opencode/, which\n` +
51
+ `in your home directory also contain runtime data from those CLIs (chat history,\n` +
52
+ `session JSONLs, caches). Running here can wipe that data.\n\n` +
53
+ `Run trellis from your project directory instead. If you really want to run in\n` +
54
+ `$HOME, set TRELLIS_ALLOW_HOMEDIR=1.`);
55
+ }
56
+ /**
57
+ * Returns true when the bypass env var is set.
58
+ */
59
+ export function homedirBypassEnabled() {
60
+ return process.env.TRELLIS_ALLOW_HOMEDIR === "1";
61
+ }
62
+ //# sourceMappingURL=cwd-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cwd-guard.js","sourceRoot":"","sources":["../../src/utils/cwd-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9B;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,YAAY;IAC1B,IAAI,CAAC;QACH,IAAI,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QAC7C,IAAI,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7C,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YACjC,GAAG,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;YACxB,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAC5B,CAAC;QACD,OAAO,GAAG,KAAK,IAAI,CAAC;IACtB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,WAAiC;IACnE,OAAO,CACL,+BAA+B,WAAW,gCAAgC;QAC1E,kFAAkF;QAClF,mFAAmF;QACnF,+DAA+D;QAC/D,iFAAiF;QACjF,qCAAqC,CACtC,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,OAAO,CAAC,GAAG,CAAC,qBAAqB,KAAK,GAAG,CAAC;AACnD,CAAC"}
@@ -4,6 +4,19 @@ export interface WriteOptions {
4
4
  }
5
5
  export declare function setWriteMode(mode: WriteMode): void;
6
6
  export declare function getWriteMode(): WriteMode;
7
+ /**
8
+ * Begin recording every write into the returned Set. Calls accumulate into the
9
+ * same set until `stopRecordingWrites` runs. POSIX relative paths (relative to
10
+ * `cwd`) are stored, matching `.template-hashes.json` keys.
11
+ *
12
+ * Nested recording sessions are NOT supported — the caller must ensure
13
+ * `stopRecordingWrites` runs before the next `startRecordingWrites`. Failure
14
+ * is silent (the second `start` replaces the first set), so callers should
15
+ * always pair start/stop in try/finally.
16
+ */
17
+ export declare function startRecordingWrites(cwd: string): Set<string>;
18
+ /** End recording. Subsequent writes are not captured until `start` is called again. */
19
+ export declare function stopRecordingWrites(): void;
7
20
  /**
8
21
  * Write file with conflict handling
9
22
  * - If file doesn't exist: write directly
@@ -1 +1 @@
1
- {"version":3,"file":"file-writer.d.ts","sourceRoot":"","sources":["../../src/utils/file-writer.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE5D,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,SAAS,CAAC;CACjB;AASD,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAElD;AAED,wBAAgB,YAAY,IAAI,SAAS,CAExC;AA6BD;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GACjC,OAAO,CAAC,OAAO,CAAC,CA8GlB;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE/C"}
1
+ {"version":3,"file":"file-writer.d.ts","sourceRoot":"","sources":["../../src/utils/file-writer.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE5D,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,SAAS,CAAC;CACjB;AASD,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAElD;AAED,wBAAgB,YAAY,IAAI,SAAS,CAExC;AAkBD;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAK7D;AAED,uFAAuF;AACvF,wBAAgB,mBAAmB,IAAI,IAAI,CAG1C;AAsCD;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GACjC,OAAO,CAAC,OAAO,CAAC,CA0HlB;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE/C"}
@@ -2,6 +2,7 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import chalk from "chalk";
4
4
  import inquirer from "inquirer";
5
+ import { toPosix } from "./posix.js";
5
6
  // Global write mode (set from CLI options)
6
7
  let globalWriteMode = "ask";
7
8
  export function setWriteMode(mode) {
@@ -10,6 +11,51 @@ export function setWriteMode(mode) {
10
11
  export function getWriteMode() {
11
12
  return globalWriteMode;
12
13
  }
14
+ // ---------------------------------------------------------------------------
15
+ // Write recording
16
+ //
17
+ // `trellis init` uses recording to capture exactly which files were actually
18
+ // written this run (vs skipped because they already existed). The captured
19
+ // set is what `.template-hashes.json` should contain — NOT a blind directory
20
+ // walk of `.codex/` / `.claude/` / etc, which would include user-owned files
21
+ // that pre-dated init. See `pruneOrphanManifestKeys` for the self-heal side
22
+ // of the same contract.
23
+ // ---------------------------------------------------------------------------
24
+ /** When recording is active, every actual `writeFile` disk write appends here. */
25
+ let writeRecorder = null;
26
+ /** Project root used to convert absolute write paths to POSIX-relative keys. */
27
+ let writeRecorderRoot = null;
28
+ /**
29
+ * Begin recording every write into the returned Set. Calls accumulate into the
30
+ * same set until `stopRecordingWrites` runs. POSIX relative paths (relative to
31
+ * `cwd`) are stored, matching `.template-hashes.json` keys.
32
+ *
33
+ * Nested recording sessions are NOT supported — the caller must ensure
34
+ * `stopRecordingWrites` runs before the next `startRecordingWrites`. Failure
35
+ * is silent (the second `start` replaces the first set), so callers should
36
+ * always pair start/stop in try/finally.
37
+ */
38
+ export function startRecordingWrites(cwd) {
39
+ const sink = new Set();
40
+ writeRecorder = sink;
41
+ writeRecorderRoot = cwd;
42
+ return sink;
43
+ }
44
+ /** End recording. Subsequent writes are not captured until `start` is called again. */
45
+ export function stopRecordingWrites() {
46
+ writeRecorder = null;
47
+ writeRecorderRoot = null;
48
+ }
49
+ /** Record a successful write. Called internally by `writeFile`. */
50
+ function recordWrite(absPath) {
51
+ if (!writeRecorder || !writeRecorderRoot)
52
+ return;
53
+ const rel = path.relative(writeRecorderRoot, absPath);
54
+ // Defensive: skip writes outside cwd (no meaningful manifest key).
55
+ if (rel.startsWith("..") || path.isAbsolute(rel))
56
+ return;
57
+ writeRecorder.add(toPosix(rel));
58
+ }
13
59
  /**
14
60
  * Get relative path from cwd for display
15
61
  */
@@ -49,12 +95,15 @@ export async function writeFile(filePath, content, options) {
49
95
  if (options?.executable) {
50
96
  fs.chmodSync(filePath, "755");
51
97
  }
98
+ recordWrite(filePath);
52
99
  return true;
53
100
  }
54
101
  // File exists, check if content is identical
55
102
  const existingContent = fs.readFileSync(filePath, "utf-8");
56
103
  if (existingContent === content) {
57
- // Content identical, skip silently (no output)
104
+ // Content identical, but no disk write happened. Do not record it for
105
+ // init-time manifests: pre-existing user files can legitimately be
106
+ // byte-identical to a Trellis template and still not be Trellis-owned.
58
107
  return false;
59
108
  }
60
109
  // File exists with different content, handle based on mode.
@@ -70,15 +119,22 @@ export async function writeFile(filePath, content, options) {
70
119
  fs.chmodSync(filePath, "755");
71
120
  }
72
121
  console.log(chalk.yellow(` ↻ Overwritten: ${displayPath}`));
122
+ recordWrite(filePath);
73
123
  return true;
74
124
  }
75
125
  if (mode === "skip") {
76
126
  console.log(chalk.gray(` ○ Skipped: ${displayPath} (already exists)`));
127
+ // Skipped: trellis did NOT write this file — caller should not track it
128
+ // in the manifest. This is the AGENTS.md skip-existing case.
77
129
  return false;
78
130
  }
79
131
  if (mode === "append") {
80
132
  appendToFile(filePath, content, options);
81
133
  console.log(chalk.blue(` + Appended: ${displayPath}`));
134
+ // Append: trellis added trellis content to a user-owned file. Tracking
135
+ // is risky here (uninstall would unlink the whole file), so we do NOT
136
+ // record appended files. Users on `--append` get a fresh manifest miss
137
+ // on next update; that's the safer default.
82
138
  return true;
83
139
  }
84
140
  // mode === 'ask': Interactive prompt
@@ -107,6 +163,7 @@ export async function writeFile(filePath, content, options) {
107
163
  fs.chmodSync(filePath, "755");
108
164
  }
109
165
  console.log(chalk.yellow(` ↻ Overwritten: ${displayPath}`));
166
+ recordWrite(filePath);
110
167
  return true;
111
168
  }
112
169
  if (action === "append") {
@@ -126,6 +183,7 @@ export async function writeFile(filePath, content, options) {
126
183
  fs.chmodSync(filePath, "755");
127
184
  }
128
185
  console.log(chalk.yellow(` ↻ Overwritten: ${displayPath}`));
186
+ recordWrite(filePath);
129
187
  return true;
130
188
  }
131
189
  if (action === "append-all") {
@@ -1 +1 @@
1
- {"version":3,"file":"file-writer.js","sourceRoot":"","sources":["../../src/utils/file-writer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"file-writer.js","sourceRoot":"","sources":["../../src/utils/file-writer.ts"],"names":[],"mappings":"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"}