@mindfoldhq/trellis 0.6.4 → 0.6.6

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 (116) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/index.js +2 -0
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/commands/channel/messages.js +1 -1
  5. package/dist/commands/channel/supervisor/stdout.d.ts +7 -5
  6. package/dist/commands/channel/supervisor/stdout.d.ts.map +1 -1
  7. package/dist/commands/channel/supervisor/stdout.js +65 -18
  8. package/dist/commands/channel/supervisor/stdout.js.map +1 -1
  9. package/dist/commands/channel/supervisor.d.ts +13 -0
  10. package/dist/commands/channel/supervisor.d.ts.map +1 -1
  11. package/dist/commands/channel/supervisor.js +54 -2
  12. package/dist/commands/channel/supervisor.js.map +1 -1
  13. package/dist/commands/init.d.ts +2 -0
  14. package/dist/commands/init.d.ts.map +1 -1
  15. package/dist/commands/init.js.map +1 -1
  16. package/dist/commands/uninstall.d.ts.map +1 -1
  17. package/dist/commands/uninstall.js +8 -1
  18. package/dist/commands/uninstall.js.map +1 -1
  19. package/dist/commands/update.d.ts.map +1 -1
  20. package/dist/commands/update.js +83 -42
  21. package/dist/commands/update.js.map +1 -1
  22. package/dist/configurators/copilot.d.ts +1 -0
  23. package/dist/configurators/copilot.d.ts.map +1 -1
  24. package/dist/configurators/copilot.js +4 -1
  25. package/dist/configurators/copilot.js.map +1 -1
  26. package/dist/configurators/index.d.ts.map +1 -1
  27. package/dist/configurators/index.js +27 -1
  28. package/dist/configurators/index.js.map +1 -1
  29. package/dist/configurators/omp.d.ts +11 -0
  30. package/dist/configurators/omp.d.ts.map +1 -0
  31. package/dist/configurators/omp.js +51 -0
  32. package/dist/configurators/omp.js.map +1 -0
  33. package/dist/configurators/pi.d.ts.map +1 -1
  34. package/dist/configurators/pi.js +14 -4
  35. package/dist/configurators/pi.js.map +1 -1
  36. package/dist/configurators/shared.d.ts +7 -0
  37. package/dist/configurators/shared.d.ts.map +1 -1
  38. package/dist/configurators/shared.js +27 -0
  39. package/dist/configurators/shared.js.map +1 -1
  40. package/dist/configurators/trae.d.ts +17 -0
  41. package/dist/configurators/trae.d.ts.map +1 -0
  42. package/dist/configurators/trae.js +42 -0
  43. package/dist/configurators/trae.js.map +1 -0
  44. package/dist/configurators/zcode.d.ts +3 -3
  45. package/dist/configurators/zcode.js +12 -12
  46. package/dist/configurators/zcode.js.map +1 -1
  47. package/dist/migrations/manifests/0.6.5.json +9 -0
  48. package/dist/migrations/manifests/0.6.6.json +17 -0
  49. package/dist/templates/codex/hooks/session-start.py +6 -6
  50. package/dist/templates/codex/skills/brainstorm/SKILL.md +19 -1
  51. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-agents.md +1 -1
  52. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-hooks.md +1 -1
  53. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-skills-or-commands.md +1 -1
  54. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/bundled-skills.md +2 -1
  55. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/generated-files.md +3 -3
  56. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/agents.md +1 -1
  57. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/hooks-and-settings.md +1 -0
  58. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/overview.md +5 -5
  59. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/platform-map.md +21 -2
  60. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/skills-and-commands.md +1 -1
  61. package/dist/templates/common/skills/brainstorm.md +19 -1
  62. package/dist/templates/copilot/copilot-instructions.md +11 -0
  63. package/dist/templates/copilot/hooks/session-start.py +6 -6
  64. package/dist/templates/copilot/index.d.ts +6 -1
  65. package/dist/templates/copilot/index.d.ts.map +1 -1
  66. package/dist/templates/copilot/index.js +8 -1
  67. package/dist/templates/copilot/index.js.map +1 -1
  68. package/dist/templates/copilot/prompts/brainstorm.prompt.md +19 -1
  69. package/dist/templates/omp/agents/trellis-check.md +41 -0
  70. package/dist/templates/omp/agents/trellis-implement.md +45 -0
  71. package/dist/templates/omp/agents/trellis-research.md +30 -0
  72. package/dist/templates/omp/extensions/trellis/index.ts.txt +462 -0
  73. package/dist/templates/omp/index.d.ts +4 -0
  74. package/dist/templates/omp/index.d.ts.map +1 -0
  75. package/dist/templates/omp/index.js +9 -0
  76. package/dist/templates/omp/index.js.map +1 -0
  77. package/dist/templates/pi/agents/trellis-check.md +1 -1
  78. package/dist/templates/pi/agents/trellis-implement.md +1 -1
  79. package/dist/templates/pi/agents/trellis-research.md +1 -1
  80. package/dist/templates/pi/extensions/trellis/index.ts.txt +111 -9
  81. package/dist/templates/shared-hooks/index.d.ts +1 -1
  82. package/dist/templates/shared-hooks/index.d.ts.map +1 -1
  83. package/dist/templates/shared-hooks/index.js +1 -0
  84. package/dist/templates/shared-hooks/index.js.map +1 -1
  85. package/dist/templates/shared-hooks/inject-workflow-state.py +41 -6
  86. package/dist/templates/shared-hooks/session-start.py +12 -7
  87. package/dist/templates/trae/agents/trellis-check.md +108 -0
  88. package/dist/templates/trae/agents/trellis-implement.md +103 -0
  89. package/dist/templates/trae/agents/trellis-research.md +137 -0
  90. package/dist/templates/trae/hooks.json +27 -0
  91. package/dist/templates/trae/index.d.ts +15 -0
  92. package/dist/templates/trae/index.d.ts.map +1 -0
  93. package/dist/templates/trae/index.js +15 -0
  94. package/dist/templates/trae/index.js.map +1 -0
  95. package/dist/templates/trellis/scripts/add_session.py +65 -17
  96. package/dist/templates/trellis/scripts/common/active_task.py +2 -0
  97. package/dist/templates/trellis/scripts/common/cli_adapter.py +72 -11
  98. package/dist/templates/trellis/scripts/common/config.py +29 -0
  99. package/dist/templates/trellis/scripts/common/session_context.py +1 -1
  100. package/dist/templates/trellis/scripts/common/task_store.py +79 -19
  101. package/dist/templates/trellis/scripts/task.py +8 -2
  102. package/dist/templates/trellis/workflow.md +22 -20
  103. package/dist/templates/zcode/agents/trellis-research.md +133 -0
  104. package/dist/templates/zcode/index.d.ts +6 -5
  105. package/dist/templates/zcode/index.d.ts.map +1 -1
  106. package/dist/templates/zcode/index.js +6 -5
  107. package/dist/templates/zcode/index.js.map +1 -1
  108. package/dist/types/ai-tools.d.ts +4 -4
  109. package/dist/types/ai-tools.d.ts.map +1 -1
  110. package/dist/types/ai-tools.js +45 -2
  111. package/dist/types/ai-tools.js.map +1 -1
  112. package/dist/utils/uninstall-scrubbers.d.ts +1 -0
  113. package/dist/utils/uninstall-scrubbers.d.ts.map +1 -1
  114. package/dist/utils/uninstall-scrubbers.js +19 -0
  115. package/dist/utils/uninstall-scrubbers.js.map +1 -1
  116. package/package.json +2 -2
@@ -0,0 +1,137 @@
1
+ ---
2
+ name: trellis-research
3
+ description: |
4
+ Code and tech search expert. Finds files, patterns, and tech solutions, and PERSISTS every finding to the current task's research/ directory. No code modifications outside that directory.
5
+ tools: Read, Write, Glob, Grep, Bash, Skill, mcp__*
6
+ ---
7
+ # Research Agent
8
+
9
+ You are the Research Agent in the Trellis workflow.
10
+
11
+ ## Core Principle
12
+
13
+ **You do one thing: find, explain, and PERSIST information.**
14
+
15
+ Conversations get compacted; files don't. Every research output MUST end up as a file under `{TASK_DIR}/research/`. Returning findings only through the chat reply is a failure — the caller cannot read them next session.
16
+
17
+ ---
18
+
19
+ ## Core Responsibilities
20
+
21
+ 1. **Internal Search** — locate files/components, understand code logic, discover patterns (Glob, Grep, Read)
22
+ 2. **External Search** — library docs, API references, best practices (web search)
23
+ 3. **Persist** — write each research topic to `{TASK_DIR}/research/<topic>.md`
24
+ 4. **Report** — return file paths + one-line summaries to the main agent (not full content)
25
+
26
+ ---
27
+
28
+ ## Workflow
29
+
30
+ ### Step 1: Resolve Current Task
31
+
32
+ Run `python3 ./.trellis/scripts/task.py current --source` → active task path. If no active task is set, ask the user where to write output; do NOT guess.
33
+
34
+ Ensure `{TASK_DIR}/research/` exists:
35
+
36
+ ```bash
37
+ mkdir -p <TASK_DIR>/research
38
+ ```
39
+
40
+ ### Step 2: Understand Search Request
41
+
42
+ Classify: internal / external / mixed. Determine scope (global / specific directory) and expected shape (file list / pattern notes / tech comparison).
43
+
44
+ ### Step 3: Execute Search
45
+
46
+ Run independent searches in parallel (Glob + Grep + web) for efficiency.
47
+
48
+ ### Step 4: Persist Each Topic
49
+
50
+ For each distinct research topic, Write a markdown file at `{TASK_DIR}/research/<topic-slug>.md`. Use the File Format below.
51
+
52
+ ### Step 5: Report to Main Agent
53
+
54
+ Reply with ONLY:
55
+
56
+ - List of files written (paths relative to repo root)
57
+ - One-line summary per file
58
+ - Any critical caveats that the main agent needs to know right now
59
+
60
+ Do NOT paste full research content into the reply. The files are the contract.
61
+
62
+ ---
63
+
64
+ ## Scope Limits (Strict)
65
+
66
+ ### Write ALLOWED
67
+
68
+ - `{TASK_DIR}/research/*.md` — your own output
69
+ - Creating `{TASK_DIR}/research/` if it doesn't exist (via `mkdir -p`)
70
+
71
+ ### Write FORBIDDEN
72
+
73
+ - Code files (`src/`, `lib/`, …)
74
+ - Spec files (`.trellis/spec/`) — main agent should use `update-spec` skill instead
75
+ - `.trellis/scripts/`, `.trellis/workflow.md`, platform config (`.claude/`, `.cursor/`, `.trae/`, etc.)
76
+ - Other task directories
77
+ - Any git operation (commit / push / branch / merge)
78
+
79
+ If the user asks you to edit code, decline and suggest spawning `implement` instead.
80
+
81
+ ---
82
+
83
+ ## File Format
84
+
85
+ Each `{TASK_DIR}/research/<topic>.md` should follow:
86
+
87
+ ```markdown
88
+ # Research: <topic>
89
+
90
+ - **Query**: <original query>
91
+ - **Scope**: <internal / external / mixed>
92
+ - **Date**: <YYYY-MM-DD>
93
+
94
+ ## Findings
95
+
96
+ ### Files Found
97
+
98
+ | File Path | Description |
99
+ |---|---|
100
+ | `src/services/xxx.ts` | Main implementation |
101
+ | `src/types/xxx.ts` | Type definitions |
102
+
103
+ ### Code Patterns
104
+
105
+ <describe patterns, cite file:line>
106
+
107
+ ### External References
108
+
109
+ - [Library X docs](url) — <why relevant, version constraints>
110
+
111
+ ### Related Specs
112
+
113
+ - `.trellis/spec/xxx.md` — <description>
114
+
115
+ ## Caveats / Not Found
116
+
117
+ <anything incomplete or uncertain>
118
+ ```
119
+
120
+ ---
121
+
122
+ ## Guidelines
123
+
124
+ ### DO
125
+
126
+ - Provide specific file paths and line numbers
127
+ - Quote actual code snippets
128
+ - Persist every topic to its own file
129
+ - Return file paths in your reply, not the full content
130
+ - Mark "not found" explicitly when searches come up empty
131
+
132
+ ### DON'T
133
+
134
+ - Don't write code or modify files outside `{TASK_DIR}/research/`
135
+ - Don't guess uncertain info
136
+ - Don't paste full research text into the reply (files are the deliverable)
137
+ - Don't propose improvements or critique implementation (that's not your role)
@@ -0,0 +1,27 @@
1
+ {
2
+ "version": 1,
3
+ "hooks": {
4
+ "SessionStart": [
5
+ {
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "{{PYTHON_CMD}} .trae/hooks/session-start.py",
10
+ "timeout": 30
11
+ }
12
+ ]
13
+ }
14
+ ],
15
+ "UserPromptSubmit": [
16
+ {
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "{{PYTHON_CMD}} .trae/hooks/inject-workflow-state.py",
21
+ "timeout": 15
22
+ }
23
+ ]
24
+ }
25
+ ]
26
+ }
27
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Trae IDE templates
3
+ *
4
+ * These are GENERIC templates for user projects.
5
+ *
6
+ * Directory structure:
7
+ * trae/
8
+ * ├── agents/ # Multi-agent pipeline agents (Markdown)
9
+ * └── hooks.json # Hook configuration
10
+ */
11
+ import { type AgentTemplate, type HookTemplate } from "../template-utils.js";
12
+ export type { AgentTemplate, HookTemplate };
13
+ export declare const getAllAgents: () => AgentTemplate[];
14
+ export declare const getSettingsTemplate: () => HookTemplate;
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/trae/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,YAAY,EAClB,MAAM,sBAAsB,CAAC;AAE9B,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,CAAC;AAI5C,eAAO,MAAM,YAAY,QAAO,aAAa,EAAoB,CAAC;AAClE,eAAO,MAAM,mBAAmB,QAAO,YACZ,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Trae IDE templates
3
+ *
4
+ * These are GENERIC templates for user projects.
5
+ *
6
+ * Directory structure:
7
+ * trae/
8
+ * ├── agents/ # Multi-agent pipeline agents (Markdown)
9
+ * └── hooks.json # Hook configuration
10
+ */
11
+ import { createTemplateReader, } from "../template-utils.js";
12
+ const { listMdAgents, getSettings } = createTemplateReader(import.meta.url);
13
+ export const getAllAgents = () => listMdAgents();
14
+ export const getSettingsTemplate = () => getSettings("hooks.json");
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/trae/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,oBAAoB,GAGrB,MAAM,sBAAsB,CAAC;AAI9B,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAE5E,MAAM,CAAC,MAAM,YAAY,GAAG,GAAoB,EAAE,CAAC,YAAY,EAAE,CAAC;AAClE,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAiB,EAAE,CACpD,WAAW,CAAC,YAAY,CAAC,CAAC"}
@@ -14,7 +14,7 @@ Usage:
14
14
 
15
15
  Branch resolution order:
16
16
  1. --branch CLI arg (explicit)
17
- 2. task.json branch field (from active task)
17
+ 2. task.json branch field (from active task, if still exists)
18
18
  3. git branch --show-current (auto-detect)
19
19
  4. None (omitted gracefully)
20
20
  """
@@ -44,6 +44,7 @@ from common.safe_commit import (
44
44
  safe_trellis_paths_to_add,
45
45
  )
46
46
  from common.tasks import load_task
47
+ from common.types import TaskInfo
47
48
  from common.config import (
48
49
  get_packages,
49
50
  get_session_auto_commit,
@@ -55,6 +56,12 @@ from common.config import (
55
56
  )
56
57
 
57
58
 
59
+ DEFAULT_MAIN_CHANGES = (
60
+ "- Detailed change bullets were not supplied; see the summary above."
61
+ )
62
+ DEFAULT_TESTING = "- Validation was not recorded for this session."
63
+
64
+
58
65
  # =============================================================================
59
66
  # Helper Functions
60
67
  # =============================================================================
@@ -126,6 +133,56 @@ def count_journal_files(dev_dir: Path, active_num: int) -> str:
126
133
  return "\n".join(result_lines)
127
134
 
128
135
 
136
+ def get_current_git_branch(repo_root: Path) -> str | None:
137
+ """Return the current checkout branch, or None for detached/non-git states."""
138
+ rc, branch_out, _ = run_git(["branch", "--show-current"], cwd=repo_root)
139
+ if rc != 0:
140
+ return None
141
+ detected = branch_out.strip()
142
+ return detected or None
143
+
144
+
145
+ def branch_ref_exists(repo_root: Path, branch: str) -> bool:
146
+ """Return True when branch exists locally or as the local origin ref."""
147
+ for ref in (f"refs/heads/{branch}", f"refs/remotes/origin/{branch}"):
148
+ rc, _, _ = run_git(["show-ref", "--verify", "--quiet", ref], cwd=repo_root)
149
+ if rc == 0:
150
+ return True
151
+ return False
152
+
153
+
154
+ def resolve_session_branch(
155
+ repo_root: Path,
156
+ cli_branch: str | None,
157
+ task_data: TaskInfo | None,
158
+ ) -> str | None:
159
+ """Resolve journal branch without trusting stale task.json branch fields."""
160
+ if cli_branch:
161
+ return cli_branch
162
+
163
+ current_branch = get_current_git_branch(repo_root)
164
+ raw_task_branch = task_data.raw.get("branch") if task_data else None
165
+ task_branch = raw_task_branch.strip() if isinstance(raw_task_branch, str) else ""
166
+ if not task_branch:
167
+ return current_branch
168
+
169
+ if branch_ref_exists(repo_root, task_branch):
170
+ return task_branch
171
+
172
+ if current_branch:
173
+ print(
174
+ f"Warning: task.json branch '{task_branch}' no longer exists locally or as origin/{task_branch}; using current branch '{current_branch}'.",
175
+ file=sys.stderr,
176
+ )
177
+ return current_branch
178
+
179
+ print(
180
+ f"Warning: task.json branch '{task_branch}' no longer exists locally or as origin/{task_branch}; omitting branch.",
181
+ file=sys.stderr,
182
+ )
183
+ return None
184
+
185
+
129
186
  def create_new_journal_file(
130
187
  dev_dir: Path, num: int, developer: str, today: str, max_lines: int = 2000,
131
188
  ) -> Path:
@@ -154,6 +211,7 @@ def generate_session_content(
154
211
  today: str,
155
212
  package: str | None = None,
156
213
  branch: str | None = None,
214
+ testing_content: str = DEFAULT_TESTING,
157
215
  ) -> str:
158
216
  """Generate session content."""
159
217
  if commit and commit != "-":
@@ -189,7 +247,7 @@ def generate_session_content(
189
247
 
190
248
  ### Testing
191
249
 
192
- - [OK] (Add test results)
250
+ {testing_content}
193
251
 
194
252
  ### Status
195
253
 
@@ -395,8 +453,8 @@ def _auto_commit_workspace(repo_root: Path) -> None:
395
453
  def add_session(
396
454
  title: str,
397
455
  commit: str = "-",
398
- summary: str = "(Add summary)",
399
- extra_content: str = "(Add details)",
456
+ summary: str = "Session summary was not supplied.",
457
+ extra_content: str = DEFAULT_MAIN_CHANGES,
400
458
  auto_commit: bool = True,
401
459
  package: str | None = None,
402
460
  branch: str | None = None,
@@ -503,7 +561,7 @@ def main() -> int:
503
561
  )
504
562
  parser.add_argument("--title", required=True, help="Session title")
505
563
  parser.add_argument("--commit", default="-", help="Comma-separated commit hashes")
506
- parser.add_argument("--summary", default="(Add summary)", help="Brief summary")
564
+ parser.add_argument("--summary", default="Session summary was not supplied.", help="Brief summary")
507
565
  parser.add_argument("--content-file", help="Path to file with detailed content")
508
566
  parser.add_argument("--package", help="Package name tag (e.g., cli, docs-site)")
509
567
  parser.add_argument("--branch", help="Branch name (auto-detected if omitted)")
@@ -514,7 +572,7 @@ def main() -> int:
514
572
 
515
573
  args = parser.parse_args()
516
574
 
517
- extra_content = "(Add details)"
575
+ extra_content = DEFAULT_MAIN_CHANGES
518
576
  if args.content_file:
519
577
  content_path = Path(args.content_file)
520
578
  if content_path.is_file():
@@ -543,17 +601,7 @@ def main() -> int:
543
601
  task_package = task_data.package if task_data else None
544
602
  package = resolve_package(task_package, repo_root)
545
603
 
546
- # Resolve branch: CLI task.json → git auto-detect → None
547
- branch = args.branch
548
-
549
- if not branch:
550
- if task_data and task_data.raw.get("branch"):
551
- branch = task_data.raw["branch"]
552
- else:
553
- _, branch_out, _ = run_git(["branch", "--show-current"], cwd=repo_root)
554
- detected = branch_out.strip()
555
- if detected:
556
- branch = detected
604
+ branch = resolve_session_branch(repo_root, args.branch, task_data)
557
605
 
558
606
  return add_session(
559
607
  args.title, args.commit, args.summary, extra_content,
@@ -43,6 +43,7 @@ _KNOWN_PLATFORMS = {
43
43
  "kiro",
44
44
  "copilot",
45
45
  "pi",
46
+ "trae",
46
47
  }
47
48
 
48
49
  _ENV_SESSION_KEYS: tuple[tuple[str, tuple[str, ...]], ...] = (
@@ -57,6 +58,7 @@ _ENV_SESSION_KEYS: tuple[tuple[str, tuple[str, ...]], ...] = (
57
58
  ("kiro", ("KIRO_SESSION_ID",)),
58
59
  ("copilot", ("COPILOT_SESSION_ID", "COPILOT_SESSIONID")),
59
60
  ("pi", ("PI_SESSION_ID", "PI_SESSIONID")),
61
+ ("trae", ("TRAE_SESSION_ID",)),
60
62
  )
61
63
  _ENV_CONVERSATION_KEYS: tuple[tuple[str, tuple[str, ...]], ...] = (
62
64
  ("cursor", ("CURSOR_CONVERSATION_ID", "CURSOR_CONVERSATIONID")),
@@ -19,6 +19,7 @@ Supported platforms:
19
19
  - copilot: GitHub Copilot (VS Code)
20
20
  - droid: Factory Droid (commands-based)
21
21
  - pi: Pi Agent (extension-backed)
22
+ - trae: Trae IDE (IDE-only, hooks-based)
22
23
 
23
24
  Usage:
24
25
  from common.cli_adapter import CLIAdapter
@@ -53,6 +54,8 @@ Platform = Literal[
53
54
  "copilot",
54
55
  "droid",
55
56
  "pi",
57
+ "trae",
58
+ "omp",
56
59
  ]
57
60
 
58
61
 
@@ -97,7 +100,7 @@ class CLIAdapter:
97
100
  """Get platform-specific config directory name.
98
101
 
99
102
  Returns:
100
- Directory name ('.claude', '.opencode', '.cursor', '.iflow', '.codex', '.kilocode', '.kiro', '.gemini', '.agent', '.devin', '.qoder', '.codebuddy', '.github/copilot', '.factory', or '.pi')
103
+ Directory name ('.claude', '.opencode', '.cursor', '.iflow', '.codex', '.kilocode', '.kiro', '.gemini', '.agent', '.devin', '.qoder', '.codebuddy', '.github/copilot', '.factory', '.pi', or '.trae')
101
104
  """
102
105
  if self.platform == "opencode":
103
106
  return ".opencode"
@@ -127,6 +130,10 @@ class CLIAdapter:
127
130
  return ".factory"
128
131
  elif self.platform == "pi":
129
132
  return ".pi"
133
+ elif self.platform == "trae":
134
+ return ".trae"
135
+ elif self.platform == "omp":
136
+ return ".omp"
130
137
  else:
131
138
  return ".claude"
132
139
 
@@ -137,7 +144,7 @@ class CLIAdapter:
137
144
  project_root: Project root directory
138
145
 
139
146
  Returns:
140
- Path to config directory (.claude, .opencode, .cursor, .iflow, .codex, .kilocode, .kiro, .gemini, .agent, .devin, .qoder, .codebuddy, .github/copilot, .factory, or .pi)
147
+ Path to config directory (.claude, .opencode, .cursor, .iflow, .codex, .kilocode, .kiro, .gemini, .agent, .devin, .qoder, .codebuddy, .github/copilot, .factory, .pi, or .trae)
141
148
  """
142
149
  return project_root / self.config_dir_name
143
150
 
@@ -184,6 +191,17 @@ class CLIAdapter:
184
191
  filename = filename[:-3]
185
192
  return prompts_dir / f"trellis-{filename}.md"
186
193
  return prompts_dir / Path(*parts)
194
+ if self.platform == "omp":
195
+ commands_dir = self.get_config_dir(project_root) / "commands"
196
+ if not parts:
197
+ return commands_dir
198
+ if len(parts) >= 2 and parts[0] == "trellis":
199
+ filename = parts[-1]
200
+ if filename.endswith(".md"):
201
+ filename = filename[:-3]
202
+ return commands_dir / f"trellis-{filename}.md"
203
+ return commands_dir / Path(*parts)
204
+
187
205
 
188
206
  if self.platform == "devin":
189
207
  workflow_dir = self.get_config_dir(project_root) / "workflows"
@@ -268,6 +286,8 @@ class CLIAdapter:
268
286
  return f".factory/commands/trellis/{name}.md"
269
287
  elif self.platform == "pi":
270
288
  return f".pi/prompts/trellis-{name}.md"
289
+ elif self.platform == "omp":
290
+ return f".omp/commands/trellis-{name}.md"
271
291
  else:
272
292
  return f"{self.config_dir_name}/commands/trellis/{name}.md"
273
293
 
@@ -305,6 +325,10 @@ class CLIAdapter:
305
325
  return {}
306
326
  elif self.platform == "pi":
307
327
  return {}
328
+ elif self.platform == "trae":
329
+ return {}
330
+ elif self.platform == "omp":
331
+ return {}
308
332
  else:
309
333
  return {"CLAUDE_NON_INTERACTIVE": "1"}
310
334
 
@@ -390,6 +414,14 @@ class CLIAdapter:
390
414
  )
391
415
  elif self.platform == "pi":
392
416
  cmd = ["pi", "-p", prompt]
417
+ elif self.platform == "trae":
418
+ raise ValueError(
419
+ "Trae is IDE-only; CLI agent run is not supported."
420
+ )
421
+ elif self.platform == "omp":
422
+ raise ValueError(
423
+ "OMP uses native task tool for agent runs; CLI agent run is not supported."
424
+ )
393
425
 
394
426
  else: # claude
395
427
  cmd = ["claude", "-p"]
@@ -456,6 +488,14 @@ class CLIAdapter:
456
488
  )
457
489
  elif self.platform == "pi":
458
490
  return ["pi", "-c", session_id]
491
+ elif self.platform == "trae":
492
+ raise ValueError(
493
+ "Trae is IDE-only; CLI resume is not supported."
494
+ )
495
+ elif self.platform == "omp":
496
+ raise ValueError(
497
+ "OMP uses native task tool for agent runs; CLI resume is not supported."
498
+ )
459
499
  else:
460
500
  return ["claude", "--resume", session_id]
461
501
 
@@ -530,6 +570,10 @@ class CLIAdapter:
530
570
  return "droid"
531
571
  elif self.platform == "pi":
532
572
  return "pi"
573
+ elif self.platform == "trae":
574
+ return "trae"
575
+ elif self.platform == "omp":
576
+ return "omp"
533
577
  else:
534
578
  return "claude"
535
579
 
@@ -594,7 +638,7 @@ def get_cli_adapter(platform: str = "claude") -> CLIAdapter:
594
638
  """Get CLI adapter for the specified platform.
595
639
 
596
640
  Args:
597
- platform: Platform name ('claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', 'devin', 'qoder', 'codebuddy', 'copilot', 'droid', or 'pi')
641
+ platform: Platform name ('claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', 'devin', 'qoder', 'codebuddy', 'copilot', 'droid', 'pi', or 'trae')
598
642
 
599
643
  Returns:
600
644
  CLIAdapter instance
@@ -625,9 +669,11 @@ def get_cli_adapter(platform: str = "claude") -> CLIAdapter:
625
669
  "copilot",
626
670
  "droid",
627
671
  "pi",
672
+ "trae",
673
+ "omp",
628
674
  ):
629
675
  raise ValueError(
630
- f"Unsupported platform: {platform} (must be 'claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', 'devin', 'qoder', 'codebuddy', 'copilot', 'droid', or 'pi')"
676
+ f"Unsupported platform: {platform} (must be 'claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', 'devin', 'qoder', 'codebuddy', 'copilot', 'droid', 'pi', 'trae', or 'omp')"
631
677
  )
632
678
 
633
679
  return CLIAdapter(platform=platform) # type: ignore
@@ -650,6 +696,8 @@ _ALL_PLATFORM_CONFIG_DIRS = (
650
696
  ".github/copilot",
651
697
  ".factory",
652
698
  ".pi",
699
+ ".trae",
700
+ ".omp",
653
701
  )
654
702
  """Platform-specific config directory names used by detect_platform exclusion
655
703
  checks. `.agents/skills/` is NOT listed here: it is a shared cross-platform
@@ -675,22 +723,25 @@ def detect_platform(project_root: Path) -> Platform:
675
723
  2. .opencode directory exists → opencode
676
724
  3. .iflow directory exists → iflow
677
725
  4. .cursor directory exists (without .claude) → cursor
678
- 5. .codex exists and no other platform dirs codex
679
- 6. .kilocode directory exists → kilo
680
- 7. .kiro/skills exists and no other platform dirs kiro
681
- 8. .gemini directory exists → gemini
726
+ 5. .gemini directory exists → gemini
727
+ 6. .codex exists and no other platform dirs codex
728
+ 7. .kilocode directory exists → kilo
729
+ 8. .kiro/skills exists and no other platform dirs kiro
682
730
  9. .agent/workflows exists and no other platform dirs → antigravity
683
731
  10. .devin/workflows (or legacy .windsurf/workflows) exists and no other platform dirs → devin
684
732
  11. .codebuddy directory exists → codebuddy
685
733
  12. .qoder directory exists → qoder
686
- 13. .pi directory exists → pi
687
- 14. Defaultclaude
734
+ 13. .github/copilot directory exists → copilot
735
+ 14. .factory directory exists droid
736
+ 15. .pi directory exists → pi
737
+ 16. .trae directory exists → trae
738
+ 17. Default → claude
688
739
 
689
740
  Args:
690
741
  project_root: Project root directory
691
742
 
692
743
  Returns:
693
- Detected platform ('claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', 'devin', 'qoder', 'codebuddy', 'copilot', 'droid', 'pi', or default 'claude')
744
+ Detected platform ('claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', 'devin', 'qoder', 'codebuddy', 'copilot', 'droid', 'pi', 'trae', or default 'claude')
694
745
  """
695
746
  import os
696
747
 
@@ -715,6 +766,8 @@ def detect_platform(project_root: Path) -> Platform:
715
766
  "copilot",
716
767
  "droid",
717
768
  "pi",
769
+ "trae",
770
+ "omp",
718
771
  ):
719
772
  return env_platform # type: ignore
720
773
 
@@ -791,6 +844,14 @@ def detect_platform(project_root: Path) -> Platform:
791
844
  if (project_root / ".pi").is_dir():
792
845
  return "pi"
793
846
 
847
+ # Check for .trae directory (Trae IDE-specific)
848
+ if (project_root / ".trae").is_dir():
849
+ return "trae"
850
+
851
+ # Check for .omp directory (OMP-specific)
852
+ if (project_root / ".omp").is_dir():
853
+ return "omp"
854
+
794
855
  # Fallback: checkout only has the Codex shared-skills layer
795
856
  # (.agents/skills/trellis-* dirs) and no explicit platform config dir.
796
857
  # Happens on fresh clones where .codex/ is gitignored/absent but the
@@ -167,6 +167,7 @@ def _next_content_line(lines: list[str], start: int) -> tuple[int, str]:
167
167
  DEFAULT_SESSION_COMMIT_MESSAGE = "chore: record journal"
168
168
  DEFAULT_MAX_JOURNAL_LINES = 2000
169
169
  DEFAULT_SESSION_AUTO_COMMIT = True
170
+ DEFAULT_CODEX_DISPATCH_MODE = "inline"
170
171
 
171
172
  CONFIG_FILE = "config.yaml"
172
173
 
@@ -243,6 +244,34 @@ def get_session_auto_commit(repo_root: Path | None = None) -> bool:
243
244
  return DEFAULT_SESSION_AUTO_COMMIT
244
245
 
245
246
 
247
+ def get_codex_dispatch_mode(repo_root: Path | None = None) -> str:
248
+ """Return Codex dispatch mode.
249
+
250
+ Default is ``inline``. ``sub-agent`` is an explicit opt-in because Codex
251
+ sub-agents do not inherit the parent session context.
252
+ """
253
+ config = _load_config(repo_root)
254
+ codex = config.get("codex")
255
+ if codex is None:
256
+ return DEFAULT_CODEX_DISPATCH_MODE
257
+ if not isinstance(codex, dict):
258
+ print(
259
+ f"[WARN] invalid codex config: {codex!r}; using {DEFAULT_CODEX_DISPATCH_MODE}",
260
+ file=sys.stderr,
261
+ )
262
+ return DEFAULT_CODEX_DISPATCH_MODE
263
+
264
+ raw = codex.get("dispatch_mode", DEFAULT_CODEX_DISPATCH_MODE)
265
+ mode = str(raw).strip().lower()
266
+ if mode in ("inline", "sub-agent"):
267
+ return mode
268
+ print(
269
+ f"[WARN] invalid codex.dispatch_mode value: {raw!r}; using {DEFAULT_CODEX_DISPATCH_MODE}",
270
+ file=sys.stderr,
271
+ )
272
+ return DEFAULT_CODEX_DISPATCH_MODE
273
+
274
+
246
275
  def get_hooks(event: str, repo_root: Path | None = None) -> list[str]:
247
276
  """Get hook commands for a lifecycle event.
248
277
 
@@ -412,7 +412,7 @@ def _get_update_hint(repo_root: Path) -> str | None:
412
412
 
413
413
  return (
414
414
  f"Trellis update available: {current_version} -> {latest_version}, "
415
- "run trellis upgrade"
415
+ "run trellis update"
416
416
  )
417
417
 
418
418