@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.
Files changed (85) hide show
  1. package/README.md +74 -130
  2. package/dist/cli/index.js +1 -0
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/commands/init.d.ts +1 -0
  5. package/dist/commands/init.d.ts.map +1 -1
  6. package/dist/commands/init.js +30 -2
  7. package/dist/commands/init.js.map +1 -1
  8. package/dist/commands/update.d.ts.map +1 -1
  9. package/dist/commands/update.js +11 -39
  10. package/dist/commands/update.js.map +1 -1
  11. package/dist/configurators/index.d.ts.map +1 -1
  12. package/dist/configurators/index.js +13 -1
  13. package/dist/configurators/index.js.map +1 -1
  14. package/dist/configurators/qoder.d.ts +8 -0
  15. package/dist/configurators/qoder.d.ts.map +1 -0
  16. package/dist/configurators/qoder.js +52 -0
  17. package/dist/configurators/qoder.js.map +1 -0
  18. package/dist/configurators/workflow.d.ts.map +1 -1
  19. package/dist/configurators/workflow.js +3 -1
  20. package/dist/configurators/workflow.js.map +1 -1
  21. package/dist/migrations/manifests/0.3.0-beta.0.json +60 -41
  22. package/dist/migrations/manifests/0.3.0.json +2 -2
  23. package/dist/migrations/manifests/0.3.1.json +9 -0
  24. package/dist/migrations/manifests/0.3.2.json +9 -0
  25. package/dist/migrations/manifests/0.3.3.json +9 -0
  26. package/dist/templates/claude/commands/trellis/record-session.md +12 -16
  27. package/dist/templates/claude/settings.json +20 -0
  28. package/dist/templates/codex/skills/record-session/SKILL.md +13 -17
  29. package/dist/templates/cursor/commands/trellis-record-session.md +12 -16
  30. package/dist/templates/extract.d.ts +7 -0
  31. package/dist/templates/extract.d.ts.map +1 -1
  32. package/dist/templates/extract.js +13 -0
  33. package/dist/templates/extract.js.map +1 -1
  34. package/dist/templates/gemini/commands/trellis/record-session.toml +12 -16
  35. package/dist/templates/iflow/commands/trellis/record-session.md +12 -16
  36. package/dist/templates/iflow/hooks/session-start.py +1 -0
  37. package/dist/templates/iflow/settings.json +20 -0
  38. package/dist/templates/kilo/commands/trellis/record-session.md +12 -16
  39. package/dist/templates/kiro/skills/record-session/SKILL.md +13 -17
  40. package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md.txt +13 -0
  41. package/dist/templates/opencode/commands/trellis/record-session.md +12 -16
  42. package/dist/templates/qoder/index.d.ts +18 -0
  43. package/dist/templates/qoder/index.d.ts.map +1 -0
  44. package/dist/templates/qoder/index.js +40 -0
  45. package/dist/templates/qoder/index.js.map +1 -0
  46. package/dist/templates/qoder/skills/before-backend-dev/SKILL.md +18 -0
  47. package/dist/templates/qoder/skills/before-frontend-dev/SKILL.md +18 -0
  48. package/dist/templates/qoder/skills/brainstorm/SKILL.md +479 -0
  49. package/dist/templates/qoder/skills/break-loop/SKILL.md +130 -0
  50. package/dist/templates/qoder/skills/check-backend/SKILL.md +18 -0
  51. package/dist/templates/qoder/skills/check-cross-layer/SKILL.md +158 -0
  52. package/dist/templates/qoder/skills/check-frontend/SKILL.md +18 -0
  53. package/dist/templates/qoder/skills/create-command/SKILL.md +101 -0
  54. package/dist/templates/qoder/skills/finish-work/SKILL.md +134 -0
  55. package/dist/templates/qoder/skills/integrate-skill/SKILL.md +221 -0
  56. package/dist/templates/qoder/skills/onboard/SKILL.md +363 -0
  57. package/dist/templates/qoder/skills/record-session/SKILL.md +63 -0
  58. package/dist/templates/qoder/skills/start/SKILL.md +326 -0
  59. package/dist/templates/qoder/skills/update-spec/SKILL.md +290 -0
  60. package/dist/templates/trellis/config.yaml +15 -0
  61. package/dist/templates/trellis/index.d.ts +3 -0
  62. package/dist/templates/trellis/index.d.ts.map +1 -1
  63. package/dist/templates/trellis/index.js +4 -0
  64. package/dist/templates/trellis/index.js.map +1 -1
  65. package/dist/templates/trellis/scripts/add_session.py +52 -21
  66. package/dist/templates/trellis/scripts/common/__init__.py +3 -1
  67. package/dist/templates/trellis/scripts/common/cli_adapter.py +122 -19
  68. package/dist/templates/trellis/scripts/common/config.py +52 -0
  69. package/dist/templates/trellis/scripts/common/git_context.py +121 -11
  70. package/dist/templates/trellis/scripts/multi_agent/plan.py +4 -1
  71. package/dist/templates/trellis/scripts/multi_agent/start.py +5 -1
  72. package/dist/templates/trellis/scripts/task.py +26 -0
  73. package/dist/types/ai-tools.d.ts +3 -3
  74. package/dist/types/ai-tools.d.ts.map +1 -1
  75. package/dist/types/ai-tools.js +8 -0
  76. package/dist/types/ai-tools.js.map +1 -1
  77. package/dist/utils/proxy.d.ts +25 -0
  78. package/dist/utils/proxy.d.ts.map +1 -0
  79. package/dist/utils/proxy.js +60 -0
  80. package/dist/utils/proxy.js.map +1 -0
  81. package/dist/utils/template-fetcher.d.ts +11 -2
  82. package/dist/utils/template-fetcher.d.ts.map +1 -1
  83. package/dist/utils/template-fetcher.js +92 -19
  84. package/dist/utils/template-fetcher.js.map +1 -1
  85. package/package.json +4 -3
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/trellis/index.ts"],"names":[],"mappings":"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"}
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(dev_dir: Path, num: int, developer: str, today: str) -> Path:
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 ~{MAX_LINES} lines)
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 > MAX_LINES:
354
+ if current_lines + content_lines > max_lines:
334
355
  target_num = current_num + 1
335
- print(f"[!] Exceeds {MAX_LINES} lines, creating {FILE_JOURNAL_PREFIX}{target_num}.md", file=sys.stderr)
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(args.title, args.commit, args.summary, extra_content)
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 Antigravity interfaces.
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["claude", "opencode", "cursor", "iflow", "codex", "kilo", "kiro", "gemini", "antigravity"]
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 '.agent')
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 .agent)
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 self.get_config_dir(project_root) / "commands" / f"trellis-{filename}"
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 OpenCode support CLI agent execution.
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', or 'antigravity')
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 ("claude", "opencode", "cursor", "iflow", "codex", "kilo", "kiro", "gemini", "antigravity"):
430
- raise ValueError(f"Unsupported platform: {platform} (must be 'claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', or 'antigravity')")
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. Defaultclaude
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 'antigravity')
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 ("claude", "opencode", "cursor", "iflow", "codex", "kilo", "kiro", "gemini", "antigravity"):
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 = (".claude", ".cursor", ".iflow", ".opencode", ".kilocode", ".kiro", ".gemini", ".agent")
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 = (".claude", ".cursor", ".iflow", ".opencode", ".agents", ".kilocode", ".gemini", ".agent")
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 = (".claude", ".cursor", ".iflow", ".opencode", ".agents", ".kilocode", ".kiro")
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() for directory in other_platform_dirs_antigravity
601
+ (project_root / directory).is_dir()
602
+ for directory in other_platform_dirs_antigravity
506
603
  )
507
- if (project_root / ".agent" / "workflows").is_dir() and not has_other_platform_config:
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