@jahanxu/trellis 0.5.8 → 0.6.0
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 +74 -130
- 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 +30 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +11 -39
- package/dist/commands/update.js.map +1 -1
- package/dist/configurators/index.d.ts.map +1 -1
- package/dist/configurators/index.js +13 -1
- package/dist/configurators/index.js.map +1 -1
- package/dist/configurators/qoder.d.ts +8 -0
- package/dist/configurators/qoder.d.ts.map +1 -0
- package/dist/configurators/qoder.js +52 -0
- package/dist/configurators/qoder.js.map +1 -0
- package/dist/configurators/workflow.d.ts.map +1 -1
- package/dist/configurators/workflow.js +3 -1
- package/dist/configurators/workflow.js.map +1 -1
- package/dist/migrations/manifests/0.3.0-beta.0.json +60 -41
- package/dist/migrations/manifests/0.3.0.json +2 -2
- package/dist/migrations/manifests/0.3.1.json +9 -0
- package/dist/migrations/manifests/0.3.2.json +9 -0
- package/dist/migrations/manifests/0.3.3.json +9 -0
- package/dist/templates/claude/commands/trellis/record-session.md +12 -16
- package/dist/templates/claude/settings.json +20 -0
- package/dist/templates/codex/skills/record-session/SKILL.md +13 -17
- package/dist/templates/cursor/commands/trellis-record-session.md +12 -16
- package/dist/templates/extract.d.ts +7 -0
- package/dist/templates/extract.d.ts.map +1 -1
- package/dist/templates/extract.js +13 -0
- package/dist/templates/extract.js.map +1 -1
- package/dist/templates/gemini/commands/trellis/record-session.toml +12 -16
- package/dist/templates/iflow/commands/trellis/record-session.md +12 -16
- package/dist/templates/iflow/hooks/session-start.py +1 -0
- package/dist/templates/iflow/settings.json +20 -0
- package/dist/templates/kilo/commands/trellis/record-session.md +12 -16
- package/dist/templates/kiro/skills/record-session/SKILL.md +13 -17
- package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md.txt +13 -0
- package/dist/templates/opencode/commands/trellis/record-session.md +12 -16
- package/dist/templates/qoder/index.d.ts +18 -0
- package/dist/templates/qoder/index.d.ts.map +1 -0
- package/dist/templates/qoder/index.js +40 -0
- package/dist/templates/qoder/index.js.map +1 -0
- package/dist/templates/qoder/skills/before-backend-dev/SKILL.md +18 -0
- package/dist/templates/qoder/skills/before-frontend-dev/SKILL.md +18 -0
- package/dist/templates/qoder/skills/brainstorm/SKILL.md +479 -0
- package/dist/templates/qoder/skills/break-loop/SKILL.md +130 -0
- package/dist/templates/qoder/skills/check-backend/SKILL.md +18 -0
- package/dist/templates/qoder/skills/check-cross-layer/SKILL.md +158 -0
- package/dist/templates/qoder/skills/check-frontend/SKILL.md +18 -0
- package/dist/templates/qoder/skills/create-command/SKILL.md +101 -0
- package/dist/templates/qoder/skills/finish-work/SKILL.md +134 -0
- package/dist/templates/qoder/skills/integrate-skill/SKILL.md +221 -0
- package/dist/templates/qoder/skills/onboard/SKILL.md +363 -0
- package/dist/templates/qoder/skills/record-session/SKILL.md +63 -0
- package/dist/templates/qoder/skills/start/SKILL.md +326 -0
- package/dist/templates/qoder/skills/update-spec/SKILL.md +290 -0
- package/dist/templates/trellis/config.yaml +15 -0
- package/dist/templates/trellis/index.d.ts +3 -0
- package/dist/templates/trellis/index.d.ts.map +1 -1
- package/dist/templates/trellis/index.js +4 -0
- package/dist/templates/trellis/index.js.map +1 -1
- package/dist/templates/trellis/scripts/add_session.py +52 -21
- package/dist/templates/trellis/scripts/common/__init__.py +3 -1
- package/dist/templates/trellis/scripts/common/cli_adapter.py +122 -19
- package/dist/templates/trellis/scripts/common/config.py +52 -0
- package/dist/templates/trellis/scripts/common/git_context.py +121 -11
- package/dist/templates/trellis/scripts/multi_agent/plan.py +4 -1
- package/dist/templates/trellis/scripts/multi_agent/start.py +5 -1
- package/dist/templates/trellis/scripts/task.py +26 -0
- 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 +8 -0
- package/dist/types/ai-tools.js.map +1 -1
- package/dist/utils/proxy.d.ts +25 -0
- package/dist/utils/proxy.d.ts.map +1 -0
- package/dist/utils/proxy.js +60 -0
- package/dist/utils/proxy.js.map +1 -0
- package/dist/utils/template-fetcher.d.ts +11 -2
- package/dist/utils/template-fetcher.d.ts.map +1 -1
- package/dist/utils/template-fetcher.js +92 -19
- package/dist/utils/template-fetcher.js.map +1 -1
- package/package.json +4 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/trellis/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/trellis/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,19 +10,9 @@ Usage:
|
|
|
10
10
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
|
-
import sys
|
|
14
|
-
|
|
15
|
-
# IMPORTANT: Force stdout to use UTF-8 on Windows
|
|
16
|
-
# This fixes UnicodeEncodeError when outputting non-ASCII characters
|
|
17
|
-
if sys.platform == "win32":
|
|
18
|
-
import io as _io
|
|
19
|
-
if hasattr(sys.stdout, "reconfigure"):
|
|
20
|
-
sys.stdout.reconfigure(encoding="utf-8", errors="replace") # type: ignore[union-attr]
|
|
21
|
-
elif hasattr(sys.stdout, "detach"):
|
|
22
|
-
sys.stdout = _io.TextIOWrapper(sys.stdout.detach(), encoding="utf-8", errors="replace") # type: ignore[union-attr]
|
|
23
|
-
|
|
24
13
|
import argparse
|
|
25
14
|
import re
|
|
15
|
+
import subprocess
|
|
26
16
|
import sys
|
|
27
17
|
from datetime import datetime
|
|
28
18
|
from pathlib import Path
|
|
@@ -34,9 +24,7 @@ from common.paths import (
|
|
|
34
24
|
get_workspace_dir,
|
|
35
25
|
)
|
|
36
26
|
from common.developer import ensure_developer
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
MAX_LINES = 2000
|
|
27
|
+
from common.config import get_session_commit_message, get_max_journal_lines
|
|
40
28
|
|
|
41
29
|
|
|
42
30
|
# =============================================================================
|
|
@@ -110,14 +98,16 @@ def count_journal_files(dev_dir: Path, active_num: int) -> str:
|
|
|
110
98
|
return "\n".join(result_lines)
|
|
111
99
|
|
|
112
100
|
|
|
113
|
-
def create_new_journal_file(
|
|
101
|
+
def create_new_journal_file(
|
|
102
|
+
dev_dir: Path, num: int, developer: str, today: str, max_lines: int = 2000,
|
|
103
|
+
) -> Path:
|
|
114
104
|
"""Create a new journal file."""
|
|
115
105
|
prev_num = num - 1
|
|
116
106
|
new_file = dev_dir / f"{FILE_JOURNAL_PREFIX}{num}.md"
|
|
117
107
|
|
|
118
108
|
content = f"""# Journal - {developer} (Part {num})
|
|
119
109
|
|
|
120
|
-
> Continuation from `{FILE_JOURNAL_PREFIX}{prev_num}.md` (archived at ~{
|
|
110
|
+
> Continuation from `{FILE_JOURNAL_PREFIX}{prev_num}.md` (archived at ~{max_lines} lines)
|
|
121
111
|
> Started: {today}
|
|
122
112
|
|
|
123
113
|
---
|
|
@@ -281,11 +271,40 @@ def update_index(
|
|
|
281
271
|
# Main Function
|
|
282
272
|
# =============================================================================
|
|
283
273
|
|
|
274
|
+
def _auto_commit_workspace(repo_root: Path) -> None:
|
|
275
|
+
"""Stage .trellis/workspace and .trellis/tasks, then commit with a configured message."""
|
|
276
|
+
commit_msg = get_session_commit_message(repo_root)
|
|
277
|
+
subprocess.run(
|
|
278
|
+
["git", "add", "-A", ".trellis/workspace", ".trellis/tasks"],
|
|
279
|
+
cwd=repo_root,
|
|
280
|
+
capture_output=True,
|
|
281
|
+
)
|
|
282
|
+
# Check if there are staged changes
|
|
283
|
+
result = subprocess.run(
|
|
284
|
+
["git", "diff", "--cached", "--quiet", "--", ".trellis/workspace", ".trellis/tasks"],
|
|
285
|
+
cwd=repo_root,
|
|
286
|
+
)
|
|
287
|
+
if result.returncode == 0:
|
|
288
|
+
print("[OK] No workspace changes to commit.", file=sys.stderr)
|
|
289
|
+
return
|
|
290
|
+
commit_result = subprocess.run(
|
|
291
|
+
["git", "commit", "-m", commit_msg],
|
|
292
|
+
cwd=repo_root,
|
|
293
|
+
capture_output=True,
|
|
294
|
+
text=True,
|
|
295
|
+
)
|
|
296
|
+
if commit_result.returncode == 0:
|
|
297
|
+
print(f"[OK] Auto-committed: {commit_msg}", file=sys.stderr)
|
|
298
|
+
else:
|
|
299
|
+
print(f"[WARN] Auto-commit failed: {commit_result.stderr.strip()}", file=sys.stderr)
|
|
300
|
+
|
|
301
|
+
|
|
284
302
|
def add_session(
|
|
285
303
|
title: str,
|
|
286
304
|
commit: str = "-",
|
|
287
305
|
summary: str = "(Add summary)",
|
|
288
|
-
extra_content: str = "(Add details)"
|
|
306
|
+
extra_content: str = "(Add details)",
|
|
307
|
+
auto_commit: bool = True,
|
|
289
308
|
) -> int:
|
|
290
309
|
"""Add a new session."""
|
|
291
310
|
repo_root = get_repo_root()
|
|
@@ -301,6 +320,8 @@ def add_session(
|
|
|
301
320
|
print("Error: Workspace directory not found", file=sys.stderr)
|
|
302
321
|
return 1
|
|
303
322
|
|
|
323
|
+
max_lines = get_max_journal_lines(repo_root)
|
|
324
|
+
|
|
304
325
|
index_file = dev_dir / "index.md"
|
|
305
326
|
today = datetime.now().strftime("%Y-%m-%d")
|
|
306
327
|
|
|
@@ -330,10 +351,10 @@ def add_session(
|
|
|
330
351
|
target_file = journal_file
|
|
331
352
|
target_num = current_num
|
|
332
353
|
|
|
333
|
-
if current_lines + content_lines >
|
|
354
|
+
if current_lines + content_lines > max_lines:
|
|
334
355
|
target_num = current_num + 1
|
|
335
|
-
print(f"[!] Exceeds {
|
|
336
|
-
target_file = create_new_journal_file(dev_dir, target_num, developer, today)
|
|
356
|
+
print(f"[!] Exceeds {max_lines} lines, creating {FILE_JOURNAL_PREFIX}{target_num}.md", file=sys.stderr)
|
|
357
|
+
target_file = create_new_journal_file(dev_dir, target_num, developer, today, max_lines)
|
|
337
358
|
print(f"Created: {target_file}", file=sys.stderr)
|
|
338
359
|
|
|
339
360
|
# Append session content
|
|
@@ -358,6 +379,11 @@ def add_session(
|
|
|
358
379
|
print(f" - {target_file.name if target_file else 'journal'}", file=sys.stderr)
|
|
359
380
|
print(" - index.md", file=sys.stderr)
|
|
360
381
|
|
|
382
|
+
# Auto-commit workspace changes
|
|
383
|
+
if auto_commit:
|
|
384
|
+
print("", file=sys.stderr)
|
|
385
|
+
_auto_commit_workspace(repo_root)
|
|
386
|
+
|
|
361
387
|
return 0
|
|
362
388
|
|
|
363
389
|
|
|
@@ -374,6 +400,8 @@ def main() -> int:
|
|
|
374
400
|
parser.add_argument("--commit", default="-", help="Comma-separated commit hashes")
|
|
375
401
|
parser.add_argument("--summary", default="(Add summary)", help="Brief summary")
|
|
376
402
|
parser.add_argument("--content-file", help="Path to file with detailed content")
|
|
403
|
+
parser.add_argument("--no-commit", action="store_true",
|
|
404
|
+
help="Skip auto-commit of workspace changes")
|
|
377
405
|
|
|
378
406
|
args = parser.parse_args()
|
|
379
407
|
|
|
@@ -385,7 +413,10 @@ def main() -> int:
|
|
|
385
413
|
elif not sys.stdin.isatty():
|
|
386
414
|
extra_content = sys.stdin.read()
|
|
387
415
|
|
|
388
|
-
return add_session(
|
|
416
|
+
return add_session(
|
|
417
|
+
args.title, args.commit, args.summary, extra_content,
|
|
418
|
+
auto_commit=not args.no_commit,
|
|
419
|
+
)
|
|
389
420
|
|
|
390
421
|
|
|
391
422
|
if __name__ == "__main__":
|
|
@@ -36,11 +36,12 @@ def _configure_stream(stream: object) -> object:
|
|
|
36
36
|
if sys.platform == "win32":
|
|
37
37
|
sys.stdout = _configure_stream(sys.stdout) # type: ignore[assignment]
|
|
38
38
|
sys.stderr = _configure_stream(sys.stderr) # type: ignore[assignment]
|
|
39
|
+
sys.stdin = _configure_stream(sys.stdin) # type: ignore[assignment]
|
|
39
40
|
|
|
40
41
|
|
|
41
42
|
def configure_encoding() -> None:
|
|
42
43
|
"""
|
|
43
|
-
Configure stdout/stderr for UTF-8 encoding on Windows.
|
|
44
|
+
Configure stdout/stderr/stdin for UTF-8 encoding on Windows.
|
|
44
45
|
|
|
45
46
|
This is automatically called when importing from common,
|
|
46
47
|
but can be called manually for scripts that don't import common.
|
|
@@ -51,6 +52,7 @@ def configure_encoding() -> None:
|
|
|
51
52
|
if sys.platform == "win32":
|
|
52
53
|
sys.stdout = _configure_stream(sys.stdout) # type: ignore[assignment]
|
|
53
54
|
sys.stderr = _configure_stream(sys.stderr) # type: ignore[assignment]
|
|
55
|
+
sys.stdin = _configure_stream(sys.stdin) # type: ignore[assignment]
|
|
54
56
|
|
|
55
57
|
|
|
56
58
|
from .paths import (
|
|
@@ -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, and
|
|
4
|
+
Abstracts differences between Claude Code, OpenCode, Cursor, iFlow, Codex, Kilo, Kiro Code, Gemini CLI, Antigravity, and Qoder interfaces.
|
|
5
5
|
|
|
6
6
|
Supported platforms:
|
|
7
7
|
- claude: Claude Code (default)
|
|
@@ -13,6 +13,7 @@ Supported platforms:
|
|
|
13
13
|
- kiro: Kiro Code (skills-based)
|
|
14
14
|
- gemini: Gemini CLI
|
|
15
15
|
- antigravity: Antigravity (workflow-based)
|
|
16
|
+
- qoder: Qoder
|
|
16
17
|
|
|
17
18
|
Usage:
|
|
18
19
|
from common.cli_adapter import CLIAdapter
|
|
@@ -31,7 +32,18 @@ from dataclasses import dataclass
|
|
|
31
32
|
from pathlib import Path
|
|
32
33
|
from typing import ClassVar, Literal
|
|
33
34
|
|
|
34
|
-
Platform = Literal[
|
|
35
|
+
Platform = Literal[
|
|
36
|
+
"claude",
|
|
37
|
+
"opencode",
|
|
38
|
+
"cursor",
|
|
39
|
+
"iflow",
|
|
40
|
+
"codex",
|
|
41
|
+
"kilo",
|
|
42
|
+
"kiro",
|
|
43
|
+
"gemini",
|
|
44
|
+
"antigravity",
|
|
45
|
+
"qoder",
|
|
46
|
+
]
|
|
35
47
|
|
|
36
48
|
|
|
37
49
|
@dataclass
|
|
@@ -75,7 +87,7 @@ class CLIAdapter:
|
|
|
75
87
|
"""Get platform-specific config directory name.
|
|
76
88
|
|
|
77
89
|
Returns:
|
|
78
|
-
Directory name ('.claude', '.opencode', '.cursor', '.iflow', '.agents', '.kilocode', '.kiro', '.gemini', or '.
|
|
90
|
+
Directory name ('.claude', '.opencode', '.cursor', '.iflow', '.agents', '.kilocode', '.kiro', '.gemini', '.agent', or '.qoder')
|
|
79
91
|
"""
|
|
80
92
|
if self.platform == "opencode":
|
|
81
93
|
return ".opencode"
|
|
@@ -93,6 +105,8 @@ class CLIAdapter:
|
|
|
93
105
|
return ".gemini"
|
|
94
106
|
elif self.platform == "antigravity":
|
|
95
107
|
return ".agent"
|
|
108
|
+
elif self.platform == "qoder":
|
|
109
|
+
return ".qoder"
|
|
96
110
|
else:
|
|
97
111
|
return ".claude"
|
|
98
112
|
|
|
@@ -103,7 +117,7 @@ class CLIAdapter:
|
|
|
103
117
|
project_root: Project root directory
|
|
104
118
|
|
|
105
119
|
Returns:
|
|
106
|
-
Path to config directory (.claude, .opencode, .cursor, .iflow, .agents, .kilocode, .kiro, or .
|
|
120
|
+
Path to config directory (.claude, .opencode, .cursor, .iflow, .agents, .kilocode, .kiro, .gemini, .agent, or .qoder)
|
|
107
121
|
"""
|
|
108
122
|
return project_root / self.config_dir_name
|
|
109
123
|
|
|
@@ -151,7 +165,9 @@ class CLIAdapter:
|
|
|
151
165
|
if self.platform == "cursor" and len(parts) >= 2 and parts[0] == "trellis":
|
|
152
166
|
# Convert trellis/<name>.md to trellis-<name>.md
|
|
153
167
|
filename = parts[-1]
|
|
154
|
-
return
|
|
168
|
+
return (
|
|
169
|
+
self.get_config_dir(project_root) / "commands" / f"trellis-{filename}"
|
|
170
|
+
)
|
|
155
171
|
|
|
156
172
|
return self.get_config_dir(project_root) / "commands" / Path(*parts)
|
|
157
173
|
|
|
@@ -197,6 +213,8 @@ class CLIAdapter:
|
|
|
197
213
|
"""
|
|
198
214
|
if self.platform == "opencode":
|
|
199
215
|
return {"OPENCODE_NON_INTERACTIVE": "1"}
|
|
216
|
+
elif self.platform == "iflow":
|
|
217
|
+
return {"IFLOW_NON_INTERACTIVE": "1"}
|
|
200
218
|
elif self.platform == "codex":
|
|
201
219
|
return {"CODEX_NON_INTERACTIVE": "1"}
|
|
202
220
|
elif self.platform == "kiro":
|
|
@@ -205,6 +223,8 @@ class CLIAdapter:
|
|
|
205
223
|
return {} # Gemini CLI doesn't have a non-interactive env var
|
|
206
224
|
elif self.platform == "antigravity":
|
|
207
225
|
return {}
|
|
226
|
+
elif self.platform == "qoder":
|
|
227
|
+
return {}
|
|
208
228
|
else:
|
|
209
229
|
return {"CLAUDE_NON_INTERACTIVE": "1"}
|
|
210
230
|
|
|
@@ -255,6 +275,13 @@ class CLIAdapter:
|
|
|
255
275
|
|
|
256
276
|
cmd.append(prompt)
|
|
257
277
|
|
|
278
|
+
elif self.platform == "iflow":
|
|
279
|
+
cmd = ["iflow", "-p"]
|
|
280
|
+
cmd.extend(["-y", "--agent", mapped_agent])
|
|
281
|
+
# iFlow doesn't support --session-id on creation
|
|
282
|
+
if verbose:
|
|
283
|
+
cmd.append("--verbose")
|
|
284
|
+
cmd.append(prompt)
|
|
258
285
|
elif self.platform == "codex":
|
|
259
286
|
cmd = ["codex", "exec"]
|
|
260
287
|
cmd.append(prompt)
|
|
@@ -267,6 +294,8 @@ class CLIAdapter:
|
|
|
267
294
|
raise ValueError(
|
|
268
295
|
"Antigravity workflows are UI slash commands; CLI agent run is not supported."
|
|
269
296
|
)
|
|
297
|
+
elif self.platform == "qoder":
|
|
298
|
+
cmd = ["qodercli", "-p", prompt]
|
|
270
299
|
|
|
271
300
|
else: # claude
|
|
272
301
|
cmd = ["claude", "-p"]
|
|
@@ -292,13 +321,17 @@ class CLIAdapter:
|
|
|
292
321
|
"""Build CLI command for resuming a session.
|
|
293
322
|
|
|
294
323
|
Args:
|
|
295
|
-
session_id: Session ID to resume
|
|
324
|
+
session_id: Session ID to resume (ignored for iFlow)
|
|
296
325
|
|
|
297
326
|
Returns:
|
|
298
327
|
List of command arguments
|
|
299
328
|
"""
|
|
300
329
|
if self.platform == "opencode":
|
|
301
330
|
return ["opencode", "run", "--session", session_id]
|
|
331
|
+
elif self.platform == "iflow":
|
|
332
|
+
# iFlow uses -c to continue most recent conversation
|
|
333
|
+
# session_id is ignored as iFlow doesn't support session IDs
|
|
334
|
+
return ["iflow", "-c"]
|
|
302
335
|
elif self.platform == "codex":
|
|
303
336
|
return ["codex", "resume", session_id]
|
|
304
337
|
elif self.platform == "kiro":
|
|
@@ -309,6 +342,8 @@ class CLIAdapter:
|
|
|
309
342
|
raise ValueError(
|
|
310
343
|
"Antigravity workflows are UI slash commands; CLI resume is not supported."
|
|
311
344
|
)
|
|
345
|
+
elif self.platform == "qoder":
|
|
346
|
+
return ["qodercli", "--resume", session_id]
|
|
312
347
|
else:
|
|
313
348
|
return ["claude", "--resume", session_id]
|
|
314
349
|
|
|
@@ -348,6 +383,11 @@ class CLIAdapter:
|
|
|
348
383
|
"""Check if platform is Cursor."""
|
|
349
384
|
return self.platform == "cursor"
|
|
350
385
|
|
|
386
|
+
@property
|
|
387
|
+
def is_iflow(self) -> bool:
|
|
388
|
+
"""Check if platform is iFlow CLI."""
|
|
389
|
+
return self.platform == "iflow"
|
|
390
|
+
|
|
351
391
|
@property
|
|
352
392
|
def cli_name(self) -> str:
|
|
353
393
|
"""Get CLI executable name.
|
|
@@ -358,12 +398,16 @@ class CLIAdapter:
|
|
|
358
398
|
return "opencode"
|
|
359
399
|
elif self.is_cursor:
|
|
360
400
|
return "cursor" # Note: Cursor is IDE-only, no CLI
|
|
401
|
+
elif self.platform == "iflow":
|
|
402
|
+
return "iflow"
|
|
361
403
|
elif self.platform == "kiro":
|
|
362
404
|
return "kiro"
|
|
363
405
|
elif self.platform == "gemini":
|
|
364
406
|
return "gemini"
|
|
365
407
|
elif self.platform == "antigravity":
|
|
366
408
|
return "agy"
|
|
409
|
+
elif self.platform == "qoder":
|
|
410
|
+
return "qodercli"
|
|
367
411
|
else:
|
|
368
412
|
return "claude"
|
|
369
413
|
|
|
@@ -371,10 +415,10 @@ class CLIAdapter:
|
|
|
371
415
|
def supports_cli_agents(self) -> bool:
|
|
372
416
|
"""Check if platform supports running agents via CLI.
|
|
373
417
|
|
|
374
|
-
Claude Code and
|
|
418
|
+
Claude Code, OpenCode, and iFlow support CLI agent execution.
|
|
375
419
|
Cursor is IDE-only and doesn't support CLI agents.
|
|
376
420
|
"""
|
|
377
|
-
return self.platform in ("claude", "opencode")
|
|
421
|
+
return self.platform in ("claude", "opencode", "iflow")
|
|
378
422
|
|
|
379
423
|
# =========================================================================
|
|
380
424
|
# Session ID Handling
|
|
@@ -386,6 +430,7 @@ class CLIAdapter:
|
|
|
386
430
|
|
|
387
431
|
Claude Code: Yes (--session-id)
|
|
388
432
|
OpenCode: No (auto-generated, extract from logs)
|
|
433
|
+
iFlow: No (no session ID support)
|
|
389
434
|
"""
|
|
390
435
|
return self.platform == "claude"
|
|
391
436
|
|
|
@@ -418,7 +463,7 @@ def get_cli_adapter(platform: str = "claude") -> CLIAdapter:
|
|
|
418
463
|
"""Get CLI adapter for the specified platform.
|
|
419
464
|
|
|
420
465
|
Args:
|
|
421
|
-
platform: Platform name ('claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro',
|
|
466
|
+
platform: Platform name ('claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', or 'qoder')
|
|
422
467
|
|
|
423
468
|
Returns:
|
|
424
469
|
CLIAdapter instance
|
|
@@ -426,8 +471,21 @@ def get_cli_adapter(platform: str = "claude") -> CLIAdapter:
|
|
|
426
471
|
Raises:
|
|
427
472
|
ValueError: If platform is not supported
|
|
428
473
|
"""
|
|
429
|
-
if platform not in (
|
|
430
|
-
|
|
474
|
+
if platform not in (
|
|
475
|
+
"claude",
|
|
476
|
+
"opencode",
|
|
477
|
+
"cursor",
|
|
478
|
+
"iflow",
|
|
479
|
+
"codex",
|
|
480
|
+
"kilo",
|
|
481
|
+
"kiro",
|
|
482
|
+
"gemini",
|
|
483
|
+
"antigravity",
|
|
484
|
+
"qoder",
|
|
485
|
+
):
|
|
486
|
+
raise ValueError(
|
|
487
|
+
f"Unsupported platform: {platform} (must be 'claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', or 'qoder')"
|
|
488
|
+
)
|
|
431
489
|
|
|
432
490
|
return CLIAdapter(platform=platform) # type: ignore
|
|
433
491
|
|
|
@@ -445,19 +503,31 @@ def detect_platform(project_root: Path) -> Platform:
|
|
|
445
503
|
7. .kiro/skills exists and no other platform dirs → kiro
|
|
446
504
|
8. .gemini directory exists → gemini
|
|
447
505
|
9. .agent/workflows exists and no other platform dirs → antigravity
|
|
448
|
-
10.
|
|
506
|
+
10. .qoder directory exists → qoder
|
|
507
|
+
11. Default → claude
|
|
449
508
|
|
|
450
509
|
Args:
|
|
451
510
|
project_root: Project root directory
|
|
452
511
|
|
|
453
512
|
Returns:
|
|
454
|
-
Detected platform ('claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', or '
|
|
513
|
+
Detected platform ('claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', or 'qoder')
|
|
455
514
|
"""
|
|
456
515
|
import os
|
|
457
516
|
|
|
458
517
|
# Check environment variable first
|
|
459
518
|
env_platform = os.environ.get("TRELLIS_PLATFORM", "").lower()
|
|
460
|
-
if env_platform in (
|
|
519
|
+
if env_platform in (
|
|
520
|
+
"claude",
|
|
521
|
+
"opencode",
|
|
522
|
+
"cursor",
|
|
523
|
+
"iflow",
|
|
524
|
+
"codex",
|
|
525
|
+
"kilo",
|
|
526
|
+
"kiro",
|
|
527
|
+
"gemini",
|
|
528
|
+
"antigravity",
|
|
529
|
+
"qoder",
|
|
530
|
+
):
|
|
461
531
|
return env_platform # type: ignore
|
|
462
532
|
|
|
463
533
|
# Check for .opencode directory (OpenCode-specific)
|
|
@@ -480,7 +550,16 @@ def detect_platform(project_root: Path) -> Platform:
|
|
|
480
550
|
return "gemini"
|
|
481
551
|
|
|
482
552
|
# Check for Codex skills directory only when no other platform config exists
|
|
483
|
-
other_platform_dirs_codex = (
|
|
553
|
+
other_platform_dirs_codex = (
|
|
554
|
+
".claude",
|
|
555
|
+
".cursor",
|
|
556
|
+
".iflow",
|
|
557
|
+
".opencode",
|
|
558
|
+
".kilocode",
|
|
559
|
+
".kiro",
|
|
560
|
+
".gemini",
|
|
561
|
+
".agent",
|
|
562
|
+
)
|
|
484
563
|
has_other_platform_config = any(
|
|
485
564
|
(project_root / directory).is_dir() for directory in other_platform_dirs_codex
|
|
486
565
|
)
|
|
@@ -492,7 +571,16 @@ def detect_platform(project_root: Path) -> Platform:
|
|
|
492
571
|
return "kilo"
|
|
493
572
|
|
|
494
573
|
# Check for Kiro skills directory only when no other platform config exists
|
|
495
|
-
other_platform_dirs_kiro = (
|
|
574
|
+
other_platform_dirs_kiro = (
|
|
575
|
+
".claude",
|
|
576
|
+
".cursor",
|
|
577
|
+
".iflow",
|
|
578
|
+
".opencode",
|
|
579
|
+
".agents",
|
|
580
|
+
".kilocode",
|
|
581
|
+
".gemini",
|
|
582
|
+
".agent",
|
|
583
|
+
)
|
|
496
584
|
has_other_platform_config = any(
|
|
497
585
|
(project_root / directory).is_dir() for directory in other_platform_dirs_kiro
|
|
498
586
|
)
|
|
@@ -500,13 +588,28 @@ def detect_platform(project_root: Path) -> Platform:
|
|
|
500
588
|
return "kiro"
|
|
501
589
|
|
|
502
590
|
# Check for Antigravity workflow directory only when no other platform config exists
|
|
503
|
-
other_platform_dirs_antigravity = (
|
|
591
|
+
other_platform_dirs_antigravity = (
|
|
592
|
+
".claude",
|
|
593
|
+
".cursor",
|
|
594
|
+
".iflow",
|
|
595
|
+
".opencode",
|
|
596
|
+
".agents",
|
|
597
|
+
".kilocode",
|
|
598
|
+
".kiro",
|
|
599
|
+
)
|
|
504
600
|
has_other_platform_config = any(
|
|
505
|
-
(project_root / directory).is_dir()
|
|
601
|
+
(project_root / directory).is_dir()
|
|
602
|
+
for directory in other_platform_dirs_antigravity
|
|
506
603
|
)
|
|
507
|
-
if (
|
|
604
|
+
if (
|
|
605
|
+
project_root / ".agent" / "workflows"
|
|
606
|
+
).is_dir() and not has_other_platform_config:
|
|
508
607
|
return "antigravity"
|
|
509
608
|
|
|
609
|
+
# Check for .qoder directory (Qoder-specific)
|
|
610
|
+
if (project_root / ".qoder").is_dir():
|
|
611
|
+
return "qoder"
|
|
612
|
+
|
|
510
613
|
return "claude"
|
|
511
614
|
|
|
512
615
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Trellis configuration reader.
|
|
4
|
+
|
|
5
|
+
Reads settings from .trellis/config.yaml with sensible defaults.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from .paths import DIR_WORKFLOW, get_repo_root
|
|
13
|
+
from .worktree import parse_simple_yaml
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# Defaults
|
|
17
|
+
DEFAULT_SESSION_COMMIT_MESSAGE = "chore: record journal"
|
|
18
|
+
DEFAULT_MAX_JOURNAL_LINES = 2000
|
|
19
|
+
|
|
20
|
+
CONFIG_FILE = "config.yaml"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _get_config_path(repo_root: Path | None = None) -> Path:
|
|
24
|
+
"""Get path to config.yaml."""
|
|
25
|
+
root = repo_root or get_repo_root()
|
|
26
|
+
return root / DIR_WORKFLOW / CONFIG_FILE
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _load_config(repo_root: Path | None = None) -> dict:
|
|
30
|
+
"""Load and parse config.yaml. Returns empty dict on any error."""
|
|
31
|
+
config_file = _get_config_path(repo_root)
|
|
32
|
+
try:
|
|
33
|
+
content = config_file.read_text(encoding="utf-8")
|
|
34
|
+
return parse_simple_yaml(content)
|
|
35
|
+
except (OSError, IOError):
|
|
36
|
+
return {}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_session_commit_message(repo_root: Path | None = None) -> str:
|
|
40
|
+
"""Get the commit message for auto-committing session records."""
|
|
41
|
+
config = _load_config(repo_root)
|
|
42
|
+
return config.get("session_commit_message", DEFAULT_SESSION_COMMIT_MESSAGE)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_max_journal_lines(repo_root: Path | None = None) -> int:
|
|
46
|
+
"""Get the maximum lines per journal file."""
|
|
47
|
+
config = _load_config(repo_root)
|
|
48
|
+
value = config.get("max_journal_lines", DEFAULT_MAX_JOURNAL_LINES)
|
|
49
|
+
try:
|
|
50
|
+
return int(value)
|
|
51
|
+
except (ValueError, TypeError):
|
|
52
|
+
return DEFAULT_MAX_JOURNAL_LINES
|