@mindfoldhq/trellis 0.3.10-beta.0 → 0.3.10

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 (173) hide show
  1. package/dist/cli/index.js +0 -2
  2. package/dist/cli/index.js.map +1 -1
  3. package/dist/commands/init.d.ts +0 -1
  4. package/dist/commands/init.d.ts.map +1 -1
  5. package/dist/commands/init.js +31 -203
  6. package/dist/commands/init.js.map +1 -1
  7. package/dist/commands/update.d.ts.map +1 -1
  8. package/dist/commands/update.js +6 -154
  9. package/dist/commands/update.js.map +1 -1
  10. package/dist/configurators/workflow.d.ts +2 -6
  11. package/dist/configurators/workflow.d.ts.map +1 -1
  12. package/dist/configurators/workflow.js +58 -88
  13. package/dist/configurators/workflow.js.map +1 -1
  14. package/dist/migrations/index.d.ts +0 -1
  15. package/dist/migrations/index.d.ts.map +1 -1
  16. package/dist/migrations/index.js +0 -2
  17. package/dist/migrations/index.js.map +1 -1
  18. package/dist/migrations/manifests/0.3.10.json +9 -0
  19. package/dist/templates/claude/agents/dispatch.md +2 -1
  20. package/dist/templates/claude/agents/implement.md +3 -2
  21. package/dist/templates/claude/commands/trellis/before-backend-dev.md +13 -0
  22. package/dist/templates/claude/commands/trellis/before-frontend-dev.md +13 -0
  23. package/dist/templates/claude/commands/trellis/check-backend.md +13 -0
  24. package/dist/templates/claude/commands/trellis/check-frontend.md +13 -0
  25. package/dist/templates/claude/commands/trellis/create-command.md +2 -2
  26. package/dist/templates/claude/commands/trellis/onboard.md +13 -13
  27. package/dist/templates/claude/commands/trellis/parallel.md +2 -1
  28. package/dist/templates/claude/commands/trellis/record-session.md +2 -2
  29. package/dist/templates/claude/commands/trellis/start.md +4 -8
  30. package/dist/templates/claude/hooks/inject-subagent-context.py +13 -21
  31. package/dist/templates/claude/hooks/session-start.py +2 -170
  32. package/dist/templates/codex/skills/before-backend-dev/SKILL.md +18 -0
  33. package/dist/templates/codex/skills/before-frontend-dev/SKILL.md +18 -0
  34. package/dist/templates/codex/skills/check-backend/SKILL.md +18 -0
  35. package/dist/templates/codex/skills/check-frontend/SKILL.md +18 -0
  36. package/dist/templates/codex/skills/create-command/SKILL.md +2 -2
  37. package/dist/templates/codex/skills/onboard/SKILL.md +11 -11
  38. package/dist/templates/codex/skills/record-session/SKILL.md +2 -2
  39. package/dist/templates/codex/skills/start/SKILL.md +3 -8
  40. package/dist/templates/cursor/commands/trellis-before-backend-dev.md +13 -0
  41. package/dist/templates/cursor/commands/trellis-before-frontend-dev.md +13 -0
  42. package/dist/templates/cursor/commands/trellis-check-backend.md +13 -0
  43. package/dist/templates/cursor/commands/trellis-check-frontend.md +13 -0
  44. package/dist/templates/cursor/commands/trellis-create-command.md +2 -2
  45. package/dist/templates/cursor/commands/trellis-onboard.md +13 -13
  46. package/dist/templates/cursor/commands/trellis-record-session.md +2 -2
  47. package/dist/templates/cursor/commands/trellis-start.md +16 -7
  48. package/dist/templates/gemini/commands/trellis/before-backend-dev.toml +17 -0
  49. package/dist/templates/gemini/commands/trellis/before-frontend-dev.toml +17 -0
  50. package/dist/templates/gemini/commands/trellis/check-backend.toml +17 -0
  51. package/dist/templates/gemini/commands/trellis/check-frontend.toml +17 -0
  52. package/dist/templates/gemini/commands/trellis/create-command.toml +2 -2
  53. package/dist/templates/gemini/commands/trellis/onboard.toml +2 -2
  54. package/dist/templates/gemini/commands/trellis/record-session.toml +2 -2
  55. package/dist/templates/gemini/commands/trellis/start.toml +4 -9
  56. package/dist/templates/iflow/agents/dispatch.md +2 -1
  57. package/dist/templates/iflow/agents/implement.md +3 -2
  58. package/dist/templates/iflow/commands/trellis/before-backend-dev.md +13 -0
  59. package/dist/templates/iflow/commands/trellis/before-frontend-dev.md +13 -0
  60. package/dist/templates/iflow/commands/trellis/check-backend.md +13 -0
  61. package/dist/templates/iflow/commands/trellis/check-frontend.md +13 -0
  62. package/dist/templates/iflow/commands/trellis/create-command.md +2 -2
  63. package/dist/templates/iflow/commands/trellis/onboard.md +13 -13
  64. package/dist/templates/iflow/commands/trellis/parallel.md +2 -1
  65. package/dist/templates/iflow/commands/trellis/record-session.md +2 -2
  66. package/dist/templates/iflow/commands/trellis/start.md +4 -8
  67. package/dist/templates/iflow/hooks/inject-subagent-context.py +13 -21
  68. package/dist/templates/iflow/hooks/session-start.py +1 -156
  69. package/dist/templates/kilo/workflows/before-backend-dev.md +13 -0
  70. package/dist/templates/kilo/workflows/before-frontend-dev.md +13 -0
  71. package/dist/templates/kilo/workflows/check-backend.md +13 -0
  72. package/dist/templates/kilo/workflows/check-frontend.md +13 -0
  73. package/dist/templates/kilo/workflows/create-command.md +2 -2
  74. package/dist/templates/kilo/workflows/onboard.md +13 -13
  75. package/dist/templates/kilo/workflows/parallel.md +2 -1
  76. package/dist/templates/kilo/workflows/record-session.md +2 -2
  77. package/dist/templates/kilo/workflows/start.md +3 -8
  78. package/dist/templates/kiro/skills/before-backend-dev/SKILL.md +18 -0
  79. package/dist/templates/kiro/skills/before-frontend-dev/SKILL.md +18 -0
  80. package/dist/templates/kiro/skills/check-backend/SKILL.md +18 -0
  81. package/dist/templates/kiro/skills/check-frontend/SKILL.md +18 -0
  82. package/dist/templates/kiro/skills/create-command/SKILL.md +2 -2
  83. package/dist/templates/kiro/skills/onboard/SKILL.md +11 -11
  84. package/dist/templates/kiro/skills/record-session/SKILL.md +2 -2
  85. package/dist/templates/kiro/skills/start/SKILL.md +3 -8
  86. package/dist/templates/markdown/spec/backend/script-conventions.md +0 -93
  87. package/dist/templates/opencode/agents/dispatch.md +2 -1
  88. package/dist/templates/opencode/agents/implement.md +2 -2
  89. package/dist/templates/opencode/agents/research.md +2 -1
  90. package/dist/templates/opencode/commands/trellis/before-backend-dev.md +13 -0
  91. package/dist/templates/opencode/commands/trellis/before-frontend-dev.md +13 -0
  92. package/dist/templates/opencode/commands/trellis/check-backend.md +13 -0
  93. package/dist/templates/opencode/commands/trellis/check-frontend.md +13 -0
  94. package/dist/templates/opencode/commands/trellis/create-command.md +2 -2
  95. package/dist/templates/opencode/commands/trellis/onboard.md +13 -13
  96. package/dist/templates/opencode/commands/trellis/parallel.md +2 -1
  97. package/dist/templates/opencode/commands/trellis/record-session.md +2 -2
  98. package/dist/templates/opencode/commands/trellis/start.md +3 -8
  99. package/dist/templates/opencode/plugin/inject-subagent-context.js +18 -45
  100. package/dist/templates/opencode/plugin/session-start.js +1 -149
  101. package/dist/templates/qoder/skills/before-backend-dev/SKILL.md +18 -0
  102. package/dist/templates/qoder/skills/before-frontend-dev/SKILL.md +18 -0
  103. package/dist/templates/qoder/skills/check-backend/SKILL.md +18 -0
  104. package/dist/templates/qoder/skills/check-frontend/SKILL.md +18 -0
  105. package/dist/templates/qoder/skills/create-command/SKILL.md +2 -2
  106. package/dist/templates/qoder/skills/onboard/SKILL.md +13 -13
  107. package/dist/templates/qoder/skills/record-session/SKILL.md +2 -2
  108. package/dist/templates/qoder/skills/start/SKILL.md +3 -8
  109. package/dist/templates/trellis/config.yaml +0 -20
  110. package/dist/templates/trellis/index.d.ts +0 -11
  111. package/dist/templates/trellis/index.d.ts.map +1 -1
  112. package/dist/templates/trellis/index.js +0 -22
  113. package/dist/templates/trellis/index.js.map +1 -1
  114. package/dist/templates/trellis/scripts/add_session.py +7 -52
  115. package/dist/templates/trellis/scripts/common/cli_adapter.py +45 -33
  116. package/dist/templates/trellis/scripts/common/config.py +0 -152
  117. package/dist/templates/trellis/scripts/common/git_context.py +586 -23
  118. package/dist/templates/trellis/scripts/common/paths.py +0 -46
  119. package/dist/templates/trellis/scripts/common/phase.py +49 -50
  120. package/dist/templates/trellis/scripts/common/registry.py +72 -41
  121. package/dist/templates/trellis/scripts/common/task_queue.py +98 -27
  122. package/dist/templates/trellis/scripts/common/task_utils.py +6 -96
  123. package/dist/templates/trellis/scripts/create_bootstrap.py +26 -31
  124. package/dist/templates/trellis/scripts/multi_agent/cleanup.py +48 -43
  125. package/dist/templates/trellis/scripts/multi_agent/create_pr.py +45 -336
  126. package/dist/templates/trellis/scripts/multi_agent/plan.py +26 -2
  127. package/dist/templates/trellis/scripts/multi_agent/start.py +57 -126
  128. package/dist/templates/trellis/scripts/multi_agent/status.py +753 -12
  129. package/dist/templates/trellis/scripts/task.py +975 -50
  130. package/dist/templates/trellis/workflow.md +34 -21
  131. package/dist/types/migration.d.ts +1 -3
  132. package/dist/types/migration.d.ts.map +1 -1
  133. package/dist/utils/project-detector.d.ts +0 -23
  134. package/dist/utils/project-detector.d.ts.map +1 -1
  135. package/dist/utils/project-detector.js +0 -364
  136. package/dist/utils/project-detector.js.map +1 -1
  137. package/dist/utils/template-fetcher.d.ts +10 -2
  138. package/dist/utils/template-fetcher.d.ts.map +1 -1
  139. package/dist/utils/template-fetcher.js +43 -12
  140. package/dist/utils/template-fetcher.js.map +1 -1
  141. package/package.json +1 -1
  142. package/dist/migrations/manifests/0.4.0-beta.1.json +0 -228
  143. package/dist/templates/claude/commands/trellis/before-dev.md +0 -29
  144. package/dist/templates/claude/commands/trellis/check.md +0 -25
  145. package/dist/templates/codex/skills/before-dev/SKILL.md +0 -34
  146. package/dist/templates/codex/skills/check/SKILL.md +0 -30
  147. package/dist/templates/cursor/commands/trellis-before-dev.md +0 -29
  148. package/dist/templates/cursor/commands/trellis-check.md +0 -25
  149. package/dist/templates/gemini/commands/trellis/before-dev.toml +0 -33
  150. package/dist/templates/gemini/commands/trellis/check.toml +0 -29
  151. package/dist/templates/iflow/commands/trellis/before-dev.md +0 -29
  152. package/dist/templates/iflow/commands/trellis/check.md +0 -25
  153. package/dist/templates/kilo/workflows/before-dev.md +0 -29
  154. package/dist/templates/kilo/workflows/check.md +0 -25
  155. package/dist/templates/kiro/skills/before-dev/SKILL.md +0 -34
  156. package/dist/templates/kiro/skills/check/SKILL.md +0 -30
  157. package/dist/templates/opencode/commands/trellis/before-dev.md +0 -29
  158. package/dist/templates/opencode/commands/trellis/check.md +0 -25
  159. package/dist/templates/qoder/skills/before-dev/SKILL.md +0 -34
  160. package/dist/templates/qoder/skills/check/SKILL.md +0 -30
  161. package/dist/templates/trellis/scripts/common/git.py +0 -31
  162. package/dist/templates/trellis/scripts/common/io.py +0 -37
  163. package/dist/templates/trellis/scripts/common/log.py +0 -45
  164. package/dist/templates/trellis/scripts/common/packages_context.py +0 -233
  165. package/dist/templates/trellis/scripts/common/session_context.py +0 -466
  166. package/dist/templates/trellis/scripts/common/task_context.py +0 -384
  167. package/dist/templates/trellis/scripts/common/task_store.py +0 -534
  168. package/dist/templates/trellis/scripts/common/tasks.py +0 -109
  169. package/dist/templates/trellis/scripts/common/types.py +0 -112
  170. package/dist/templates/trellis/scripts/hooks/linear_sync.py +0 -243
  171. package/dist/templates/trellis/scripts/multi_agent/_bootstrap.py +0 -17
  172. package/dist/templates/trellis/scripts/multi_agent/status_display.py +0 -542
  173. package/dist/templates/trellis/scripts/multi_agent/status_monitor.py +0 -225
@@ -1,466 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Session context generation (default + record modes).
4
-
5
- Provides:
6
- get_context_json - JSON output for default mode
7
- get_context_text - Text output for default mode
8
- get_context_record_json - JSON for record mode
9
- get_context_text_record - Text for record mode
10
- output_json - Print JSON
11
- output_text - Print text
12
- """
13
-
14
- from __future__ import annotations
15
-
16
- import json
17
- from pathlib import Path
18
-
19
- from .git import run_git
20
- from .packages_context import get_packages_section
21
- from .tasks import iter_active_tasks, load_task, get_all_statuses, children_progress
22
- from .paths import (
23
- DIR_SCRIPTS,
24
- DIR_SPEC,
25
- DIR_TASKS,
26
- DIR_WORKFLOW,
27
- DIR_WORKSPACE,
28
- count_lines,
29
- get_active_journal_file,
30
- get_current_task,
31
- get_developer,
32
- get_repo_root,
33
- get_tasks_dir,
34
- )
35
-
36
-
37
- # =============================================================================
38
- # JSON Output
39
- # =============================================================================
40
-
41
- def get_context_json(repo_root: Path | None = None) -> dict:
42
- """Get context as a dictionary.
43
-
44
- Args:
45
- repo_root: Repository root path. Defaults to auto-detected.
46
-
47
- Returns:
48
- Context dictionary.
49
- """
50
- if repo_root is None:
51
- repo_root = get_repo_root()
52
-
53
- developer = get_developer(repo_root)
54
- tasks_dir = get_tasks_dir(repo_root)
55
- journal_file = get_active_journal_file(repo_root)
56
-
57
- journal_lines = 0
58
- journal_relative = ""
59
- if journal_file and developer:
60
- journal_lines = count_lines(journal_file)
61
- journal_relative = (
62
- f"{DIR_WORKFLOW}/{DIR_WORKSPACE}/{developer}/{journal_file.name}"
63
- )
64
-
65
- # Git info
66
- _, branch_out, _ = run_git(["branch", "--show-current"], cwd=repo_root)
67
- branch = branch_out.strip() or "unknown"
68
-
69
- _, status_out, _ = run_git(["status", "--porcelain"], cwd=repo_root)
70
- git_status_count = len([line for line in status_out.splitlines() if line.strip()])
71
- is_clean = git_status_count == 0
72
-
73
- # Recent commits
74
- _, log_out, _ = run_git(["log", "--oneline", "-5"], cwd=repo_root)
75
- commits = []
76
- for line in log_out.splitlines():
77
- if line.strip():
78
- parts = line.split(" ", 1)
79
- if len(parts) >= 2:
80
- commits.append({"hash": parts[0], "message": parts[1]})
81
- elif len(parts) == 1:
82
- commits.append({"hash": parts[0], "message": ""})
83
-
84
- # Tasks
85
- tasks = [
86
- {
87
- "dir": t.dir_name,
88
- "name": t.name,
89
- "status": t.status,
90
- "children": list(t.children),
91
- "parent": t.parent,
92
- }
93
- for t in iter_active_tasks(tasks_dir)
94
- ]
95
-
96
- return {
97
- "developer": developer or "",
98
- "git": {
99
- "branch": branch,
100
- "isClean": is_clean,
101
- "uncommittedChanges": git_status_count,
102
- "recentCommits": commits,
103
- },
104
- "tasks": {
105
- "active": tasks,
106
- "directory": f"{DIR_WORKFLOW}/{DIR_TASKS}",
107
- },
108
- "journal": {
109
- "file": journal_relative,
110
- "lines": journal_lines,
111
- "nearLimit": journal_lines > 1800,
112
- },
113
- }
114
-
115
-
116
- def output_json(repo_root: Path | None = None) -> None:
117
- """Output context in JSON format.
118
-
119
- Args:
120
- repo_root: Repository root path. Defaults to auto-detected.
121
- """
122
- context = get_context_json(repo_root)
123
- print(json.dumps(context, indent=2, ensure_ascii=False))
124
-
125
-
126
- # =============================================================================
127
- # Text Output
128
- # =============================================================================
129
-
130
- def get_context_text(repo_root: Path | None = None) -> str:
131
- """Get context as formatted text.
132
-
133
- Args:
134
- repo_root: Repository root path. Defaults to auto-detected.
135
-
136
- Returns:
137
- Formatted text output.
138
- """
139
- if repo_root is None:
140
- repo_root = get_repo_root()
141
-
142
- lines = []
143
- lines.append("========================================")
144
- lines.append("SESSION CONTEXT")
145
- lines.append("========================================")
146
- lines.append("")
147
-
148
- developer = get_developer(repo_root)
149
-
150
- # Developer section
151
- lines.append("## DEVELOPER")
152
- if not developer:
153
- lines.append(
154
- f"ERROR: Not initialized. Run: python3 ./{DIR_WORKFLOW}/{DIR_SCRIPTS}/init_developer.py <name>"
155
- )
156
- return "\n".join(lines)
157
-
158
- lines.append(f"Name: {developer}")
159
- lines.append("")
160
-
161
- # Git status
162
- lines.append("## GIT STATUS")
163
- _, branch_out, _ = run_git(["branch", "--show-current"], cwd=repo_root)
164
- branch = branch_out.strip() or "unknown"
165
- lines.append(f"Branch: {branch}")
166
-
167
- _, status_out, _ = run_git(["status", "--porcelain"], cwd=repo_root)
168
- status_lines = [line for line in status_out.splitlines() if line.strip()]
169
- status_count = len(status_lines)
170
-
171
- if status_count == 0:
172
- lines.append("Working directory: Clean")
173
- else:
174
- lines.append(f"Working directory: {status_count} uncommitted change(s)")
175
- lines.append("")
176
- lines.append("Changes:")
177
- _, short_out, _ = run_git(["status", "--short"], cwd=repo_root)
178
- for line in short_out.splitlines()[:10]:
179
- lines.append(line)
180
- lines.append("")
181
-
182
- # Recent commits
183
- lines.append("## RECENT COMMITS")
184
- _, log_out, _ = run_git(["log", "--oneline", "-5"], cwd=repo_root)
185
- if log_out.strip():
186
- for line in log_out.splitlines():
187
- lines.append(line)
188
- else:
189
- lines.append("(no commits)")
190
- lines.append("")
191
-
192
- # Current task
193
- lines.append("## CURRENT TASK")
194
- current_task = get_current_task(repo_root)
195
- if current_task:
196
- current_task_dir = repo_root / current_task
197
- lines.append(f"Path: {current_task}")
198
-
199
- ct = load_task(current_task_dir)
200
- if ct:
201
- lines.append(f"Name: {ct.name}")
202
- lines.append(f"Status: {ct.status}")
203
- lines.append(f"Created: {ct.raw.get('createdAt', 'unknown')}")
204
- if ct.description:
205
- lines.append(f"Description: {ct.description}")
206
-
207
- # Check for prd.md
208
- prd_file = current_task_dir / "prd.md"
209
- if prd_file.is_file():
210
- lines.append("")
211
- lines.append("[!] This task has prd.md - read it for task details")
212
- else:
213
- lines.append("(none)")
214
- lines.append("")
215
-
216
- # Active tasks
217
- lines.append("## ACTIVE TASKS")
218
- tasks_dir = get_tasks_dir(repo_root)
219
- task_count = 0
220
-
221
- # Collect all task data for hierarchy display
222
- all_tasks = {t.dir_name: t for t in iter_active_tasks(tasks_dir)}
223
- all_statuses = {name: t.status for name, t in all_tasks.items()}
224
-
225
- def _print_task_tree(name: str, indent: int = 0) -> None:
226
- nonlocal task_count
227
- t = all_tasks[name]
228
- progress = children_progress(t.children, all_statuses)
229
- prefix = " " * indent
230
- lines.append(f"{prefix}- {name}/ ({t.status}){progress} @{t.assignee or '-'}")
231
- task_count += 1
232
- for child in t.children:
233
- if child in all_tasks:
234
- _print_task_tree(child, indent + 1)
235
-
236
- for dir_name in sorted(all_tasks.keys()):
237
- if not all_tasks[dir_name].parent:
238
- _print_task_tree(dir_name)
239
-
240
- if task_count == 0:
241
- lines.append("(no active tasks)")
242
- lines.append(f"Total: {task_count} active task(s)")
243
- lines.append("")
244
-
245
- # My tasks
246
- lines.append("## MY TASKS (Assigned to me)")
247
- my_task_count = 0
248
-
249
- for t in all_tasks.values():
250
- if t.assignee == developer and t.status != "done":
251
- progress = children_progress(t.children, all_statuses)
252
- lines.append(f"- [{t.priority}] {t.title} ({t.status}){progress}")
253
- my_task_count += 1
254
-
255
- if my_task_count == 0:
256
- lines.append("(no tasks assigned to you)")
257
- lines.append("")
258
-
259
- # Journal file
260
- lines.append("## JOURNAL FILE")
261
- journal_file = get_active_journal_file(repo_root)
262
- if journal_file:
263
- journal_lines = count_lines(journal_file)
264
- relative = f"{DIR_WORKFLOW}/{DIR_WORKSPACE}/{developer}/{journal_file.name}"
265
- lines.append(f"Active file: {relative}")
266
- lines.append(f"Line count: {journal_lines} / 2000")
267
- if journal_lines > 1800:
268
- lines.append("[!] WARNING: Approaching 2000 line limit!")
269
- else:
270
- lines.append("No journal file found")
271
- lines.append("")
272
-
273
- # Packages
274
- packages_text = get_packages_section(repo_root)
275
- if packages_text:
276
- lines.append(packages_text)
277
- lines.append("")
278
-
279
- # Paths
280
- lines.append("## PATHS")
281
- lines.append(f"Workspace: {DIR_WORKFLOW}/{DIR_WORKSPACE}/{developer}/")
282
- lines.append(f"Tasks: {DIR_WORKFLOW}/{DIR_TASKS}/")
283
- lines.append(f"Spec: {DIR_WORKFLOW}/{DIR_SPEC}/")
284
- lines.append("")
285
-
286
- lines.append("========================================")
287
-
288
- return "\n".join(lines)
289
-
290
-
291
- # =============================================================================
292
- # Record Mode
293
- # =============================================================================
294
-
295
- def get_context_record_json(repo_root: Path | None = None) -> dict:
296
- """Get record-mode context as a dictionary.
297
-
298
- Focused on: my active tasks, git status, current task.
299
- """
300
- if repo_root is None:
301
- repo_root = get_repo_root()
302
-
303
- developer = get_developer(repo_root)
304
- tasks_dir = get_tasks_dir(repo_root)
305
-
306
- # Git info
307
- _, branch_out, _ = run_git(["branch", "--show-current"], cwd=repo_root)
308
- branch = branch_out.strip() or "unknown"
309
-
310
- _, status_out, _ = run_git(["status", "--porcelain"], cwd=repo_root)
311
- git_status_count = len([line for line in status_out.splitlines() if line.strip()])
312
-
313
- _, log_out, _ = run_git(["log", "--oneline", "-5"], cwd=repo_root)
314
- commits = []
315
- for line in log_out.splitlines():
316
- if line.strip():
317
- parts = line.split(" ", 1)
318
- if len(parts) >= 2:
319
- commits.append({"hash": parts[0], "message": parts[1]})
320
-
321
- # My tasks (single pass — collect statuses and filter by assignee)
322
- all_tasks_list = list(iter_active_tasks(tasks_dir))
323
- all_statuses = {t.dir_name: t.status for t in all_tasks_list}
324
-
325
- my_tasks = []
326
- for t in all_tasks_list:
327
- if t.assignee == developer:
328
- done = sum(
329
- 1 for c in t.children
330
- if all_statuses.get(c) in ("completed", "done")
331
- )
332
- my_tasks.append({
333
- "dir": t.dir_name,
334
- "title": t.title,
335
- "status": t.status,
336
- "priority": t.priority,
337
- "children": list(t.children),
338
- "childrenDone": done,
339
- "parent": t.parent,
340
- "meta": t.meta,
341
- })
342
-
343
- # Current task
344
- current_task_info = None
345
- current_task = get_current_task(repo_root)
346
- if current_task:
347
- ct = load_task(repo_root / current_task)
348
- if ct:
349
- current_task_info = {
350
- "path": current_task,
351
- "name": ct.name,
352
- "status": ct.status,
353
- }
354
-
355
- return {
356
- "developer": developer or "",
357
- "git": {
358
- "branch": branch,
359
- "isClean": git_status_count == 0,
360
- "uncommittedChanges": git_status_count,
361
- "recentCommits": commits,
362
- },
363
- "myTasks": my_tasks,
364
- "currentTask": current_task_info,
365
- }
366
-
367
-
368
- def get_context_text_record(repo_root: Path | None = None) -> str:
369
- """Get context as formatted text for record-session mode.
370
-
371
- Focused output: MY ACTIVE TASKS first (with [!!!] emphasis),
372
- then GIT STATUS, RECENT COMMITS, CURRENT TASK.
373
- """
374
- if repo_root is None:
375
- repo_root = get_repo_root()
376
-
377
- lines: list[str] = []
378
- lines.append("========================================")
379
- lines.append("SESSION CONTEXT (RECORD MODE)")
380
- lines.append("========================================")
381
- lines.append("")
382
-
383
- developer = get_developer(repo_root)
384
- if not developer:
385
- lines.append(
386
- f"ERROR: Not initialized. Run: python3 ./{DIR_WORKFLOW}/{DIR_SCRIPTS}/init_developer.py <name>"
387
- )
388
- return "\n".join(lines)
389
-
390
- # MY ACTIVE TASKS — first and prominent
391
- lines.append(f"## [!!!] MY ACTIVE TASKS (Assigned to {developer})")
392
- lines.append("[!] Review whether any should be archived before recording this session.")
393
- lines.append("")
394
-
395
- tasks_dir = get_tasks_dir(repo_root)
396
- my_task_count = 0
397
-
398
- # Single pass — collect all tasks and filter by assignee
399
- all_statuses = get_all_statuses(tasks_dir)
400
-
401
- for t in iter_active_tasks(tasks_dir):
402
- if t.assignee == developer:
403
- progress = children_progress(t.children, all_statuses)
404
- lines.append(f"- [{t.priority}] {t.title} ({t.status}){progress} — {t.dir_name}")
405
- my_task_count += 1
406
-
407
- if my_task_count == 0:
408
- lines.append("(no active tasks assigned to you)")
409
- lines.append("")
410
-
411
- # GIT STATUS
412
- lines.append("## GIT STATUS")
413
- _, branch_out, _ = run_git(["branch", "--show-current"], cwd=repo_root)
414
- branch = branch_out.strip() or "unknown"
415
- lines.append(f"Branch: {branch}")
416
-
417
- _, status_out, _ = run_git(["status", "--porcelain"], cwd=repo_root)
418
- status_lines = [line for line in status_out.splitlines() if line.strip()]
419
- status_count = len(status_lines)
420
-
421
- if status_count == 0:
422
- lines.append("Working directory: Clean")
423
- else:
424
- lines.append(f"Working directory: {status_count} uncommitted change(s)")
425
- lines.append("")
426
- lines.append("Changes:")
427
- _, short_out, _ = run_git(["status", "--short"], cwd=repo_root)
428
- for line in short_out.splitlines()[:10]:
429
- lines.append(line)
430
- lines.append("")
431
-
432
- # RECENT COMMITS
433
- lines.append("## RECENT COMMITS")
434
- _, log_out, _ = run_git(["log", "--oneline", "-5"], cwd=repo_root)
435
- if log_out.strip():
436
- for line in log_out.splitlines():
437
- lines.append(line)
438
- else:
439
- lines.append("(no commits)")
440
- lines.append("")
441
-
442
- # CURRENT TASK
443
- lines.append("## CURRENT TASK")
444
- current_task = get_current_task(repo_root)
445
- if current_task:
446
- lines.append(f"Path: {current_task}")
447
- ct = load_task(repo_root / current_task)
448
- if ct:
449
- lines.append(f"Name: {ct.name}")
450
- lines.append(f"Status: {ct.status}")
451
- else:
452
- lines.append("(none)")
453
- lines.append("")
454
-
455
- lines.append("========================================")
456
-
457
- return "\n".join(lines)
458
-
459
-
460
- def output_text(repo_root: Path | None = None) -> None:
461
- """Output context in text format.
462
-
463
- Args:
464
- repo_root: Repository root path. Defaults to auto-detected.
465
- """
466
- print(get_context_text(repo_root))