@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.
- package/README.md +5 -5
- package/dist/cli/index.js +1 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +24 -20
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +15 -12
- package/dist/commands/update.js.map +1 -1
- package/dist/configurators/claude.js +1 -1
- package/dist/configurators/claude.js.map +1 -1
- package/dist/configurators/codebuddy.js +1 -1
- package/dist/configurators/codebuddy.js.map +1 -1
- package/dist/configurators/codex.d.ts.map +1 -1
- package/dist/configurators/codex.js +3 -6
- package/dist/configurators/codex.js.map +1 -1
- package/dist/configurators/copilot.d.ts.map +1 -1
- package/dist/configurators/copilot.js +4 -11
- package/dist/configurators/copilot.js.map +1 -1
- package/dist/configurators/cursor.js +1 -1
- package/dist/configurators/cursor.js.map +1 -1
- package/dist/configurators/droid.js +1 -1
- package/dist/configurators/droid.js.map +1 -1
- package/dist/configurators/gemini.d.ts.map +1 -1
- package/dist/configurators/gemini.js +1 -3
- package/dist/configurators/gemini.js.map +1 -1
- package/dist/configurators/index.d.ts.map +1 -1
- package/dist/configurators/index.js +24 -38
- package/dist/configurators/index.js.map +1 -1
- package/dist/configurators/kiro.js +1 -1
- package/dist/configurators/kiro.js.map +1 -1
- package/dist/configurators/pi.d.ts +3 -0
- package/dist/configurators/pi.d.ts.map +1 -0
- package/dist/configurators/pi.js +39 -0
- package/dist/configurators/pi.js.map +1 -0
- package/dist/configurators/qoder.d.ts.map +1 -1
- package/dist/configurators/qoder.js +1 -3
- package/dist/configurators/qoder.js.map +1 -1
- package/dist/configurators/shared.d.ts +2 -4
- package/dist/configurators/shared.d.ts.map +1 -1
- package/dist/configurators/shared.js +6 -9
- package/dist/configurators/shared.js.map +1 -1
- package/dist/migrations/manifests/0.5.0-beta.14.json +9 -0
- package/dist/migrations/manifests/0.5.0-beta.15.json +126 -0
- package/dist/templates/claude/agents/trellis-research.md +1 -1
- package/dist/templates/claude/settings.json +0 -4
- package/dist/templates/codebuddy/agents/trellis-research.md +1 -1
- package/dist/templates/codex/agents/trellis-check.toml +0 -16
- package/dist/templates/codex/agents/trellis-implement.toml +0 -16
- package/dist/templates/codex/agents/trellis-research.toml +3 -2
- package/dist/templates/codex/hooks/session-start.py +82 -22
- package/dist/templates/codex/skills/start/SKILL.md +1 -1
- package/dist/templates/copilot/hooks/session-start.py +84 -26
- package/dist/templates/copilot/prompts/start.prompt.md +1 -1
- package/dist/templates/cursor/agents/trellis-check.md +1 -1
- package/dist/templates/cursor/agents/trellis-implement.md +1 -1
- package/dist/templates/cursor/agents/trellis-research.md +2 -2
- package/dist/templates/cursor/hooks.json +7 -1
- package/dist/templates/droid/droids/trellis-research.md +1 -1
- package/dist/templates/extract.d.ts +6 -0
- package/dist/templates/extract.d.ts.map +1 -1
- package/dist/templates/extract.js +14 -0
- package/dist/templates/extract.js.map +1 -1
- package/dist/templates/gemini/agents/trellis-research.md +1 -1
- package/dist/templates/kiro/agents/trellis-research.json +1 -1
- package/dist/templates/markdown/agents.md +11 -12
- package/dist/templates/markdown/gitignore.txt +3 -0
- package/dist/templates/opencode/agents/trellis-check.md +1 -1
- package/dist/templates/opencode/agents/trellis-implement.md +1 -1
- package/dist/templates/opencode/agents/trellis-research.md +2 -2
- package/dist/templates/opencode/lib/trellis-context.js +100 -13
- package/dist/templates/opencode/plugins/inject-subagent-context.js +54 -4
- package/dist/templates/opencode/plugins/inject-workflow-state.js +50 -23
- package/dist/templates/opencode/plugins/session-start.js +46 -21
- package/dist/templates/pi/agents/trellis-check.md +28 -0
- package/dist/templates/pi/agents/trellis-implement.md +33 -0
- package/dist/templates/pi/agents/trellis-research.md +25 -0
- package/dist/templates/pi/extensions/trellis/index.ts.txt +549 -0
- package/dist/templates/pi/index.d.ts +5 -0
- package/dist/templates/pi/index.d.ts.map +1 -0
- package/dist/templates/pi/index.js +12 -0
- package/dist/templates/pi/index.js.map +1 -0
- package/dist/templates/pi/settings.json +12 -0
- package/dist/templates/qoder/agents/trellis-research.md +1 -1
- package/dist/templates/shared-hooks/index.d.ts +31 -0
- package/dist/templates/shared-hooks/index.d.ts.map +1 -1
- package/dist/templates/shared-hooks/index.js +59 -0
- package/dist/templates/shared-hooks/index.js.map +1 -1
- package/dist/templates/shared-hooks/inject-shell-session-context.py +180 -0
- package/dist/templates/shared-hooks/inject-subagent-context.py +128 -26
- package/dist/templates/shared-hooks/inject-workflow-state.py +101 -61
- package/dist/templates/shared-hooks/session-start.py +151 -28
- package/dist/templates/trellis/gitignore.txt +3 -0
- package/dist/templates/trellis/index.d.ts +1 -0
- package/dist/templates/trellis/index.d.ts.map +1 -1
- package/dist/templates/trellis/index.js +2 -0
- package/dist/templates/trellis/index.js.map +1 -1
- package/dist/templates/trellis/scripts/common/__init__.py +8 -0
- package/dist/templates/trellis/scripts/common/active_task.py +593 -0
- package/dist/templates/trellis/scripts/common/cli_adapter.py +43 -8
- package/dist/templates/trellis/scripts/common/paths.py +61 -58
- package/dist/templates/trellis/scripts/common/session_context.py +12 -0
- package/dist/templates/trellis/scripts/common/task_store.py +4 -6
- package/dist/templates/trellis/scripts/task.py +56 -14
- package/dist/templates/trellis/workflow.md +31 -26
- package/dist/types/ai-tools.d.ts +3 -3
- package/dist/types/ai-tools.d.ts.map +1 -1
- package/dist/types/ai-tools.js +16 -0
- package/dist/types/ai-tools.js.map +1 -1
- package/dist/utils/template-fetcher.d.ts +22 -6
- package/dist/utils/template-fetcher.d.ts.map +1 -1
- package/dist/utils/template-fetcher.js +405 -27
- package/dist/utils/template-fetcher.js.map +1 -1
- package/dist/utils/template-hash.d.ts.map +1 -1
- package/dist/utils/template-hash.js +3 -2
- package/dist/utils/template-hash.js.map +1 -1
- package/package.json +1 -1
- package/dist/templates/shared-hooks/statusline.py +0 -218
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
CLI Adapter for Multi-Platform Support.
|
|
3
3
|
|
|
4
|
-
Abstracts differences between Claude Code, OpenCode, Cursor, iFlow, Codex, Kilo, Kiro Code, Gemini CLI, Antigravity, Windsurf, Qoder, CodeBuddy, GitHub Copilot,
|
|
4
|
+
Abstracts differences between Claude Code, OpenCode, Cursor, iFlow, Codex, Kilo, Kiro Code, Gemini CLI, Antigravity, Windsurf, Qoder, CodeBuddy, GitHub Copilot, Factory Droid, and Pi Agent interfaces.
|
|
5
5
|
|
|
6
6
|
Supported platforms:
|
|
7
7
|
- claude: Claude Code (default)
|
|
@@ -18,6 +18,7 @@ Supported platforms:
|
|
|
18
18
|
- codebuddy: CodeBuddy
|
|
19
19
|
- copilot: GitHub Copilot (VS Code)
|
|
20
20
|
- droid: Factory Droid (commands-based)
|
|
21
|
+
- pi: Pi Agent (extension-backed)
|
|
21
22
|
|
|
22
23
|
Usage:
|
|
23
24
|
from common.cli_adapter import CLIAdapter
|
|
@@ -51,6 +52,7 @@ Platform = Literal[
|
|
|
51
52
|
"codebuddy",
|
|
52
53
|
"copilot",
|
|
53
54
|
"droid",
|
|
55
|
+
"pi",
|
|
54
56
|
]
|
|
55
57
|
|
|
56
58
|
|
|
@@ -95,7 +97,7 @@ class CLIAdapter:
|
|
|
95
97
|
"""Get platform-specific config directory name.
|
|
96
98
|
|
|
97
99
|
Returns:
|
|
98
|
-
Directory name ('.claude', '.opencode', '.cursor', '.iflow', '.codex', '.kilocode', '.kiro', '.gemini', '.agent', '.windsurf', '.qoder', or '.
|
|
100
|
+
Directory name ('.claude', '.opencode', '.cursor', '.iflow', '.codex', '.kilocode', '.kiro', '.gemini', '.agent', '.windsurf', '.qoder', '.codebuddy', '.github/copilot', '.factory', or '.pi')
|
|
99
101
|
"""
|
|
100
102
|
if self.platform == "opencode":
|
|
101
103
|
return ".opencode"
|
|
@@ -123,6 +125,8 @@ class CLIAdapter:
|
|
|
123
125
|
return ".github/copilot"
|
|
124
126
|
elif self.platform == "droid":
|
|
125
127
|
return ".factory"
|
|
128
|
+
elif self.platform == "pi":
|
|
129
|
+
return ".pi"
|
|
126
130
|
else:
|
|
127
131
|
return ".claude"
|
|
128
132
|
|
|
@@ -133,7 +137,7 @@ class CLIAdapter:
|
|
|
133
137
|
project_root: Project root directory
|
|
134
138
|
|
|
135
139
|
Returns:
|
|
136
|
-
Path to config directory (.claude, .opencode, .cursor, .iflow, .codex, .kilocode, .kiro, .gemini, .agent, .windsurf, .qoder, or .
|
|
140
|
+
Path to config directory (.claude, .opencode, .cursor, .iflow, .codex, .kilocode, .kiro, .gemini, .agent, .windsurf, .qoder, .codebuddy, .github/copilot, .factory, or .pi)
|
|
137
141
|
"""
|
|
138
142
|
return project_root / self.config_dir_name
|
|
139
143
|
|
|
@@ -167,8 +171,20 @@ class CLIAdapter:
|
|
|
167
171
|
Antigravity uses workflow directory: .agent/workflows/<name>.md
|
|
168
172
|
Windsurf uses workflow directory: .windsurf/workflows/trellis-<name>.md
|
|
169
173
|
Copilot uses prompt files: .github/prompts/<name>.prompt.md
|
|
174
|
+
Pi uses prompt templates: .pi/prompts/trellis-<name>.md
|
|
170
175
|
Claude/OpenCode use subdirectory: .claude/commands/trellis/<name>.md
|
|
171
176
|
"""
|
|
177
|
+
if self.platform == "pi":
|
|
178
|
+
prompts_dir = self.get_config_dir(project_root) / "prompts"
|
|
179
|
+
if not parts:
|
|
180
|
+
return prompts_dir
|
|
181
|
+
if len(parts) >= 2 and parts[0] == "trellis":
|
|
182
|
+
filename = parts[-1]
|
|
183
|
+
if filename.endswith(".md"):
|
|
184
|
+
filename = filename[:-3]
|
|
185
|
+
return prompts_dir / f"trellis-{filename}.md"
|
|
186
|
+
return prompts_dir / Path(*parts)
|
|
187
|
+
|
|
172
188
|
if self.platform == "windsurf":
|
|
173
189
|
workflow_dir = self.get_config_dir(project_root) / "workflows"
|
|
174
190
|
if not parts:
|
|
@@ -227,6 +243,7 @@ class CLIAdapter:
|
|
|
227
243
|
Gemini: .gemini/commands/trellis/<name>.toml
|
|
228
244
|
Antigravity: .agent/workflows/<name>.md
|
|
229
245
|
Windsurf: .windsurf/workflows/trellis-<name>.md
|
|
246
|
+
Pi: .pi/prompts/trellis-<name>.md
|
|
230
247
|
Others: .{platform}/commands/trellis/<name>.md
|
|
231
248
|
"""
|
|
232
249
|
if self.platform == "cursor":
|
|
@@ -249,6 +266,8 @@ class CLIAdapter:
|
|
|
249
266
|
return f".github/prompts/{name}.prompt.md"
|
|
250
267
|
elif self.platform == "droid":
|
|
251
268
|
return f".factory/commands/trellis/{name}.md"
|
|
269
|
+
elif self.platform == "pi":
|
|
270
|
+
return f".pi/prompts/trellis-{name}.md"
|
|
252
271
|
else:
|
|
253
272
|
return f"{self.config_dir_name}/commands/trellis/{name}.md"
|
|
254
273
|
|
|
@@ -284,6 +303,8 @@ class CLIAdapter:
|
|
|
284
303
|
return {}
|
|
285
304
|
elif self.platform == "droid":
|
|
286
305
|
return {}
|
|
306
|
+
elif self.platform == "pi":
|
|
307
|
+
return {}
|
|
287
308
|
else:
|
|
288
309
|
return {"CLAUDE_NON_INTERACTIVE": "1"}
|
|
289
310
|
|
|
@@ -367,6 +388,8 @@ class CLIAdapter:
|
|
|
367
388
|
raise ValueError(
|
|
368
389
|
"Factory Droid CLI agent run is not yet supported."
|
|
369
390
|
)
|
|
391
|
+
elif self.platform == "pi":
|
|
392
|
+
cmd = ["pi", "-p", prompt]
|
|
370
393
|
|
|
371
394
|
else: # claude
|
|
372
395
|
cmd = ["claude", "-p"]
|
|
@@ -431,6 +454,8 @@ class CLIAdapter:
|
|
|
431
454
|
raise ValueError(
|
|
432
455
|
"Factory Droid CLI resume is not yet supported."
|
|
433
456
|
)
|
|
457
|
+
elif self.platform == "pi":
|
|
458
|
+
return ["pi", "-c", session_id]
|
|
434
459
|
else:
|
|
435
460
|
return ["claude", "--resume", session_id]
|
|
436
461
|
|
|
@@ -503,6 +528,8 @@ class CLIAdapter:
|
|
|
503
528
|
return "copilot"
|
|
504
529
|
elif self.platform == "droid":
|
|
505
530
|
return "droid"
|
|
531
|
+
elif self.platform == "pi":
|
|
532
|
+
return "pi"
|
|
506
533
|
else:
|
|
507
534
|
return "claude"
|
|
508
535
|
|
|
@@ -513,7 +540,7 @@ class CLIAdapter:
|
|
|
513
540
|
Claude Code, OpenCode, iFlow, and Codex support CLI agent execution.
|
|
514
541
|
Cursor is IDE-only and doesn't support CLI agents.
|
|
515
542
|
"""
|
|
516
|
-
return self.platform in ("claude", "opencode", "iflow", "codex")
|
|
543
|
+
return self.platform in ("claude", "opencode", "iflow", "codex", "pi")
|
|
517
544
|
|
|
518
545
|
@property
|
|
519
546
|
def requires_agent_definition_file(self) -> bool:
|
|
@@ -567,7 +594,7 @@ def get_cli_adapter(platform: str = "claude") -> CLIAdapter:
|
|
|
567
594
|
"""Get CLI adapter for the specified platform.
|
|
568
595
|
|
|
569
596
|
Args:
|
|
570
|
-
platform: Platform name ('claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', 'windsurf', 'qoder', or '
|
|
597
|
+
platform: Platform name ('claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', 'windsurf', 'qoder', 'codebuddy', 'copilot', 'droid', or 'pi')
|
|
571
598
|
|
|
572
599
|
Returns:
|
|
573
600
|
CLIAdapter instance
|
|
@@ -590,9 +617,10 @@ def get_cli_adapter(platform: str = "claude") -> CLIAdapter:
|
|
|
590
617
|
"codebuddy",
|
|
591
618
|
"copilot",
|
|
592
619
|
"droid",
|
|
620
|
+
"pi",
|
|
593
621
|
):
|
|
594
622
|
raise ValueError(
|
|
595
|
-
f"Unsupported platform: {platform} (must be 'claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', 'windsurf', 'qoder', 'codebuddy', 'copilot', or '
|
|
623
|
+
f"Unsupported platform: {platform} (must be 'claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', 'windsurf', 'qoder', 'codebuddy', 'copilot', 'droid', or 'pi')"
|
|
596
624
|
)
|
|
597
625
|
|
|
598
626
|
return CLIAdapter(platform=platform) # type: ignore
|
|
@@ -613,6 +641,7 @@ _ALL_PLATFORM_CONFIG_DIRS = (
|
|
|
613
641
|
".codebuddy",
|
|
614
642
|
".github/copilot",
|
|
615
643
|
".factory",
|
|
644
|
+
".pi",
|
|
616
645
|
)
|
|
617
646
|
"""Platform-specific config directory names used by detect_platform exclusion
|
|
618
647
|
checks. `.agents/skills/` is NOT listed here: it is a shared cross-platform
|
|
@@ -646,13 +675,14 @@ def detect_platform(project_root: Path) -> Platform:
|
|
|
646
675
|
10. .windsurf/workflows exists and no other platform dirs → windsurf
|
|
647
676
|
11. .codebuddy directory exists → codebuddy
|
|
648
677
|
12. .qoder directory exists → qoder
|
|
649
|
-
13.
|
|
678
|
+
13. .pi directory exists → pi
|
|
679
|
+
14. Default → claude
|
|
650
680
|
|
|
651
681
|
Args:
|
|
652
682
|
project_root: Project root directory
|
|
653
683
|
|
|
654
684
|
Returns:
|
|
655
|
-
Detected platform ('claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', 'windsurf', 'qoder', 'codebuddy', or default 'claude')
|
|
685
|
+
Detected platform ('claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', 'windsurf', 'qoder', 'codebuddy', 'copilot', 'droid', 'pi', or default 'claude')
|
|
656
686
|
"""
|
|
657
687
|
import os
|
|
658
688
|
|
|
@@ -673,6 +703,7 @@ def detect_platform(project_root: Path) -> Platform:
|
|
|
673
703
|
"codebuddy",
|
|
674
704
|
"copilot",
|
|
675
705
|
"droid",
|
|
706
|
+
"pi",
|
|
676
707
|
):
|
|
677
708
|
return env_platform # type: ignore
|
|
678
709
|
|
|
@@ -742,6 +773,10 @@ def detect_platform(project_root: Path) -> Platform:
|
|
|
742
773
|
if (project_root / ".factory").is_dir():
|
|
743
774
|
return "droid"
|
|
744
775
|
|
|
776
|
+
# Check for .pi directory (Pi Agent-specific)
|
|
777
|
+
if (project_root / ".pi").is_dir():
|
|
778
|
+
return "pi"
|
|
779
|
+
|
|
745
780
|
# Fallback: checkout only has the Codex shared-skills layer
|
|
746
781
|
# (.agents/skills/trellis-* dirs) and no explicit platform config dir.
|
|
747
782
|
# Happens on fresh clones where .codex/ is gitignored/absent but the
|
|
@@ -207,22 +207,8 @@ def count_lines(file_path: Path) -> int:
|
|
|
207
207
|
# Current Task Management
|
|
208
208
|
# =============================================================================
|
|
209
209
|
|
|
210
|
-
def _get_current_task_file(repo_root: Path | None = None) -> Path:
|
|
211
|
-
"""Get .current-task file path.
|
|
212
|
-
|
|
213
|
-
Args:
|
|
214
|
-
repo_root: Repository root path. Defaults to auto-detected.
|
|
215
|
-
|
|
216
|
-
Returns:
|
|
217
|
-
Path to .current-task file.
|
|
218
|
-
"""
|
|
219
|
-
if repo_root is None:
|
|
220
|
-
repo_root = get_repo_root()
|
|
221
|
-
return repo_root / DIR_WORKFLOW / FILE_CURRENT_TASK
|
|
222
|
-
|
|
223
|
-
|
|
224
210
|
def normalize_task_ref(task_ref: str) -> str:
|
|
225
|
-
"""Normalize a task ref for stable storage
|
|
211
|
+
"""Normalize a task ref for stable runtime storage.
|
|
226
212
|
|
|
227
213
|
Stored refs should prefer repo-relative POSIX paths like
|
|
228
214
|
`.trellis/tasks/03-27-my-task`, even on Windows. Absolute paths are preserved
|
|
@@ -247,7 +233,7 @@ def normalize_task_ref(task_ref: str) -> str:
|
|
|
247
233
|
|
|
248
234
|
|
|
249
235
|
def resolve_task_ref(task_ref: str, repo_root: Path | None = None) -> Path | None:
|
|
250
|
-
"""Resolve a task ref
|
|
236
|
+
"""Resolve a task ref to an absolute task directory path."""
|
|
251
237
|
if repo_root is None:
|
|
252
238
|
repo_root = get_repo_root()
|
|
253
239
|
|
|
@@ -265,7 +251,11 @@ def resolve_task_ref(task_ref: str, repo_root: Path | None = None) -> Path | Non
|
|
|
265
251
|
return repo_root / DIR_WORKFLOW / DIR_TASKS / path_obj
|
|
266
252
|
|
|
267
253
|
|
|
268
|
-
def get_current_task(
|
|
254
|
+
def get_current_task(
|
|
255
|
+
repo_root: Path | None = None,
|
|
256
|
+
platform_input: dict | None = None,
|
|
257
|
+
platform: str | None = None,
|
|
258
|
+
) -> str | None:
|
|
269
259
|
"""Get current task directory path (relative to repo_root).
|
|
270
260
|
|
|
271
261
|
Args:
|
|
@@ -274,19 +264,19 @@ def get_current_task(repo_root: Path | None = None) -> str | None:
|
|
|
274
264
|
Returns:
|
|
275
265
|
Relative path to current task directory or None.
|
|
276
266
|
"""
|
|
277
|
-
|
|
267
|
+
if repo_root is None:
|
|
268
|
+
repo_root = get_repo_root()
|
|
278
269
|
|
|
279
|
-
|
|
280
|
-
return None
|
|
270
|
+
from .active_task import resolve_active_task
|
|
281
271
|
|
|
282
|
-
|
|
283
|
-
content = current_file.read_text(encoding="utf-8").strip()
|
|
284
|
-
return normalize_task_ref(content) if content else None
|
|
285
|
-
except (OSError, IOError):
|
|
286
|
-
return None
|
|
272
|
+
return resolve_active_task(repo_root, platform_input, platform).task_path
|
|
287
273
|
|
|
288
274
|
|
|
289
|
-
def get_current_task_abs(
|
|
275
|
+
def get_current_task_abs(
|
|
276
|
+
repo_root: Path | None = None,
|
|
277
|
+
platform_input: dict | None = None,
|
|
278
|
+
platform: str | None = None,
|
|
279
|
+
) -> Path | None:
|
|
290
280
|
"""Get current task directory absolute path.
|
|
291
281
|
|
|
292
282
|
Args:
|
|
@@ -298,14 +288,33 @@ def get_current_task_abs(repo_root: Path | None = None) -> Path | None:
|
|
|
298
288
|
if repo_root is None:
|
|
299
289
|
repo_root = get_repo_root()
|
|
300
290
|
|
|
301
|
-
relative = get_current_task(repo_root)
|
|
291
|
+
relative = get_current_task(repo_root, platform_input, platform)
|
|
302
292
|
if relative:
|
|
303
293
|
return resolve_task_ref(relative, repo_root)
|
|
304
294
|
return None
|
|
305
295
|
|
|
306
296
|
|
|
307
|
-
def
|
|
308
|
-
|
|
297
|
+
def get_current_task_source(
|
|
298
|
+
repo_root: Path | None = None,
|
|
299
|
+
platform_input: dict | None = None,
|
|
300
|
+
platform: str | None = None,
|
|
301
|
+
) -> tuple[str, str | None, str | None]:
|
|
302
|
+
"""Get active task source as (`source`, `context_key`, `task_path`)."""
|
|
303
|
+
if repo_root is None:
|
|
304
|
+
repo_root = get_repo_root()
|
|
305
|
+
|
|
306
|
+
from .active_task import get_current_task_source as _get_source
|
|
307
|
+
|
|
308
|
+
return _get_source(repo_root, platform_input, platform)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def set_current_task(
|
|
312
|
+
task_path: str,
|
|
313
|
+
repo_root: Path | None = None,
|
|
314
|
+
platform_input: dict | None = None,
|
|
315
|
+
platform: str | None = None,
|
|
316
|
+
) -> bool:
|
|
317
|
+
"""Set current task in session scope.
|
|
309
318
|
|
|
310
319
|
Args:
|
|
311
320
|
task_path: Task directory path (relative to repo_root).
|
|
@@ -317,31 +326,22 @@ def set_current_task(task_path: str, repo_root: Path | None = None) -> bool:
|
|
|
317
326
|
if repo_root is None:
|
|
318
327
|
repo_root = get_repo_root()
|
|
319
328
|
|
|
320
|
-
|
|
321
|
-
if not normalized:
|
|
322
|
-
return False
|
|
329
|
+
from .active_task import set_active_task
|
|
323
330
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
331
|
+
return set_active_task(
|
|
332
|
+
task_path,
|
|
333
|
+
repo_root,
|
|
334
|
+
platform_input=platform_input,
|
|
335
|
+
platform=platform,
|
|
336
|
+
) is not None
|
|
328
337
|
|
|
329
|
-
try:
|
|
330
|
-
normalized = full_path.relative_to(repo_root).as_posix()
|
|
331
|
-
except ValueError:
|
|
332
|
-
normalized = str(full_path)
|
|
333
338
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
return False
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
def clear_current_task(repo_root: Path | None = None) -> bool:
|
|
344
|
-
"""Clear current task.
|
|
339
|
+
def clear_current_task(
|
|
340
|
+
repo_root: Path | None = None,
|
|
341
|
+
platform_input: dict | None = None,
|
|
342
|
+
platform: str | None = None,
|
|
343
|
+
) -> bool:
|
|
344
|
+
"""Clear current task in session scope.
|
|
345
345
|
|
|
346
346
|
Args:
|
|
347
347
|
repo_root: Repository root path. Defaults to auto-detected.
|
|
@@ -349,14 +349,17 @@ def clear_current_task(repo_root: Path | None = None) -> bool:
|
|
|
349
349
|
Returns:
|
|
350
350
|
True on success.
|
|
351
351
|
"""
|
|
352
|
-
|
|
352
|
+
if repo_root is None:
|
|
353
|
+
repo_root = get_repo_root()
|
|
353
354
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
355
|
+
from .active_task import clear_active_task
|
|
356
|
+
|
|
357
|
+
clear_active_task(
|
|
358
|
+
repo_root,
|
|
359
|
+
platform_input=platform_input,
|
|
360
|
+
platform=platform,
|
|
361
|
+
)
|
|
362
|
+
return True
|
|
360
363
|
|
|
361
364
|
|
|
362
365
|
def has_current_task(repo_root: Path | None = None) -> bool:
|
|
@@ -29,6 +29,7 @@ from .paths import (
|
|
|
29
29
|
count_lines,
|
|
30
30
|
get_active_journal_file,
|
|
31
31
|
get_current_task,
|
|
32
|
+
get_current_task_source,
|
|
32
33
|
get_developer,
|
|
33
34
|
get_repo_root,
|
|
34
35
|
get_tasks_dir,
|
|
@@ -279,7 +280,11 @@ def get_context_text(repo_root: Path | None = None) -> str:
|
|
|
279
280
|
current_task = get_current_task(repo_root)
|
|
280
281
|
if current_task:
|
|
281
282
|
current_task_dir = repo_root / current_task
|
|
283
|
+
source_type, context_key, _ = get_current_task_source(repo_root)
|
|
282
284
|
lines.append(f"Path: {current_task}")
|
|
285
|
+
lines.append(
|
|
286
|
+
f"Source: {source_type}" + (f":{context_key}" if context_key else "")
|
|
287
|
+
)
|
|
283
288
|
|
|
284
289
|
ct = load_task(current_task_dir)
|
|
285
290
|
if ct:
|
|
@@ -429,12 +434,15 @@ def get_context_record_json(repo_root: Path | None = None) -> dict:
|
|
|
429
434
|
current_task_info = None
|
|
430
435
|
current_task = get_current_task(repo_root)
|
|
431
436
|
if current_task:
|
|
437
|
+
source_type, context_key, _ = get_current_task_source(repo_root)
|
|
432
438
|
ct = load_task(repo_root / current_task)
|
|
433
439
|
if ct:
|
|
434
440
|
current_task_info = {
|
|
435
441
|
"path": current_task,
|
|
436
442
|
"name": ct.name,
|
|
437
443
|
"status": ct.status,
|
|
444
|
+
"source": source_type,
|
|
445
|
+
"contextKey": context_key,
|
|
438
446
|
}
|
|
439
447
|
|
|
440
448
|
# Package git repos
|
|
@@ -539,7 +547,11 @@ def get_context_text_record(repo_root: Path | None = None) -> str:
|
|
|
539
547
|
lines.append("## CURRENT TASK")
|
|
540
548
|
current_task = get_current_task(repo_root)
|
|
541
549
|
if current_task:
|
|
550
|
+
source_type, context_key, _ = get_current_task_source(repo_root)
|
|
542
551
|
lines.append(f"Path: {current_task}")
|
|
552
|
+
lines.append(
|
|
553
|
+
f"Source: {source_type}" + (f":{context_key}" if context_key else "")
|
|
554
|
+
)
|
|
543
555
|
ct = load_task(repo_root / current_task)
|
|
544
556
|
if ct:
|
|
545
557
|
lines.append(f"Name: {ct.name}")
|
|
@@ -36,9 +36,7 @@ from .paths import (
|
|
|
36
36
|
DIR_TASKS,
|
|
37
37
|
DIR_WORKFLOW,
|
|
38
38
|
FILE_TASK_JSON,
|
|
39
|
-
clear_current_task,
|
|
40
39
|
generate_task_date_prefix,
|
|
41
|
-
get_current_task,
|
|
42
40
|
get_developer,
|
|
43
41
|
get_repo_root,
|
|
44
42
|
get_tasks_dir,
|
|
@@ -99,6 +97,7 @@ _SUBAGENT_CONFIG_DIRS: tuple[str, ...] = (
|
|
|
99
97
|
".codebuddy",
|
|
100
98
|
".factory", # Factory Droid
|
|
101
99
|
".github/copilot",
|
|
100
|
+
".pi", # Pi Agent
|
|
102
101
|
)
|
|
103
102
|
|
|
104
103
|
_SEED_EXAMPLE = (
|
|
@@ -353,10 +352,9 @@ def cmd_archive(args: argparse.Namespace) -> int:
|
|
|
353
352
|
child_data["parent"] = None
|
|
354
353
|
write_json(child_json, child_data)
|
|
355
354
|
|
|
356
|
-
# Clear
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
clear_current_task(repo_root)
|
|
355
|
+
# Clear any session that still points at this task before the path moves.
|
|
356
|
+
from .active_task import clear_task_from_sessions
|
|
357
|
+
clear_task_from_sessions(str(task_dir), repo_root)
|
|
360
358
|
|
|
361
359
|
# Archive
|
|
362
360
|
result = archive_task_complete(task_dir, repo_root)
|
|
@@ -8,8 +8,9 @@ Usage:
|
|
|
8
8
|
python3 task.py add-context <dir> <file> <path> [reason] # Add jsonl entry
|
|
9
9
|
python3 task.py validate <dir> # Validate jsonl files
|
|
10
10
|
python3 task.py list-context <dir> # List jsonl entries
|
|
11
|
-
python3 task.py start <dir> # Set
|
|
12
|
-
python3 task.py
|
|
11
|
+
python3 task.py start <dir> # Set active task
|
|
12
|
+
python3 task.py current [--source] # Show active task
|
|
13
|
+
python3 task.py finish # Clear active task
|
|
13
14
|
python3 task.py set-branch <dir> <branch> # Set git branch
|
|
14
15
|
python3 task.py set-base-branch <dir> <branch> # Set PR target branch
|
|
15
16
|
python3 task.py set-scope <dir> <scope> # Set scope for PR title
|
|
@@ -34,8 +35,12 @@ from common.paths import (
|
|
|
34
35
|
get_developer,
|
|
35
36
|
get_tasks_dir,
|
|
36
37
|
get_current_task,
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
)
|
|
39
|
+
from common.active_task import (
|
|
40
|
+
clear_active_task,
|
|
41
|
+
resolve_active_task,
|
|
42
|
+
resolve_context_key,
|
|
43
|
+
set_active_task,
|
|
39
44
|
)
|
|
40
45
|
from common.io import read_json, write_json
|
|
41
46
|
from common.task_utils import resolve_task_dir, run_task_hooks
|
|
@@ -63,7 +68,7 @@ from common.task_context import (
|
|
|
63
68
|
# =============================================================================
|
|
64
69
|
|
|
65
70
|
def cmd_start(args: argparse.Namespace) -> int:
|
|
66
|
-
"""Set
|
|
71
|
+
"""Set active task."""
|
|
67
72
|
repo_root = get_repo_root()
|
|
68
73
|
task_input = args.dir
|
|
69
74
|
|
|
@@ -85,8 +90,18 @@ def cmd_start(args: argparse.Namespace) -> int:
|
|
|
85
90
|
except ValueError:
|
|
86
91
|
task_dir = str(full_path)
|
|
87
92
|
|
|
88
|
-
if
|
|
93
|
+
if not resolve_context_key():
|
|
94
|
+
print(colored("Error: Cannot set active task without a session identity.", Colors.RED))
|
|
95
|
+
print(
|
|
96
|
+
"Hint: run inside an AI IDE/session that exposes session identity, "
|
|
97
|
+
"or set TRELLIS_CONTEXT_ID before running task.py start."
|
|
98
|
+
)
|
|
99
|
+
return 1
|
|
100
|
+
|
|
101
|
+
active = set_active_task(task_dir, repo_root)
|
|
102
|
+
if active:
|
|
89
103
|
print(colored(f"✓ Current task set to: {task_dir}", Colors.GREEN))
|
|
104
|
+
print(f"Source: {active.source}")
|
|
90
105
|
|
|
91
106
|
task_json_path = full_path / FILE_TASK_JSON
|
|
92
107
|
if task_json_path.is_file():
|
|
@@ -107,10 +122,10 @@ def cmd_start(args: argparse.Namespace) -> int:
|
|
|
107
122
|
|
|
108
123
|
|
|
109
124
|
def cmd_finish(args: argparse.Namespace) -> int:
|
|
110
|
-
"""Clear
|
|
111
|
-
_ = args # signature required by argparse dispatcher
|
|
125
|
+
"""Clear active task."""
|
|
112
126
|
repo_root = get_repo_root()
|
|
113
|
-
|
|
127
|
+
active = clear_active_task(repo_root)
|
|
128
|
+
current = active.task_path
|
|
114
129
|
|
|
115
130
|
if not current:
|
|
116
131
|
print(colored("No current task set", Colors.YELLOW))
|
|
@@ -119,14 +134,33 @@ def cmd_finish(args: argparse.Namespace) -> int:
|
|
|
119
134
|
# Resolve task.json path before clearing
|
|
120
135
|
task_json_path = repo_root / current / FILE_TASK_JSON
|
|
121
136
|
|
|
122
|
-
clear_current_task(repo_root)
|
|
123
137
|
print(colored(f"✓ Cleared current task (was: {current})", Colors.GREEN))
|
|
138
|
+
print(f"Source: {active.source}")
|
|
124
139
|
|
|
125
140
|
if task_json_path.is_file():
|
|
126
141
|
run_task_hooks("after_finish", task_json_path, repo_root)
|
|
127
142
|
return 0
|
|
128
143
|
|
|
129
144
|
|
|
145
|
+
def cmd_current(args: argparse.Namespace) -> int:
|
|
146
|
+
"""Show active task."""
|
|
147
|
+
repo_root = get_repo_root()
|
|
148
|
+
active = resolve_active_task(repo_root)
|
|
149
|
+
|
|
150
|
+
if args.source:
|
|
151
|
+
print(f"Current task: {active.task_path or '(none)'}")
|
|
152
|
+
print(f"Source: {active.source}")
|
|
153
|
+
if active.stale:
|
|
154
|
+
print("State: stale")
|
|
155
|
+
return 0 if active.task_path else 1
|
|
156
|
+
|
|
157
|
+
if active.task_path:
|
|
158
|
+
print(active.task_path)
|
|
159
|
+
return 0
|
|
160
|
+
|
|
161
|
+
return 1
|
|
162
|
+
|
|
163
|
+
|
|
130
164
|
# =============================================================================
|
|
131
165
|
# Command: list
|
|
132
166
|
# =============================================================================
|
|
@@ -257,8 +291,9 @@ Usage:
|
|
|
257
291
|
python3 task.py add-context <dir> <jsonl> <path> [reason] Add entry to jsonl
|
|
258
292
|
python3 task.py validate <dir> Validate jsonl files
|
|
259
293
|
python3 task.py list-context <dir> List jsonl entries
|
|
260
|
-
python3 task.py start <dir> Set
|
|
261
|
-
python3 task.py
|
|
294
|
+
python3 task.py start <dir> Set active task
|
|
295
|
+
python3 task.py current [--source] Show active task
|
|
296
|
+
python3 task.py finish Clear active task
|
|
262
297
|
python3 task.py set-branch <dir> <branch> Set git branch
|
|
263
298
|
python3 task.py set-base-branch <dir> <branch> Set PR target branch
|
|
264
299
|
python3 task.py set-scope <dir> <scope> Set scope for PR title
|
|
@@ -282,6 +317,7 @@ Examples:
|
|
|
282
317
|
python3 task.py add-context <dir> implement .trellis/spec/cli/backend/auth.md "Auth guidelines"
|
|
283
318
|
python3 task.py set-branch <dir> task/add-login
|
|
284
319
|
python3 task.py start .trellis/tasks/01-21-add-login
|
|
320
|
+
python3 task.py current --source
|
|
285
321
|
python3 task.py finish
|
|
286
322
|
python3 task.py archive add-login
|
|
287
323
|
python3 task.py add-subtask parent-task child-task # Link existing tasks
|
|
@@ -360,11 +396,16 @@ def main() -> int:
|
|
|
360
396
|
p_listctx.add_argument("dir", help="Task directory")
|
|
361
397
|
|
|
362
398
|
# start
|
|
363
|
-
p_start = subparsers.add_parser("start", help="Set
|
|
399
|
+
p_start = subparsers.add_parser("start", help="Set active task")
|
|
364
400
|
p_start.add_argument("dir", help="Task directory")
|
|
365
401
|
|
|
402
|
+
# current
|
|
403
|
+
p_current = subparsers.add_parser("current", help="Show active task")
|
|
404
|
+
p_current.add_argument("--source", action="store_true",
|
|
405
|
+
help="Show active task source")
|
|
406
|
+
|
|
366
407
|
# finish
|
|
367
|
-
subparsers.add_parser("finish", help="Clear
|
|
408
|
+
subparsers.add_parser("finish", help="Clear active task")
|
|
368
409
|
|
|
369
410
|
# set-branch
|
|
370
411
|
p_branch = subparsers.add_parser("set-branch", help="Set git branch")
|
|
@@ -417,6 +458,7 @@ def main() -> int:
|
|
|
417
458
|
"validate": cmd_validate,
|
|
418
459
|
"list-context": cmd_list_context,
|
|
419
460
|
"start": cmd_start,
|
|
461
|
+
"current": cmd_current,
|
|
420
462
|
"finish": cmd_finish,
|
|
421
463
|
"set-branch": cmd_set_branch,
|
|
422
464
|
"set-base-branch": cmd_set_base_branch,
|