@mindfoldhq/trellis 0.3.10 → 0.4.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -5
- package/dist/cli/index.js +5 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +240 -43
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +206 -47
- package/dist/commands/update.js.map +1 -1
- package/dist/configurators/codebuddy.d.ts +11 -0
- package/dist/configurators/codebuddy.d.ts.map +1 -0
- package/dist/configurators/codebuddy.js +58 -0
- package/dist/configurators/codebuddy.js.map +1 -0
- package/dist/configurators/codex.d.ts +7 -4
- package/dist/configurators/codex.d.ts.map +1 -1
- package/dist/configurators/codex.js +40 -10
- package/dist/configurators/codex.js.map +1 -1
- package/dist/configurators/copilot.d.ts +9 -0
- package/dist/configurators/copilot.d.ts.map +1 -0
- package/dist/configurators/copilot.js +34 -0
- package/dist/configurators/copilot.js.map +1 -0
- package/dist/configurators/index.d.ts +11 -1
- package/dist/configurators/index.d.ts.map +1 -1
- package/dist/configurators/index.js +72 -4
- package/dist/configurators/index.js.map +1 -1
- package/dist/configurators/opencode.d.ts +1 -1
- package/dist/configurators/opencode.js +1 -1
- package/dist/configurators/windsurf.d.ts +8 -0
- package/dist/configurators/windsurf.d.ts.map +1 -0
- package/dist/configurators/windsurf.js +18 -0
- package/dist/configurators/windsurf.js.map +1 -0
- package/dist/configurators/workflow.d.ts +6 -2
- package/dist/configurators/workflow.d.ts.map +1 -1
- package/dist/configurators/workflow.js +90 -58
- package/dist/configurators/workflow.js.map +1 -1
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +2 -0
- package/dist/migrations/index.js.map +1 -1
- package/dist/migrations/manifests/0.4.0-beta.1.json +228 -0
- package/dist/migrations/manifests/0.4.0-beta.10.json +9 -0
- package/dist/migrations/manifests/0.4.0-beta.2.json +9 -0
- package/dist/migrations/manifests/0.4.0-beta.3.json +9 -0
- package/dist/migrations/manifests/0.4.0-beta.4.json +9 -0
- package/dist/migrations/manifests/0.4.0-beta.5.json +9 -0
- package/dist/migrations/manifests/0.4.0-beta.6.json +9 -0
- package/dist/migrations/manifests/0.4.0-beta.7.json +9 -0
- package/dist/migrations/manifests/0.4.0-beta.8.json +34 -0
- package/dist/migrations/manifests/0.4.0-beta.9.json +9 -0
- package/dist/templates/claude/agents/dispatch.md +1 -2
- package/dist/templates/claude/agents/implement.md +2 -3
- package/dist/templates/claude/commands/trellis/before-dev.md +29 -0
- package/dist/templates/claude/commands/trellis/check.md +25 -0
- package/dist/templates/claude/commands/trellis/create-command.md +2 -2
- package/dist/templates/claude/commands/trellis/onboard.md +13 -13
- package/dist/templates/claude/commands/trellis/parallel.md +1 -2
- package/dist/templates/claude/commands/trellis/record-session.md +3 -2
- package/dist/templates/claude/commands/trellis/start.md +8 -4
- package/dist/templates/claude/hooks/inject-subagent-context.py +29 -14
- package/dist/templates/claude/hooks/ralph-loop.py +18 -10
- package/dist/templates/claude/hooks/session-start.py +201 -9
- package/dist/templates/claude/hooks/statusline.py +211 -0
- package/dist/templates/claude/settings.json +4 -0
- package/dist/templates/codebuddy/commands/trellis/before-dev.md +29 -0
- package/dist/templates/codebuddy/commands/trellis/brainstorm.md +487 -0
- package/dist/templates/codebuddy/commands/trellis/break-loop.md +107 -0
- package/dist/templates/codebuddy/commands/trellis/check-cross-layer.md +153 -0
- package/dist/templates/codebuddy/commands/trellis/check.md +25 -0
- package/dist/templates/codebuddy/commands/trellis/create-command.md +154 -0
- package/dist/templates/codebuddy/commands/trellis/finish-work.md +143 -0
- package/dist/templates/codebuddy/commands/trellis/integrate-skill.md +219 -0
- package/dist/templates/codebuddy/commands/trellis/onboard.md +358 -0
- package/dist/templates/codebuddy/commands/trellis/record-session.md +61 -0
- package/dist/templates/codebuddy/commands/trellis/start.md +373 -0
- package/dist/templates/codebuddy/commands/trellis/update-spec.md +354 -0
- package/dist/templates/codebuddy/index.d.ts +25 -0
- package/dist/templates/codebuddy/index.d.ts.map +1 -0
- package/dist/templates/codebuddy/index.js +45 -0
- package/dist/templates/codebuddy/index.js.map +1 -0
- package/dist/templates/codex/agents/check.toml +23 -0
- package/dist/templates/codex/agents/implement.toml +19 -0
- package/dist/templates/codex/agents/research.toml +26 -0
- package/dist/templates/codex/codex-skills/parallel/SKILL.md +194 -0
- package/dist/templates/codex/config.toml +5 -0
- package/dist/templates/codex/hooks/session-start.py +228 -0
- package/dist/templates/codex/hooks.json +16 -0
- package/dist/templates/codex/index.d.ts +27 -5
- package/dist/templates/codex/index.d.ts.map +1 -1
- package/dist/templates/codex/index.js +60 -8
- package/dist/templates/codex/index.js.map +1 -1
- package/dist/templates/codex/skills/before-dev/SKILL.md +34 -0
- package/dist/templates/codex/skills/brainstorm/SKILL.md +1 -1
- package/dist/templates/codex/skills/break-loop/SKILL.md +1 -1
- package/dist/templates/codex/skills/check/SKILL.md +30 -0
- package/dist/templates/codex/skills/check-cross-layer/SKILL.md +1 -1
- package/dist/templates/codex/skills/create-command/SKILL.md +3 -3
- package/dist/templates/codex/skills/finish-work/SKILL.md +1 -1
- package/dist/templates/codex/skills/improve-ut/SKILL.md +69 -0
- package/dist/templates/codex/skills/integrate-skill/SKILL.md +1 -1
- package/dist/templates/codex/skills/onboard/SKILL.md +12 -12
- package/dist/templates/codex/skills/record-session/SKILL.md +4 -3
- package/dist/templates/codex/skills/start/SKILL.md +9 -4
- package/dist/templates/codex/skills/update-spec/SKILL.md +1 -1
- package/dist/templates/copilot/hooks/session-start.py +218 -0
- package/dist/templates/copilot/hooks.json +11 -0
- package/dist/templates/copilot/index.d.ts +23 -0
- package/dist/templates/copilot/index.d.ts.map +1 -0
- package/dist/templates/copilot/index.js +54 -0
- package/dist/templates/copilot/index.js.map +1 -0
- package/dist/templates/copilot/prompts/before-dev.prompt.md +33 -0
- package/dist/templates/copilot/prompts/brainstorm.prompt.md +491 -0
- package/dist/templates/copilot/prompts/break-loop.prompt.md +129 -0
- package/dist/templates/copilot/prompts/check-cross-layer.prompt.md +157 -0
- package/dist/templates/copilot/prompts/check.prompt.md +29 -0
- package/dist/templates/copilot/prompts/create-command.prompt.md +116 -0
- package/dist/templates/copilot/prompts/finish-work.prompt.md +157 -0
- package/dist/templates/copilot/prompts/integrate-skill.prompt.md +223 -0
- package/dist/templates/copilot/prompts/onboard.prompt.md +362 -0
- package/dist/templates/copilot/prompts/parallel.prompt.md +196 -0
- package/dist/templates/copilot/prompts/record-session.prompt.md +66 -0
- package/dist/templates/copilot/prompts/start.prompt.md +397 -0
- package/dist/templates/copilot/prompts/update-spec.prompt.md +358 -0
- package/dist/templates/cursor/commands/trellis-before-dev.md +29 -0
- package/dist/templates/cursor/commands/trellis-check.md +25 -0
- package/dist/templates/cursor/commands/trellis-create-command.md +2 -2
- package/dist/templates/cursor/commands/trellis-onboard.md +13 -13
- package/dist/templates/cursor/commands/trellis-record-session.md +3 -2
- package/dist/templates/cursor/commands/trellis-start.md +7 -16
- package/dist/templates/extract.d.ts +36 -0
- package/dist/templates/extract.d.ts.map +1 -1
- package/dist/templates/extract.js +64 -0
- package/dist/templates/extract.js.map +1 -1
- package/dist/templates/gemini/commands/trellis/before-dev.toml +33 -0
- package/dist/templates/gemini/commands/trellis/check.toml +29 -0
- package/dist/templates/gemini/commands/trellis/create-command.toml +2 -2
- package/dist/templates/gemini/commands/trellis/onboard.toml +2 -2
- package/dist/templates/gemini/commands/trellis/record-session.toml +3 -2
- package/dist/templates/gemini/commands/trellis/start.toml +9 -4
- package/dist/templates/iflow/agents/dispatch.md +1 -2
- package/dist/templates/iflow/agents/implement.md +2 -3
- package/dist/templates/iflow/commands/trellis/before-dev.md +29 -0
- package/dist/templates/iflow/commands/trellis/check.md +25 -0
- package/dist/templates/iflow/commands/trellis/create-command.md +2 -2
- package/dist/templates/iflow/commands/trellis/onboard.md +13 -13
- package/dist/templates/iflow/commands/trellis/parallel.md +1 -2
- package/dist/templates/iflow/commands/trellis/record-session.md +3 -2
- package/dist/templates/iflow/commands/trellis/start.md +8 -4
- package/dist/templates/iflow/hooks/inject-subagent-context.py +29 -14
- package/dist/templates/iflow/hooks/ralph-loop.py +8 -1
- package/dist/templates/iflow/hooks/session-start.py +187 -8
- package/dist/templates/kilo/workflows/before-dev.md +29 -0
- package/dist/templates/kilo/workflows/check.md +25 -0
- package/dist/templates/kilo/workflows/create-command.md +2 -2
- package/dist/templates/kilo/workflows/onboard.md +13 -13
- package/dist/templates/kilo/workflows/parallel.md +1 -2
- package/dist/templates/kilo/workflows/record-session.md +3 -2
- package/dist/templates/kilo/workflows/start.md +8 -3
- package/dist/templates/kiro/skills/before-dev/SKILL.md +34 -0
- package/dist/templates/kiro/skills/brainstorm/SKILL.md +1 -1
- package/dist/templates/kiro/skills/break-loop/SKILL.md +1 -1
- package/dist/templates/kiro/skills/check/SKILL.md +30 -0
- package/dist/templates/kiro/skills/check-cross-layer/SKILL.md +1 -1
- package/dist/templates/kiro/skills/create-command/SKILL.md +3 -3
- package/dist/templates/kiro/skills/finish-work/SKILL.md +1 -1
- package/dist/templates/kiro/skills/integrate-skill/SKILL.md +1 -1
- package/dist/templates/kiro/skills/onboard/SKILL.md +12 -12
- package/dist/templates/kiro/skills/record-session/SKILL.md +4 -3
- package/dist/templates/kiro/skills/start/SKILL.md +9 -4
- package/dist/templates/kiro/skills/update-spec/SKILL.md +1 -1
- package/dist/templates/markdown/agents.md +4 -0
- package/dist/templates/markdown/spec/backend/directory-structure.md +1 -1
- package/dist/templates/markdown/spec/backend/script-conventions.md +93 -0
- package/dist/templates/markdown/workspace-index.md +2 -0
- package/dist/templates/opencode/agents/dispatch.md +21 -21
- package/dist/templates/opencode/agents/implement.md +2 -2
- package/dist/templates/opencode/agents/research.md +1 -2
- package/dist/templates/opencode/commands/trellis/before-dev.md +29 -0
- package/dist/templates/opencode/commands/trellis/check.md +25 -0
- package/dist/templates/opencode/commands/trellis/create-command.md +2 -2
- package/dist/templates/opencode/commands/trellis/onboard.md +13 -13
- package/dist/templates/opencode/commands/trellis/parallel.md +1 -2
- package/dist/templates/opencode/commands/trellis/record-session.md +3 -2
- package/dist/templates/opencode/commands/trellis/start.md +8 -3
- package/dist/templates/opencode/lib/trellis-context.js +42 -2
- package/dist/templates/opencode/{plugin → plugins}/inject-subagent-context.js +45 -18
- package/dist/templates/opencode/{plugin → plugins}/session-start.js +156 -28
- package/dist/templates/qoder/skills/before-dev/SKILL.md +34 -0
- package/dist/templates/qoder/skills/brainstorm/SKILL.md +1 -1
- package/dist/templates/qoder/skills/break-loop/SKILL.md +1 -1
- package/dist/templates/qoder/skills/check/SKILL.md +30 -0
- package/dist/templates/qoder/skills/check-cross-layer/SKILL.md +1 -1
- package/dist/templates/qoder/skills/create-command/SKILL.md +3 -3
- package/dist/templates/qoder/skills/finish-work/SKILL.md +1 -1
- package/dist/templates/qoder/skills/integrate-skill/SKILL.md +1 -1
- package/dist/templates/qoder/skills/onboard/SKILL.md +14 -14
- package/dist/templates/qoder/skills/record-session/SKILL.md +4 -3
- package/dist/templates/qoder/skills/start/SKILL.md +9 -4
- package/dist/templates/qoder/skills/update-spec/SKILL.md +1 -1
- package/dist/templates/trellis/config.yaml +20 -0
- package/dist/templates/trellis/index.d.ts +11 -0
- package/dist/templates/trellis/index.d.ts.map +1 -1
- package/dist/templates/trellis/index.js +22 -0
- package/dist/templates/trellis/index.js.map +1 -1
- package/dist/templates/trellis/scripts/add_session.py +111 -13
- package/dist/templates/trellis/scripts/common/__init__.py +2 -0
- package/dist/templates/trellis/scripts/common/cli_adapter.py +164 -64
- package/dist/templates/trellis/scripts/common/config.py +192 -0
- package/dist/templates/trellis/scripts/common/developer.py +2 -2
- package/dist/templates/trellis/scripts/common/git.py +31 -0
- package/dist/templates/trellis/scripts/common/git_context.py +23 -586
- package/dist/templates/trellis/scripts/common/io.py +37 -0
- package/dist/templates/trellis/scripts/common/log.py +45 -0
- package/dist/templates/trellis/scripts/common/packages_context.py +238 -0
- package/dist/templates/trellis/scripts/common/paths.py +103 -6
- package/dist/templates/trellis/scripts/common/phase.py +50 -49
- package/dist/templates/trellis/scripts/common/registry.py +41 -72
- package/dist/templates/trellis/scripts/common/session_context.py +562 -0
- package/dist/templates/trellis/scripts/common/task_context.py +410 -0
- package/dist/templates/trellis/scripts/common/task_queue.py +27 -98
- package/dist/templates/trellis/scripts/common/task_store.py +536 -0
- package/dist/templates/trellis/scripts/common/task_utils.py +106 -10
- package/dist/templates/trellis/scripts/common/tasks.py +109 -0
- package/dist/templates/trellis/scripts/common/types.py +112 -0
- package/dist/templates/trellis/scripts/create_bootstrap.py +32 -27
- package/dist/templates/trellis/scripts/hooks/linear_sync.py +243 -0
- package/dist/templates/trellis/scripts/multi_agent/_bootstrap.py +17 -0
- package/dist/templates/trellis/scripts/multi_agent/cleanup.py +43 -48
- package/dist/templates/trellis/scripts/multi_agent/create_pr.py +336 -45
- package/dist/templates/trellis/scripts/multi_agent/plan.py +9 -32
- package/dist/templates/trellis/scripts/multi_agent/start.py +142 -68
- package/dist/templates/trellis/scripts/multi_agent/status.py +12 -753
- package/dist/templates/trellis/scripts/multi_agent/status_display.py +542 -0
- package/dist/templates/trellis/scripts/multi_agent/status_monitor.py +225 -0
- package/dist/templates/trellis/scripts/task.py +51 -976
- package/dist/templates/trellis/scripts-shell-archive/create-bootstrap.sh +1 -1
- package/dist/templates/trellis/workflow.md +38 -38
- package/dist/templates/windsurf/index.d.ts +21 -0
- package/dist/templates/windsurf/index.d.ts.map +1 -0
- package/dist/templates/windsurf/index.js +44 -0
- package/dist/templates/windsurf/index.js.map +1 -0
- package/dist/templates/windsurf/workflows/trellis-before-dev.md +31 -0
- package/dist/templates/windsurf/workflows/trellis-brainstorm.md +491 -0
- package/dist/templates/windsurf/workflows/trellis-break-loop.md +111 -0
- package/dist/templates/windsurf/workflows/trellis-check-cross-layer.md +157 -0
- package/dist/templates/windsurf/workflows/trellis-check.md +27 -0
- package/dist/templates/windsurf/workflows/trellis-create-command.md +154 -0
- package/dist/templates/windsurf/workflows/trellis-finish-work.md +147 -0
- package/dist/templates/windsurf/workflows/trellis-integrate-skill.md +220 -0
- package/dist/templates/windsurf/workflows/trellis-onboard.md +362 -0
- package/dist/templates/windsurf/workflows/trellis-record-session.md +66 -0
- package/dist/templates/windsurf/workflows/trellis-start.md +373 -0
- package/dist/templates/windsurf/workflows/trellis-update-spec.md +358 -0
- package/dist/types/ai-tools.d.ts +15 -3
- package/dist/types/ai-tools.d.ts.map +1 -1
- package/dist/types/ai-tools.js +42 -2
- package/dist/types/ai-tools.js.map +1 -1
- package/dist/types/migration.d.ts +3 -1
- package/dist/types/migration.d.ts.map +1 -1
- package/dist/utils/project-detector.d.ts +28 -0
- package/dist/utils/project-detector.d.ts.map +1 -1
- package/dist/utils/project-detector.js +371 -0
- package/dist/utils/project-detector.js.map +1 -1
- package/dist/utils/template-fetcher.d.ts +19 -6
- package/dist/utils/template-fetcher.d.ts.map +1 -1
- package/dist/utils/template-fetcher.js +99 -17
- package/dist/utils/template-fetcher.js.map +1 -1
- package/package.json +1 -1
- package/dist/templates/claude/commands/trellis/before-backend-dev.md +0 -13
- package/dist/templates/claude/commands/trellis/before-frontend-dev.md +0 -13
- package/dist/templates/claude/commands/trellis/check-backend.md +0 -13
- package/dist/templates/claude/commands/trellis/check-frontend.md +0 -13
- package/dist/templates/codex/skills/before-backend-dev/SKILL.md +0 -18
- package/dist/templates/codex/skills/before-frontend-dev/SKILL.md +0 -18
- package/dist/templates/codex/skills/check-backend/SKILL.md +0 -18
- package/dist/templates/codex/skills/check-frontend/SKILL.md +0 -18
- package/dist/templates/cursor/commands/trellis-before-backend-dev.md +0 -13
- package/dist/templates/cursor/commands/trellis-before-frontend-dev.md +0 -13
- package/dist/templates/cursor/commands/trellis-check-backend.md +0 -13
- package/dist/templates/cursor/commands/trellis-check-frontend.md +0 -13
- package/dist/templates/gemini/commands/trellis/before-backend-dev.toml +0 -17
- package/dist/templates/gemini/commands/trellis/before-frontend-dev.toml +0 -17
- package/dist/templates/gemini/commands/trellis/check-backend.toml +0 -17
- package/dist/templates/gemini/commands/trellis/check-frontend.toml +0 -17
- package/dist/templates/iflow/commands/trellis/before-backend-dev.md +0 -13
- package/dist/templates/iflow/commands/trellis/before-frontend-dev.md +0 -13
- package/dist/templates/iflow/commands/trellis/check-backend.md +0 -13
- package/dist/templates/iflow/commands/trellis/check-frontend.md +0 -13
- package/dist/templates/kilo/workflows/before-backend-dev.md +0 -13
- package/dist/templates/kilo/workflows/before-frontend-dev.md +0 -13
- package/dist/templates/kilo/workflows/check-backend.md +0 -13
- package/dist/templates/kilo/workflows/check-frontend.md +0 -13
- package/dist/templates/kiro/skills/before-backend-dev/SKILL.md +0 -18
- package/dist/templates/kiro/skills/before-frontend-dev/SKILL.md +0 -18
- package/dist/templates/kiro/skills/check-backend/SKILL.md +0 -18
- package/dist/templates/kiro/skills/check-frontend/SKILL.md +0 -18
- package/dist/templates/opencode/commands/trellis/before-backend-dev.md +0 -13
- package/dist/templates/opencode/commands/trellis/before-frontend-dev.md +0 -13
- package/dist/templates/opencode/commands/trellis/check-backend.md +0 -13
- package/dist/templates/opencode/commands/trellis/check-frontend.md +0 -13
- package/dist/templates/qoder/skills/before-backend-dev/SKILL.md +0 -18
- package/dist/templates/qoder/skills/before-frontend-dev/SKILL.md +0 -18
- package/dist/templates/qoder/skills/check-backend/SKILL.md +0 -18
- package/dist/templates/qoder/skills/check-frontend/SKILL.md +0 -18
|
@@ -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,
|
|
4
|
+
Abstracts differences between Claude Code, OpenCode, Cursor, iFlow, Codex, Kilo, Kiro Code, Gemini CLI, Antigravity, Windsurf, Qoder, CodeBuddy, and GitHub Copilot interfaces.
|
|
5
5
|
|
|
6
6
|
Supported platforms:
|
|
7
7
|
- claude: Claude Code (default)
|
|
@@ -13,7 +13,10 @@ Supported platforms:
|
|
|
13
13
|
- kiro: Kiro Code (skills-based)
|
|
14
14
|
- gemini: Gemini CLI
|
|
15
15
|
- antigravity: Antigravity (workflow-based)
|
|
16
|
+
- windsurf: Windsurf (workflow-based)
|
|
16
17
|
- qoder: Qoder
|
|
18
|
+
- codebuddy: CodeBuddy
|
|
19
|
+
- copilot: GitHub Copilot (VS Code)
|
|
17
20
|
|
|
18
21
|
Usage:
|
|
19
22
|
from common.cli_adapter import CLIAdapter
|
|
@@ -42,7 +45,10 @@ Platform = Literal[
|
|
|
42
45
|
"kiro",
|
|
43
46
|
"gemini",
|
|
44
47
|
"antigravity",
|
|
48
|
+
"windsurf",
|
|
45
49
|
"qoder",
|
|
50
|
+
"codebuddy",
|
|
51
|
+
"copilot",
|
|
46
52
|
]
|
|
47
53
|
|
|
48
54
|
|
|
@@ -87,7 +93,7 @@ class CLIAdapter:
|
|
|
87
93
|
"""Get platform-specific config directory name.
|
|
88
94
|
|
|
89
95
|
Returns:
|
|
90
|
-
Directory name ('.claude', '.opencode', '.cursor', '.iflow', '.
|
|
96
|
+
Directory name ('.claude', '.opencode', '.cursor', '.iflow', '.codex', '.kilocode', '.kiro', '.gemini', '.agent', '.windsurf', '.qoder', or '.codebuddy')
|
|
91
97
|
"""
|
|
92
98
|
if self.platform == "opencode":
|
|
93
99
|
return ".opencode"
|
|
@@ -96,7 +102,7 @@ class CLIAdapter:
|
|
|
96
102
|
elif self.platform == "iflow":
|
|
97
103
|
return ".iflow"
|
|
98
104
|
elif self.platform == "codex":
|
|
99
|
-
return ".
|
|
105
|
+
return ".codex"
|
|
100
106
|
elif self.platform == "kilo":
|
|
101
107
|
return ".kilocode"
|
|
102
108
|
elif self.platform == "kiro":
|
|
@@ -105,8 +111,14 @@ class CLIAdapter:
|
|
|
105
111
|
return ".gemini"
|
|
106
112
|
elif self.platform == "antigravity":
|
|
107
113
|
return ".agent"
|
|
114
|
+
elif self.platform == "windsurf":
|
|
115
|
+
return ".windsurf"
|
|
108
116
|
elif self.platform == "qoder":
|
|
109
117
|
return ".qoder"
|
|
118
|
+
elif self.platform == "codebuddy":
|
|
119
|
+
return ".codebuddy"
|
|
120
|
+
elif self.platform == "copilot":
|
|
121
|
+
return ".github/copilot"
|
|
110
122
|
else:
|
|
111
123
|
return ".claude"
|
|
112
124
|
|
|
@@ -117,7 +129,7 @@ class CLIAdapter:
|
|
|
117
129
|
project_root: Project root directory
|
|
118
130
|
|
|
119
131
|
Returns:
|
|
120
|
-
Path to config directory (.claude, .opencode, .cursor, .iflow, .
|
|
132
|
+
Path to config directory (.claude, .opencode, .cursor, .iflow, .codex, .kilocode, .kiro, .gemini, .agent, .windsurf, .qoder, or .codebuddy)
|
|
121
133
|
"""
|
|
122
134
|
return project_root / self.config_dir_name
|
|
123
135
|
|
|
@@ -129,9 +141,11 @@ class CLIAdapter:
|
|
|
129
141
|
project_root: Project root directory
|
|
130
142
|
|
|
131
143
|
Returns:
|
|
132
|
-
Path to agent .md
|
|
144
|
+
Path to agent definition file (.md for most platforms, .toml for Codex)
|
|
133
145
|
"""
|
|
134
146
|
mapped_name = self.get_agent_name(agent)
|
|
147
|
+
if self.platform == "codex":
|
|
148
|
+
return self.get_config_dir(project_root) / "agents" / f"{mapped_name}.toml"
|
|
135
149
|
return self.get_config_dir(project_root) / "agents" / f"{mapped_name}.md"
|
|
136
150
|
|
|
137
151
|
def get_commands_path(self, project_root: Path, *parts: str) -> Path:
|
|
@@ -147,8 +161,19 @@ class CLIAdapter:
|
|
|
147
161
|
Note:
|
|
148
162
|
Cursor uses prefix naming: .cursor/commands/trellis-<name>.md
|
|
149
163
|
Antigravity uses workflow directory: .agent/workflows/<name>.md
|
|
164
|
+
Windsurf uses workflow directory: .windsurf/workflows/trellis-<name>.md
|
|
165
|
+
Copilot uses prompt files: .github/prompts/<name>.prompt.md
|
|
150
166
|
Claude/OpenCode use subdirectory: .claude/commands/trellis/<name>.md
|
|
151
167
|
"""
|
|
168
|
+
if self.platform == "windsurf":
|
|
169
|
+
workflow_dir = self.get_config_dir(project_root) / "workflows"
|
|
170
|
+
if not parts:
|
|
171
|
+
return workflow_dir
|
|
172
|
+
if len(parts) >= 2 and parts[0] == "trellis":
|
|
173
|
+
filename = parts[-1]
|
|
174
|
+
return workflow_dir / f"trellis-{filename}"
|
|
175
|
+
return workflow_dir / Path(*parts)
|
|
176
|
+
|
|
152
177
|
if self.platform in ("antigravity", "kilo"):
|
|
153
178
|
workflow_dir = self.get_config_dir(project_root) / "workflows"
|
|
154
179
|
if not parts:
|
|
@@ -158,6 +183,17 @@ class CLIAdapter:
|
|
|
158
183
|
return workflow_dir / filename
|
|
159
184
|
return workflow_dir / Path(*parts)
|
|
160
185
|
|
|
186
|
+
if self.platform == "copilot":
|
|
187
|
+
prompts_dir = project_root / ".github" / "prompts"
|
|
188
|
+
if not parts:
|
|
189
|
+
return prompts_dir
|
|
190
|
+
if len(parts) >= 2 and parts[0] == "trellis":
|
|
191
|
+
filename = parts[-1]
|
|
192
|
+
if filename.endswith(".md"):
|
|
193
|
+
filename = filename[:-3]
|
|
194
|
+
return prompts_dir / f"{filename}.prompt.md"
|
|
195
|
+
return prompts_dir / Path(*parts)
|
|
196
|
+
|
|
161
197
|
if not parts:
|
|
162
198
|
return self.get_config_dir(project_root) / "commands"
|
|
163
199
|
|
|
@@ -175,7 +211,7 @@ class CLIAdapter:
|
|
|
175
211
|
"""Get relative path to a trellis command file.
|
|
176
212
|
|
|
177
213
|
Args:
|
|
178
|
-
name: Command name without extension (e.g., 'finish-work', 'check
|
|
214
|
+
name: Command name without extension (e.g., 'finish-work', 'check')
|
|
179
215
|
|
|
180
216
|
Returns:
|
|
181
217
|
Relative path string for use in JSONL entries
|
|
@@ -186,6 +222,7 @@ class CLIAdapter:
|
|
|
186
222
|
Kiro: .kiro/skills/<name>/SKILL.md
|
|
187
223
|
Gemini: .gemini/commands/trellis/<name>.toml
|
|
188
224
|
Antigravity: .agent/workflows/<name>.md
|
|
225
|
+
Windsurf: .windsurf/workflows/trellis-<name>.md
|
|
189
226
|
Others: .{platform}/commands/trellis/<name>.md
|
|
190
227
|
"""
|
|
191
228
|
if self.platform == "cursor":
|
|
@@ -198,8 +235,12 @@ class CLIAdapter:
|
|
|
198
235
|
return f".gemini/commands/trellis/{name}.toml"
|
|
199
236
|
elif self.platform == "antigravity":
|
|
200
237
|
return f".agent/workflows/{name}.md"
|
|
238
|
+
elif self.platform == "windsurf":
|
|
239
|
+
return f".windsurf/workflows/trellis-{name}.md"
|
|
201
240
|
elif self.platform == "kilo":
|
|
202
241
|
return f".kilocode/workflows/{name}.md"
|
|
242
|
+
elif self.platform == "copilot":
|
|
243
|
+
return f".github/prompts/{name}.prompt.md"
|
|
203
244
|
else:
|
|
204
245
|
return f"{self.config_dir_name}/commands/trellis/{name}.md"
|
|
205
246
|
|
|
@@ -225,8 +266,14 @@ class CLIAdapter:
|
|
|
225
266
|
return {} # Gemini CLI doesn't have a non-interactive env var
|
|
226
267
|
elif self.platform == "antigravity":
|
|
227
268
|
return {}
|
|
269
|
+
elif self.platform == "windsurf":
|
|
270
|
+
return {}
|
|
228
271
|
elif self.platform == "qoder":
|
|
229
272
|
return {}
|
|
273
|
+
elif self.platform == "codebuddy":
|
|
274
|
+
return {}
|
|
275
|
+
elif self.platform == "copilot":
|
|
276
|
+
return {}
|
|
230
277
|
else:
|
|
231
278
|
return {"CLAUDE_NON_INTERACTIVE": "1"}
|
|
232
279
|
|
|
@@ -278,12 +325,8 @@ class CLIAdapter:
|
|
|
278
325
|
cmd.append(prompt)
|
|
279
326
|
|
|
280
327
|
elif self.platform == "iflow":
|
|
281
|
-
cmd = ["iflow", "-p"]
|
|
282
|
-
cmd.
|
|
283
|
-
# iFlow doesn't support --session-id on creation
|
|
284
|
-
if verbose:
|
|
285
|
-
cmd.append("--verbose")
|
|
286
|
-
cmd.append(prompt)
|
|
328
|
+
cmd = ["iflow", "-y", "-p"]
|
|
329
|
+
cmd.append(f"${mapped_agent} {prompt}")
|
|
287
330
|
elif self.platform == "codex":
|
|
288
331
|
cmd = ["codex", "exec"]
|
|
289
332
|
cmd.append(prompt)
|
|
@@ -296,8 +339,20 @@ class CLIAdapter:
|
|
|
296
339
|
raise ValueError(
|
|
297
340
|
"Antigravity workflows are UI slash commands; CLI agent run is not supported."
|
|
298
341
|
)
|
|
342
|
+
elif self.platform == "windsurf":
|
|
343
|
+
raise ValueError(
|
|
344
|
+
"Windsurf workflows are UI slash commands; CLI agent run is not supported."
|
|
345
|
+
)
|
|
299
346
|
elif self.platform == "qoder":
|
|
300
347
|
cmd = ["qodercli", "-p", prompt]
|
|
348
|
+
elif self.platform == "codebuddy":
|
|
349
|
+
raise ValueError(
|
|
350
|
+
"CodeBuddy does not support non-interactive mode (no CLI agent)"
|
|
351
|
+
)
|
|
352
|
+
elif self.platform == "copilot":
|
|
353
|
+
raise ValueError(
|
|
354
|
+
"GitHub Copilot is IDE-only; CLI agent run is not supported."
|
|
355
|
+
)
|
|
301
356
|
|
|
302
357
|
else: # claude
|
|
303
358
|
cmd = ["claude", "-p"]
|
|
@@ -344,8 +399,20 @@ class CLIAdapter:
|
|
|
344
399
|
raise ValueError(
|
|
345
400
|
"Antigravity workflows are UI slash commands; CLI resume is not supported."
|
|
346
401
|
)
|
|
402
|
+
elif self.platform == "windsurf":
|
|
403
|
+
raise ValueError(
|
|
404
|
+
"Windsurf workflows are UI slash commands; CLI resume is not supported."
|
|
405
|
+
)
|
|
347
406
|
elif self.platform == "qoder":
|
|
348
407
|
return ["qodercli", "--resume", session_id]
|
|
408
|
+
elif self.platform == "codebuddy":
|
|
409
|
+
raise ValueError(
|
|
410
|
+
"CodeBuddy does not support non-interactive mode (no CLI agent)"
|
|
411
|
+
)
|
|
412
|
+
elif self.platform == "copilot":
|
|
413
|
+
raise ValueError(
|
|
414
|
+
"GitHub Copilot is IDE-only; CLI resume is not supported."
|
|
415
|
+
)
|
|
349
416
|
else:
|
|
350
417
|
return ["claude", "--resume", session_id]
|
|
351
418
|
|
|
@@ -408,8 +475,14 @@ class CLIAdapter:
|
|
|
408
475
|
return "gemini"
|
|
409
476
|
elif self.platform == "antigravity":
|
|
410
477
|
return "agy"
|
|
478
|
+
elif self.platform == "windsurf":
|
|
479
|
+
return "windsurf"
|
|
411
480
|
elif self.platform == "qoder":
|
|
412
481
|
return "qodercli"
|
|
482
|
+
elif self.platform == "codebuddy":
|
|
483
|
+
return "codebuddy"
|
|
484
|
+
elif self.platform == "copilot":
|
|
485
|
+
return "copilot"
|
|
413
486
|
else:
|
|
414
487
|
return "claude"
|
|
415
488
|
|
|
@@ -417,9 +490,18 @@ class CLIAdapter:
|
|
|
417
490
|
def supports_cli_agents(self) -> bool:
|
|
418
491
|
"""Check if platform supports running agents via CLI.
|
|
419
492
|
|
|
420
|
-
Claude Code, OpenCode, and
|
|
493
|
+
Claude Code, OpenCode, iFlow, and Codex support CLI agent execution.
|
|
421
494
|
Cursor is IDE-only and doesn't support CLI agents.
|
|
422
495
|
"""
|
|
496
|
+
return self.platform in ("claude", "opencode", "iflow", "codex")
|
|
497
|
+
|
|
498
|
+
@property
|
|
499
|
+
def requires_agent_definition_file(self) -> bool:
|
|
500
|
+
"""Check if platform requires an agent definition file (.md/.toml) to run.
|
|
501
|
+
|
|
502
|
+
Claude Code, OpenCode, iFlow: require agent .md files (--agent flag).
|
|
503
|
+
Codex: auto-discovers agents from .codex/agents/*.toml, no --agent flag.
|
|
504
|
+
"""
|
|
423
505
|
return self.platform in ("claude", "opencode", "iflow")
|
|
424
506
|
|
|
425
507
|
# =========================================================================
|
|
@@ -465,7 +547,7 @@ def get_cli_adapter(platform: str = "claude") -> CLIAdapter:
|
|
|
465
547
|
"""Get CLI adapter for the specified platform.
|
|
466
548
|
|
|
467
549
|
Args:
|
|
468
|
-
platform: Platform name ('claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity',
|
|
550
|
+
platform: Platform name ('claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', 'windsurf', 'qoder', or 'codebuddy')
|
|
469
551
|
|
|
470
552
|
Returns:
|
|
471
553
|
CLIAdapter instance
|
|
@@ -483,15 +565,46 @@ def get_cli_adapter(platform: str = "claude") -> CLIAdapter:
|
|
|
483
565
|
"kiro",
|
|
484
566
|
"gemini",
|
|
485
567
|
"antigravity",
|
|
568
|
+
"windsurf",
|
|
486
569
|
"qoder",
|
|
570
|
+
"codebuddy",
|
|
571
|
+
"copilot",
|
|
487
572
|
):
|
|
488
573
|
raise ValueError(
|
|
489
|
-
f"Unsupported platform: {platform} (must be 'claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity',
|
|
574
|
+
f"Unsupported platform: {platform} (must be 'claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', 'windsurf', 'qoder', 'codebuddy', or 'copilot')"
|
|
490
575
|
)
|
|
491
576
|
|
|
492
577
|
return CLIAdapter(platform=platform) # type: ignore
|
|
493
578
|
|
|
494
579
|
|
|
580
|
+
_ALL_PLATFORM_CONFIG_DIRS = (
|
|
581
|
+
".claude",
|
|
582
|
+
".cursor",
|
|
583
|
+
".iflow",
|
|
584
|
+
".opencode",
|
|
585
|
+
".agents",
|
|
586
|
+
".codex",
|
|
587
|
+
".kilocode",
|
|
588
|
+
".kiro",
|
|
589
|
+
".gemini",
|
|
590
|
+
".agent",
|
|
591
|
+
".windsurf",
|
|
592
|
+
".qoder",
|
|
593
|
+
".codebuddy",
|
|
594
|
+
".github/copilot",
|
|
595
|
+
)
|
|
596
|
+
"""All platform config directory names (used by detect_platform exclusion checks)."""
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
def _has_other_platform_dir(project_root: Path, exclude: set[str]) -> bool:
|
|
600
|
+
"""Check if any platform config dir exists besides those in *exclude*."""
|
|
601
|
+
return any(
|
|
602
|
+
(project_root / d).is_dir()
|
|
603
|
+
for d in _ALL_PLATFORM_CONFIG_DIRS
|
|
604
|
+
if d not in exclude
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
|
|
495
608
|
def detect_platform(project_root: Path) -> Platform:
|
|
496
609
|
"""Auto-detect platform based on existing config directories.
|
|
497
610
|
|
|
@@ -500,19 +613,21 @@ def detect_platform(project_root: Path) -> Platform:
|
|
|
500
613
|
2. .opencode directory exists → opencode
|
|
501
614
|
3. .iflow directory exists → iflow
|
|
502
615
|
4. .cursor directory exists (without .claude) → cursor
|
|
503
|
-
5. .
|
|
616
|
+
5. .codex exists and no other platform dirs → codex
|
|
504
617
|
6. .kilocode directory exists → kilo
|
|
505
618
|
7. .kiro/skills exists and no other platform dirs → kiro
|
|
506
619
|
8. .gemini directory exists → gemini
|
|
507
620
|
9. .agent/workflows exists and no other platform dirs → antigravity
|
|
508
|
-
10. .
|
|
509
|
-
11.
|
|
621
|
+
10. .windsurf/workflows exists and no other platform dirs → windsurf
|
|
622
|
+
11. .codebuddy directory exists → codebuddy
|
|
623
|
+
12. .qoder directory exists → qoder
|
|
624
|
+
13. Default → claude
|
|
510
625
|
|
|
511
626
|
Args:
|
|
512
627
|
project_root: Project root directory
|
|
513
628
|
|
|
514
629
|
Returns:
|
|
515
|
-
Detected platform ('claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity',
|
|
630
|
+
Detected platform ('claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', 'windsurf', 'qoder', 'codebuddy', or default 'claude')
|
|
516
631
|
"""
|
|
517
632
|
import os
|
|
518
633
|
|
|
@@ -528,17 +643,18 @@ def detect_platform(project_root: Path) -> Platform:
|
|
|
528
643
|
"kiro",
|
|
529
644
|
"gemini",
|
|
530
645
|
"antigravity",
|
|
646
|
+
"windsurf",
|
|
531
647
|
"qoder",
|
|
648
|
+
"codebuddy",
|
|
649
|
+
"copilot",
|
|
532
650
|
):
|
|
533
651
|
return env_platform # type: ignore
|
|
534
652
|
|
|
535
653
|
# Check for .opencode directory (OpenCode-specific)
|
|
536
|
-
# Note: .claude might exist in both platforms during migration
|
|
537
654
|
if (project_root / ".opencode").is_dir():
|
|
538
655
|
return "opencode"
|
|
539
656
|
|
|
540
657
|
# Check for .iflow directory (iFlow-specific)
|
|
541
|
-
# Note: .claude might exist in both platforms during migration
|
|
542
658
|
if (project_root / ".iflow").is_dir():
|
|
543
659
|
return "iflow"
|
|
544
660
|
|
|
@@ -551,21 +667,11 @@ def detect_platform(project_root: Path) -> Platform:
|
|
|
551
667
|
if (project_root / ".gemini").is_dir():
|
|
552
668
|
return "gemini"
|
|
553
669
|
|
|
554
|
-
# Check for
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
".
|
|
558
|
-
|
|
559
|
-
".opencode",
|
|
560
|
-
".kilocode",
|
|
561
|
-
".kiro",
|
|
562
|
-
".gemini",
|
|
563
|
-
".agent",
|
|
564
|
-
)
|
|
565
|
-
has_other_platform_config = any(
|
|
566
|
-
(project_root / directory).is_dir() for directory in other_platform_dirs_codex
|
|
567
|
-
)
|
|
568
|
-
if (project_root / ".agents" / "skills").is_dir() and not has_other_platform_config:
|
|
670
|
+
# Check for .codex directory (Codex-specific)
|
|
671
|
+
# .agents/skills/ alone does NOT trigger codex detection (it's a shared standard)
|
|
672
|
+
if (project_root / ".codex").is_dir() and not _has_other_platform_dir(
|
|
673
|
+
project_root, {".codex", ".agents"}
|
|
674
|
+
):
|
|
569
675
|
return "codex"
|
|
570
676
|
|
|
571
677
|
# Check for .kilocode directory (Kilo-specific)
|
|
@@ -573,45 +679,39 @@ def detect_platform(project_root: Path) -> Platform:
|
|
|
573
679
|
return "kilo"
|
|
574
680
|
|
|
575
681
|
# Check for Kiro skills directory only when no other platform config exists
|
|
576
|
-
|
|
577
|
-
".
|
|
578
|
-
|
|
579
|
-
".iflow",
|
|
580
|
-
".opencode",
|
|
581
|
-
".agents",
|
|
582
|
-
".kilocode",
|
|
583
|
-
".gemini",
|
|
584
|
-
".agent",
|
|
585
|
-
)
|
|
586
|
-
has_other_platform_config = any(
|
|
587
|
-
(project_root / directory).is_dir() for directory in other_platform_dirs_kiro
|
|
588
|
-
)
|
|
589
|
-
if (project_root / ".kiro" / "skills").is_dir() and not has_other_platform_config:
|
|
682
|
+
if (project_root / ".kiro" / "skills").is_dir() and not _has_other_platform_dir(
|
|
683
|
+
project_root, {".kiro"}
|
|
684
|
+
):
|
|
590
685
|
return "kiro"
|
|
591
686
|
|
|
592
687
|
# Check for Antigravity workflow directory only when no other platform config exists
|
|
593
|
-
other_platform_dirs_antigravity = (
|
|
594
|
-
".claude",
|
|
595
|
-
".cursor",
|
|
596
|
-
".iflow",
|
|
597
|
-
".opencode",
|
|
598
|
-
".agents",
|
|
599
|
-
".kilocode",
|
|
600
|
-
".kiro",
|
|
601
|
-
)
|
|
602
|
-
has_other_platform_config = any(
|
|
603
|
-
(project_root / directory).is_dir()
|
|
604
|
-
for directory in other_platform_dirs_antigravity
|
|
605
|
-
)
|
|
606
688
|
if (
|
|
607
689
|
project_root / ".agent" / "workflows"
|
|
608
|
-
).is_dir() and not
|
|
690
|
+
).is_dir() and not _has_other_platform_dir(
|
|
691
|
+
project_root, {".agent", ".gemini"}
|
|
692
|
+
):
|
|
609
693
|
return "antigravity"
|
|
610
694
|
|
|
695
|
+
# Check for Windsurf workflow directory only when no other platform config exists
|
|
696
|
+
if (
|
|
697
|
+
project_root / ".windsurf" / "workflows"
|
|
698
|
+
).is_dir() and not _has_other_platform_dir(
|
|
699
|
+
project_root, {".windsurf"}
|
|
700
|
+
):
|
|
701
|
+
return "windsurf"
|
|
702
|
+
|
|
703
|
+
# Check for .codebuddy directory (CodeBuddy-specific)
|
|
704
|
+
if (project_root / ".codebuddy").is_dir():
|
|
705
|
+
return "codebuddy"
|
|
706
|
+
|
|
611
707
|
# Check for .qoder directory (Qoder-specific)
|
|
612
708
|
if (project_root / ".qoder").is_dir():
|
|
613
709
|
return "qoder"
|
|
614
710
|
|
|
711
|
+
# Check for .github/copilot directory (GitHub Copilot-specific)
|
|
712
|
+
if (project_root / ".github" / "copilot").is_dir():
|
|
713
|
+
return "copilot"
|
|
714
|
+
|
|
615
715
|
return "claude"
|
|
616
716
|
|
|
617
717
|
|
|
@@ -7,6 +7,7 @@ Reads settings from .trellis/config.yaml with sensible defaults.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import sys
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
|
|
12
13
|
from .paths import DIR_WORKFLOW, get_repo_root
|
|
@@ -20,6 +21,15 @@ DEFAULT_MAX_JOURNAL_LINES = 2000
|
|
|
20
21
|
CONFIG_FILE = "config.yaml"
|
|
21
22
|
|
|
22
23
|
|
|
24
|
+
def _is_true_config_value(value: object) -> bool:
|
|
25
|
+
"""Return True when a config value represents an enabled flag."""
|
|
26
|
+
if isinstance(value, bool):
|
|
27
|
+
return value
|
|
28
|
+
if isinstance(value, str):
|
|
29
|
+
return value.strip().lower() == "true"
|
|
30
|
+
return False
|
|
31
|
+
|
|
32
|
+
|
|
23
33
|
def _get_config_path(repo_root: Path | None = None) -> Path:
|
|
24
34
|
"""Get path to config.yaml."""
|
|
25
35
|
root = repo_root or get_repo_root()
|
|
@@ -70,3 +80,185 @@ def get_hooks(event: str, repo_root: Path | None = None) -> list[str]:
|
|
|
70
80
|
if isinstance(commands, list):
|
|
71
81
|
return [str(c) for c in commands]
|
|
72
82
|
return []
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# =============================================================================
|
|
86
|
+
# Monorepo / Packages
|
|
87
|
+
# =============================================================================
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def get_packages(repo_root: Path | None = None) -> dict[str, dict] | None:
|
|
91
|
+
"""Get monorepo package declarations.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Dict mapping package name to its config (path, type, etc.),
|
|
95
|
+
or None if not configured (single-repo mode).
|
|
96
|
+
|
|
97
|
+
Example return:
|
|
98
|
+
{"cli": {"path": "packages/cli"}, "docs-site": {"path": "docs-site", "type": "submodule"}}
|
|
99
|
+
"""
|
|
100
|
+
config = _load_config(repo_root)
|
|
101
|
+
packages = config.get("packages")
|
|
102
|
+
if not isinstance(packages, dict):
|
|
103
|
+
return None
|
|
104
|
+
# Ensure each value is a dict (filter out scalar entries)
|
|
105
|
+
filtered = {k: v for k, v in packages.items() if isinstance(v, dict)}
|
|
106
|
+
if not filtered:
|
|
107
|
+
return None
|
|
108
|
+
return filtered
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def get_default_package(repo_root: Path | None = None) -> str | None:
|
|
112
|
+
"""Get the default package name from config.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
Package name string, or None if not configured.
|
|
116
|
+
"""
|
|
117
|
+
config = _load_config(repo_root)
|
|
118
|
+
value = config.get("default_package")
|
|
119
|
+
return str(value) if value else None
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def get_submodule_packages(repo_root: Path | None = None) -> dict[str, str]:
|
|
123
|
+
"""Get packages that are git submodules.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
Dict mapping package name to its path for submodule-type packages.
|
|
127
|
+
Empty dict if none configured.
|
|
128
|
+
|
|
129
|
+
Example return:
|
|
130
|
+
{"docs-site": "docs-site"}
|
|
131
|
+
"""
|
|
132
|
+
packages = get_packages(repo_root)
|
|
133
|
+
if packages is None:
|
|
134
|
+
return {}
|
|
135
|
+
return {
|
|
136
|
+
name: cfg.get("path", name)
|
|
137
|
+
for name, cfg in packages.items()
|
|
138
|
+
if cfg.get("type") == "submodule"
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def get_git_packages(repo_root: Path | None = None) -> dict[str, str]:
|
|
143
|
+
"""Get packages that have their own independent git repository.
|
|
144
|
+
|
|
145
|
+
These are sub-directories with their own .git (not submodules),
|
|
146
|
+
marked with ``git: true`` in config.yaml.
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Dict mapping package name to its path for git-repo packages.
|
|
150
|
+
Empty dict if none configured.
|
|
151
|
+
|
|
152
|
+
Example config::
|
|
153
|
+
|
|
154
|
+
packages:
|
|
155
|
+
backend:
|
|
156
|
+
path: iqs
|
|
157
|
+
git: true
|
|
158
|
+
|
|
159
|
+
Example return::
|
|
160
|
+
|
|
161
|
+
{"backend": "iqs"}
|
|
162
|
+
"""
|
|
163
|
+
packages = get_packages(repo_root)
|
|
164
|
+
if packages is None:
|
|
165
|
+
return {}
|
|
166
|
+
return {
|
|
167
|
+
name: cfg.get("path", name)
|
|
168
|
+
for name, cfg in packages.items()
|
|
169
|
+
if _is_true_config_value(cfg.get("git"))
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def is_monorepo(repo_root: Path | None = None) -> bool:
|
|
174
|
+
"""Check if the project is configured as a monorepo (has packages in config)."""
|
|
175
|
+
return get_packages(repo_root) is not None
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def get_spec_base(package: str | None = None, repo_root: Path | None = None) -> str:
|
|
179
|
+
"""Get the spec directory base path relative to .trellis/.
|
|
180
|
+
|
|
181
|
+
Single-repo: returns "spec"
|
|
182
|
+
Monorepo with package: returns "spec/<package>"
|
|
183
|
+
Monorepo without package: returns "spec" (caller should specify package)
|
|
184
|
+
"""
|
|
185
|
+
if package and is_monorepo(repo_root):
|
|
186
|
+
return f"spec/{package}"
|
|
187
|
+
return "spec"
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def validate_package(package: str, repo_root: Path | None = None) -> bool:
|
|
191
|
+
"""Check if a package name is valid in this project.
|
|
192
|
+
|
|
193
|
+
Single-repo (no packages configured): always returns True.
|
|
194
|
+
Monorepo: returns True only if package exists in config.yaml packages.
|
|
195
|
+
"""
|
|
196
|
+
packages = get_packages(repo_root)
|
|
197
|
+
if packages is None:
|
|
198
|
+
return True # Single-repo, no validation needed
|
|
199
|
+
return package in packages
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def resolve_package(
|
|
203
|
+
task_package: str | None = None,
|
|
204
|
+
repo_root: Path | None = None,
|
|
205
|
+
) -> str | None:
|
|
206
|
+
"""Resolve package from inferred sources with validation.
|
|
207
|
+
|
|
208
|
+
Checks in order: task_package → default_package.
|
|
209
|
+
Invalid inferred values print a warning to stderr and are skipped.
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
Resolved package name, or None if no valid package found.
|
|
213
|
+
|
|
214
|
+
Note:
|
|
215
|
+
CLI --package should be validated separately by the caller
|
|
216
|
+
(fail-fast with available packages list on error).
|
|
217
|
+
"""
|
|
218
|
+
packages = get_packages(repo_root)
|
|
219
|
+
if packages is None:
|
|
220
|
+
return None # Single-repo, no package needed
|
|
221
|
+
|
|
222
|
+
# Try task_package (guard against non-string values from malformed JSON)
|
|
223
|
+
if task_package and isinstance(task_package, str):
|
|
224
|
+
if task_package in packages:
|
|
225
|
+
return task_package
|
|
226
|
+
print(
|
|
227
|
+
f"Warning: task.json package '{task_package}' not found in config, skipping",
|
|
228
|
+
file=sys.stderr,
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
# Try default_package
|
|
232
|
+
default = get_default_package(repo_root)
|
|
233
|
+
if default:
|
|
234
|
+
if default in packages:
|
|
235
|
+
return default
|
|
236
|
+
print(
|
|
237
|
+
f"Warning: default_package '{default}' not found in config, skipping",
|
|
238
|
+
file=sys.stderr,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
return None
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def get_spec_scope(repo_root: Path | None = None) -> list[str] | str | None:
|
|
245
|
+
"""Get session.spec_scope configuration.
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
list[str]: Package names to include in spec scanning.
|
|
249
|
+
str: "active_task" to use current task's package.
|
|
250
|
+
None: No scope configured (scan all packages).
|
|
251
|
+
"""
|
|
252
|
+
config = _load_config(repo_root)
|
|
253
|
+
session = config.get("session")
|
|
254
|
+
if not isinstance(session, dict):
|
|
255
|
+
return None
|
|
256
|
+
|
|
257
|
+
scope = session.get("spec_scope")
|
|
258
|
+
if scope is None:
|
|
259
|
+
return None
|
|
260
|
+
if isinstance(scope, str):
|
|
261
|
+
return scope # e.g. "active_task"
|
|
262
|
+
if isinstance(scope, list):
|
|
263
|
+
return [str(s) for s in scope]
|
|
264
|
+
return None
|
|
@@ -123,8 +123,8 @@ def init_developer(name: str, repo_root: Path | None = None) -> bool:
|
|
|
123
123
|
## Session History
|
|
124
124
|
|
|
125
125
|
<!-- @@@auto:session-history -->
|
|
126
|
-
| # | Date | Title | Commits |
|
|
127
|
-
|
|
126
|
+
| # | Date | Title | Commits | Branch |
|
|
127
|
+
|---|------|-------|---------|--------|
|
|
128
128
|
<!-- @@@/auto:session-history -->
|
|
129
129
|
|
|
130
130
|
---
|