@jahanxu/trellis 0.4.1
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/LICENSE +235 -0
- package/README.md +212 -0
- package/bin/trellis.js +3 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +97 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/commands/init.d.ts +21 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +527 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/update.d.ts +27 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +1289 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/configurators/antigravity.d.ts +8 -0
- package/dist/configurators/antigravity.d.ts.map +1 -0
- package/dist/configurators/antigravity.js +18 -0
- package/dist/configurators/antigravity.js.map +1 -0
- package/dist/configurators/claude.d.ts +32 -0
- package/dist/configurators/claude.d.ts.map +1 -0
- package/dist/configurators/claude.js +98 -0
- package/dist/configurators/claude.js.map +1 -0
- package/dist/configurators/codex.d.ts +8 -0
- package/dist/configurators/codex.d.ts.map +1 -0
- package/dist/configurators/codex.js +20 -0
- package/dist/configurators/codex.js.map +1 -0
- package/dist/configurators/cursor.d.ts +5 -0
- package/dist/configurators/cursor.d.ts.map +1 -0
- package/dist/configurators/cursor.js +52 -0
- package/dist/configurators/cursor.js.map +1 -0
- package/dist/configurators/gemini.d.ts +8 -0
- package/dist/configurators/gemini.d.ts.map +1 -0
- package/dist/configurators/gemini.js +52 -0
- package/dist/configurators/gemini.js.map +1 -0
- package/dist/configurators/iflow.d.ts +33 -0
- package/dist/configurators/iflow.d.ts.map +1 -0
- package/dist/configurators/iflow.js +99 -0
- package/dist/configurators/iflow.js.map +1 -0
- package/dist/configurators/index.d.ts +55 -0
- package/dist/configurators/index.d.ts.map +1 -0
- package/dist/configurators/index.js +220 -0
- package/dist/configurators/index.js.map +1 -0
- package/dist/configurators/kilo.d.ts +8 -0
- package/dist/configurators/kilo.d.ts.map +1 -0
- package/dist/configurators/kilo.js +51 -0
- package/dist/configurators/kilo.js.map +1 -0
- package/dist/configurators/kiro.d.ts +8 -0
- package/dist/configurators/kiro.d.ts.map +1 -0
- package/dist/configurators/kiro.js +20 -0
- package/dist/configurators/kiro.js.map +1 -0
- package/dist/configurators/opencode.d.ts +32 -0
- package/dist/configurators/opencode.d.ts.map +1 -0
- package/dist/configurators/opencode.js +92 -0
- package/dist/configurators/opencode.js.map +1 -0
- package/dist/configurators/shared.d.ts +12 -0
- package/dist/configurators/shared.d.ts.map +1 -0
- package/dist/configurators/shared.js +21 -0
- package/dist/configurators/shared.js.map +1 -0
- package/dist/configurators/workflow.d.ts +28 -0
- package/dist/configurators/workflow.d.ts.map +1 -0
- package/dist/configurators/workflow.js +134 -0
- package/dist/configurators/workflow.js.map +1 -0
- package/dist/constants/paths.d.ts +68 -0
- package/dist/constants/paths.d.ts.map +1 -0
- package/dist/constants/paths.js +77 -0
- package/dist/constants/paths.js.map +1 -0
- package/dist/constants/version.d.ts +9 -0
- package/dist/constants/version.d.ts.map +1 -0
- package/dist/constants/version.js +15 -0
- package/dist/constants/version.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations/index.d.ts +54 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +160 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/migrations/manifests/0.1.9.json +30 -0
- package/dist/migrations/manifests/0.2.0.json +49 -0
- package/dist/migrations/manifests/0.2.12.json +9 -0
- package/dist/migrations/manifests/0.2.13.json +9 -0
- package/dist/migrations/manifests/0.2.14.json +175 -0
- package/dist/migrations/manifests/0.2.15.json +33 -0
- package/dist/migrations/manifests/0.3.0-beta.0.json +278 -0
- package/dist/migrations/manifests/0.3.0-beta.1.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.10.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.11.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.12.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.13.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.14.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.15.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.16.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.2.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.3.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.4.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.5.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.6.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.7.json +11 -0
- package/dist/migrations/manifests/0.3.0-beta.8.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.9.json +9 -0
- package/dist/migrations/manifests/0.3.0-rc.0.json +9 -0
- package/dist/migrations/manifests/0.3.0-rc.1.json +9 -0
- package/dist/migrations/manifests/0.3.0-rc.2.json +9 -0
- package/dist/migrations/manifests/0.3.0-rc.3.json +9 -0
- package/dist/migrations/manifests/0.3.0-rc.4.json +9 -0
- package/dist/migrations/manifests/0.3.0-rc.5.json +9 -0
- package/dist/migrations/manifests/0.3.0-rc.6.json +9 -0
- package/dist/migrations/manifests/0.3.0.json +11 -0
- package/dist/templates/antigravity/index.d.ts +12 -0
- package/dist/templates/antigravity/index.d.ts.map +1 -0
- package/dist/templates/antigravity/index.js +29 -0
- package/dist/templates/antigravity/index.js.map +1 -0
- package/dist/templates/claude/agents/check.md +122 -0
- package/dist/templates/claude/agents/debug.md +106 -0
- package/dist/templates/claude/agents/dispatch.md +214 -0
- package/dist/templates/claude/agents/implement.md +96 -0
- package/dist/templates/claude/agents/plan.md +396 -0
- package/dist/templates/claude/agents/research.md +120 -0
- package/dist/templates/claude/commands/trellis/before-backend-dev.md +13 -0
- package/dist/templates/claude/commands/trellis/before-frontend-dev.md +13 -0
- package/dist/templates/claude/commands/trellis/before-role-work.md +364 -0
- package/dist/templates/claude/commands/trellis/brainstorm.md +474 -0
- package/dist/templates/claude/commands/trellis/break-loop.md +125 -0
- package/dist/templates/claude/commands/trellis/check-backend.md +13 -0
- package/dist/templates/claude/commands/trellis/check-cross-layer.md +153 -0
- package/dist/templates/claude/commands/trellis/check-frontend.md +13 -0
- package/dist/templates/claude/commands/trellis/create-command.md +154 -0
- package/dist/templates/claude/commands/trellis/finish-work.md +153 -0
- package/dist/templates/claude/commands/trellis/handoff.md +445 -0
- package/dist/templates/claude/commands/trellis/integrate-skill.md +219 -0
- package/dist/templates/claude/commands/trellis/onboard.md +358 -0
- package/dist/templates/claude/commands/trellis/parallel.md +193 -0
- package/dist/templates/claude/commands/trellis/pick-task.md +515 -0
- package/dist/templates/claude/commands/trellis/record-session.md +62 -0
- package/dist/templates/claude/commands/trellis/start.md +373 -0
- package/dist/templates/claude/commands/trellis/update-spec.md +354 -0
- package/dist/templates/claude/hooks/inject-subagent-context.py +873 -0
- package/dist/templates/claude/hooks/ralph-loop.py +388 -0
- package/dist/templates/claude/hooks/session-start.py +200 -0
- package/dist/templates/claude/index.d.ts +54 -0
- package/dist/templates/claude/index.d.ts.map +1 -0
- package/dist/templates/claude/index.js +85 -0
- package/dist/templates/claude/index.js.map +1 -0
- package/dist/templates/claude/settings.json +41 -0
- package/dist/templates/codex/index.d.ts +18 -0
- package/dist/templates/codex/index.d.ts.map +1 -0
- package/dist/templates/codex/index.js +40 -0
- package/dist/templates/codex/index.js.map +1 -0
- package/dist/templates/codex/skills/before-backend-dev/SKILL.md +18 -0
- package/dist/templates/codex/skills/before-frontend-dev/SKILL.md +18 -0
- package/dist/templates/codex/skills/brainstorm/SKILL.md +479 -0
- package/dist/templates/codex/skills/break-loop/SKILL.md +130 -0
- package/dist/templates/codex/skills/check-backend/SKILL.md +18 -0
- package/dist/templates/codex/skills/check-cross-layer/SKILL.md +158 -0
- package/dist/templates/codex/skills/check-frontend/SKILL.md +18 -0
- package/dist/templates/codex/skills/create-command/SKILL.md +101 -0
- package/dist/templates/codex/skills/finish-work/SKILL.md +148 -0
- package/dist/templates/codex/skills/integrate-skill/SKILL.md +221 -0
- package/dist/templates/codex/skills/onboard/SKILL.md +363 -0
- package/dist/templates/codex/skills/record-session/SKILL.md +67 -0
- package/dist/templates/codex/skills/start/SKILL.md +330 -0
- package/dist/templates/codex/skills/update-spec/SKILL.md +335 -0
- package/dist/templates/cursor/commands/trellis-before-backend-dev.md +13 -0
- package/dist/templates/cursor/commands/trellis-before-frontend-dev.md +13 -0
- package/dist/templates/cursor/commands/trellis-brainstorm.md +474 -0
- package/dist/templates/cursor/commands/trellis-break-loop.md +107 -0
- package/dist/templates/cursor/commands/trellis-check-backend.md +13 -0
- package/dist/templates/cursor/commands/trellis-check-cross-layer.md +153 -0
- package/dist/templates/cursor/commands/trellis-check-frontend.md +13 -0
- package/dist/templates/cursor/commands/trellis-create-command.md +154 -0
- package/dist/templates/cursor/commands/trellis-finish-work.md +143 -0
- package/dist/templates/cursor/commands/trellis-integrate-skill.md +219 -0
- package/dist/templates/cursor/commands/trellis-onboard.md +358 -0
- package/dist/templates/cursor/commands/trellis-record-session.md +62 -0
- package/dist/templates/cursor/commands/trellis-start.md +366 -0
- package/dist/templates/cursor/commands/trellis-update-spec.md +354 -0
- package/dist/templates/cursor/index.d.ts +24 -0
- package/dist/templates/cursor/index.d.ts.map +1 -0
- package/dist/templates/cursor/index.js +44 -0
- package/dist/templates/cursor/index.js.map +1 -0
- package/dist/templates/extract.d.ts +166 -0
- package/dist/templates/extract.d.ts.map +1 -0
- package/dist/templates/extract.js +296 -0
- package/dist/templates/extract.js.map +1 -0
- package/dist/templates/gemini/commands/trellis/before-backend-dev.toml +17 -0
- package/dist/templates/gemini/commands/trellis/before-frontend-dev.toml +17 -0
- package/dist/templates/gemini/commands/trellis/brainstorm.toml +420 -0
- package/dist/templates/gemini/commands/trellis/break-loop.toml +129 -0
- package/dist/templates/gemini/commands/trellis/check-backend.toml +17 -0
- package/dist/templates/gemini/commands/trellis/check-cross-layer.toml +147 -0
- package/dist/templates/gemini/commands/trellis/check-frontend.toml +17 -0
- package/dist/templates/gemini/commands/trellis/create-command.toml +119 -0
- package/dist/templates/gemini/commands/trellis/finish-work.toml +133 -0
- package/dist/templates/gemini/commands/trellis/integrate-skill.toml +104 -0
- package/dist/templates/gemini/commands/trellis/onboard.toml +111 -0
- package/dist/templates/gemini/commands/trellis/record-session.toml +66 -0
- package/dist/templates/gemini/commands/trellis/start.toml +292 -0
- package/dist/templates/gemini/commands/trellis/update-spec.toml +132 -0
- package/dist/templates/gemini/index.d.ts +21 -0
- package/dist/templates/gemini/index.d.ts.map +1 -0
- package/dist/templates/gemini/index.js +44 -0
- package/dist/templates/gemini/index.js.map +1 -0
- package/dist/templates/iflow/agents/check.md +122 -0
- package/dist/templates/iflow/agents/debug.md +106 -0
- package/dist/templates/iflow/agents/dispatch.md +214 -0
- package/dist/templates/iflow/agents/implement.md +96 -0
- package/dist/templates/iflow/agents/plan.md +396 -0
- package/dist/templates/iflow/agents/research.md +120 -0
- package/dist/templates/iflow/commands/trellis/before-backend-dev.md +13 -0
- package/dist/templates/iflow/commands/trellis/before-frontend-dev.md +13 -0
- package/dist/templates/iflow/commands/trellis/brainstorm.md +474 -0
- package/dist/templates/iflow/commands/trellis/break-loop.md +125 -0
- package/dist/templates/iflow/commands/trellis/check-backend.md +13 -0
- package/dist/templates/iflow/commands/trellis/check-cross-layer.md +153 -0
- package/dist/templates/iflow/commands/trellis/check-frontend.md +13 -0
- package/dist/templates/iflow/commands/trellis/create-command.md +152 -0
- package/dist/templates/iflow/commands/trellis/finish-work.md +153 -0
- package/dist/templates/iflow/commands/trellis/integrate-skill.md +219 -0
- package/dist/templates/iflow/commands/trellis/onboard.md +358 -0
- package/dist/templates/iflow/commands/trellis/parallel.md +193 -0
- package/dist/templates/iflow/commands/trellis/record-session.md +62 -0
- package/dist/templates/iflow/commands/trellis/start.md +373 -0
- package/dist/templates/iflow/commands/trellis/update-spec.md +354 -0
- package/dist/templates/iflow/hooks/inject-subagent-context.py +788 -0
- package/dist/templates/iflow/hooks/ralph-loop.py +388 -0
- package/dist/templates/iflow/hooks/session-start.py +143 -0
- package/dist/templates/iflow/index.d.ts +54 -0
- package/dist/templates/iflow/index.d.ts.map +1 -0
- package/dist/templates/iflow/index.js +85 -0
- package/dist/templates/iflow/index.js.map +1 -0
- package/dist/templates/iflow/settings.json +40 -0
- package/dist/templates/kilo/commands/trellis/before-backend-dev.md +13 -0
- package/dist/templates/kilo/commands/trellis/before-frontend-dev.md +13 -0
- package/dist/templates/kilo/commands/trellis/brainstorm.md +474 -0
- package/dist/templates/kilo/commands/trellis/break-loop.md +125 -0
- package/dist/templates/kilo/commands/trellis/check-backend.md +13 -0
- package/dist/templates/kilo/commands/trellis/check-cross-layer.md +153 -0
- package/dist/templates/kilo/commands/trellis/check-frontend.md +13 -0
- package/dist/templates/kilo/commands/trellis/create-command.md +152 -0
- package/dist/templates/kilo/commands/trellis/finish-work.md +129 -0
- package/dist/templates/kilo/commands/trellis/integrate-skill.md +219 -0
- package/dist/templates/kilo/commands/trellis/onboard.md +358 -0
- package/dist/templates/kilo/commands/trellis/parallel.md +194 -0
- package/dist/templates/kilo/commands/trellis/record-session.md +62 -0
- package/dist/templates/kilo/commands/trellis/start.md +321 -0
- package/dist/templates/kilo/commands/trellis/update-spec.md +285 -0
- package/dist/templates/kilo/index.d.ts +16 -0
- package/dist/templates/kilo/index.d.ts.map +1 -0
- package/dist/templates/kilo/index.js +39 -0
- package/dist/templates/kilo/index.js.map +1 -0
- package/dist/templates/kiro/index.d.ts +18 -0
- package/dist/templates/kiro/index.d.ts.map +1 -0
- package/dist/templates/kiro/index.js +40 -0
- package/dist/templates/kiro/index.js.map +1 -0
- package/dist/templates/kiro/skills/before-backend-dev/SKILL.md +18 -0
- package/dist/templates/kiro/skills/before-frontend-dev/SKILL.md +18 -0
- package/dist/templates/kiro/skills/brainstorm/SKILL.md +479 -0
- package/dist/templates/kiro/skills/break-loop/SKILL.md +130 -0
- package/dist/templates/kiro/skills/check-backend/SKILL.md +18 -0
- package/dist/templates/kiro/skills/check-cross-layer/SKILL.md +158 -0
- package/dist/templates/kiro/skills/check-frontend/SKILL.md +18 -0
- package/dist/templates/kiro/skills/create-command/SKILL.md +101 -0
- package/dist/templates/kiro/skills/finish-work/SKILL.md +148 -0
- package/dist/templates/kiro/skills/integrate-skill/SKILL.md +221 -0
- package/dist/templates/kiro/skills/onboard/SKILL.md +363 -0
- package/dist/templates/kiro/skills/record-session/SKILL.md +67 -0
- package/dist/templates/kiro/skills/start/SKILL.md +330 -0
- package/dist/templates/kiro/skills/update-spec/SKILL.md +335 -0
- package/dist/templates/markdown/agents.md +18 -0
- package/dist/templates/markdown/gitignore.txt +12 -0
- package/dist/templates/markdown/index.d.ts +27 -0
- package/dist/templates/markdown/index.d.ts.map +1 -0
- package/dist/templates/markdown/index.js +52 -0
- package/dist/templates/markdown/index.js.map +1 -0
- package/dist/templates/markdown/spec/backend/database-guidelines.md.txt +51 -0
- package/dist/templates/markdown/spec/backend/directory-structure.md.txt +54 -0
- package/dist/templates/markdown/spec/backend/error-handling.md.txt +51 -0
- package/dist/templates/markdown/spec/backend/index.md +40 -0
- package/dist/templates/markdown/spec/backend/index.md.txt +38 -0
- package/dist/templates/markdown/spec/backend/logging-guidelines.md.txt +51 -0
- package/dist/templates/markdown/spec/backend/quality-guidelines.md.txt +51 -0
- package/dist/templates/markdown/spec/backend/script-conventions.md +467 -0
- package/dist/templates/markdown/spec/frontend/component-guidelines.md.txt +59 -0
- package/dist/templates/markdown/spec/frontend/directory-structure.md.txt +54 -0
- package/dist/templates/markdown/spec/frontend/hook-guidelines.md.txt +51 -0
- package/dist/templates/markdown/spec/frontend/index.md.txt +39 -0
- package/dist/templates/markdown/spec/frontend/quality-guidelines.md.txt +51 -0
- package/dist/templates/markdown/spec/frontend/state-management.md.txt +51 -0
- package/dist/templates/markdown/spec/frontend/type-safety.md.txt +51 -0
- package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md +118 -0
- package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md.txt +92 -0
- package/dist/templates/markdown/spec/guides/cross-layer-thinking-guide.md.txt +94 -0
- package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md +394 -0
- package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +319 -0
- package/dist/templates/markdown/spec/guides/index.md.txt +79 -0
- package/dist/templates/markdown/workspace-index.md +123 -0
- package/dist/templates/markdown/worktree.yaml.txt +58 -0
- package/dist/templates/opencode/agents/check.md +146 -0
- package/dist/templates/opencode/agents/debug.md +129 -0
- package/dist/templates/opencode/agents/dispatch.md +223 -0
- package/dist/templates/opencode/agents/implement.md +120 -0
- package/dist/templates/opencode/agents/research.md +147 -0
- package/dist/templates/opencode/agents/trellis-plan.md +427 -0
- package/dist/templates/opencode/commands/trellis/before-backend-dev.md +13 -0
- package/dist/templates/opencode/commands/trellis/before-frontend-dev.md +13 -0
- package/dist/templates/opencode/commands/trellis/brainstorm.md +474 -0
- package/dist/templates/opencode/commands/trellis/break-loop.md +125 -0
- package/dist/templates/opencode/commands/trellis/check-backend.md +13 -0
- package/dist/templates/opencode/commands/trellis/check-cross-layer.md +153 -0
- package/dist/templates/opencode/commands/trellis/check-frontend.md +13 -0
- package/dist/templates/opencode/commands/trellis/create-command.md +154 -0
- package/dist/templates/opencode/commands/trellis/finish-work.md +144 -0
- package/dist/templates/opencode/commands/trellis/integrate-skill.md +219 -0
- package/dist/templates/opencode/commands/trellis/migrate-specs.md +0 -0
- package/dist/templates/opencode/commands/trellis/onboard.md +358 -0
- package/dist/templates/opencode/commands/trellis/parallel.md +194 -0
- package/dist/templates/opencode/commands/trellis/record-session.md +62 -0
- package/dist/templates/opencode/commands/trellis/start.md +338 -0
- package/dist/templates/opencode/commands/trellis/update-spec.md +354 -0
- package/dist/templates/opencode/lib/trellis-context.js +436 -0
- package/dist/templates/opencode/package.json +5 -0
- package/dist/templates/opencode/plugin/inject-subagent-context.js +538 -0
- package/dist/templates/opencode/plugin/session-start.js +192 -0
- package/dist/templates/trellis/VERSION +1 -0
- package/dist/templates/trellis/deliverables/README.md +51 -0
- package/dist/templates/trellis/gitignore.txt +29 -0
- package/dist/templates/trellis/index.d.ts +49 -0
- package/dist/templates/trellis/index.d.ts.map +1 -0
- package/dist/templates/trellis/index.js +92 -0
- package/dist/templates/trellis/index.js.map +1 -0
- package/dist/templates/trellis/paths.README.md +277 -0
- package/dist/templates/trellis/paths.yaml +41 -0
- package/dist/templates/trellis/pool/implementations.json +5 -0
- package/dist/templates/trellis/pool/prototypes.json +5 -0
- package/dist/templates/trellis/pool/requirements.json +5 -0
- package/dist/templates/trellis/scripts/__init__.py +5 -0
- package/dist/templates/trellis/scripts/add_session.py +391 -0
- package/dist/templates/trellis/scripts/common/__init__.py +80 -0
- package/dist/templates/trellis/scripts/common/cli_adapter.py +522 -0
- package/dist/templates/trellis/scripts/common/developer.py +189 -0
- package/dist/templates/trellis/scripts/common/git_context.py +383 -0
- package/dist/templates/trellis/scripts/common/paths.py +346 -0
- package/dist/templates/trellis/scripts/common/phase.py +253 -0
- package/dist/templates/trellis/scripts/common/project_paths.py +189 -0
- package/dist/templates/trellis/scripts/common/registry.py +365 -0
- package/dist/templates/trellis/scripts/common/task_queue.py +255 -0
- package/dist/templates/trellis/scripts/common/task_utils.py +177 -0
- package/dist/templates/trellis/scripts/common/worktree.py +218 -0
- package/dist/templates/trellis/scripts/create_bootstrap.py +290 -0
- package/dist/templates/trellis/scripts/get_context.py +16 -0
- package/dist/templates/trellis/scripts/get_developer.py +26 -0
- package/dist/templates/trellis/scripts/handoff_generator.py +380 -0
- package/dist/templates/trellis/scripts/init_developer.py +51 -0
- package/dist/templates/trellis/scripts/multi_agent/__init__.py +5 -0
- package/dist/templates/trellis/scripts/multi_agent/cleanup.py +403 -0
- package/dist/templates/trellis/scripts/multi_agent/create_pr.py +329 -0
- package/dist/templates/trellis/scripts/multi_agent/plan.py +233 -0
- package/dist/templates/trellis/scripts/multi_agent/start.py +461 -0
- package/dist/templates/trellis/scripts/multi_agent/status.py +817 -0
- package/dist/templates/trellis/scripts/pool.py +373 -0
- package/dist/templates/trellis/scripts/task.py +1162 -0
- package/dist/templates/trellis/scripts-shell-archive/add-session.sh +384 -0
- package/dist/templates/trellis/scripts-shell-archive/common/developer.sh +129 -0
- package/dist/templates/trellis/scripts-shell-archive/common/git-context.sh +263 -0
- package/dist/templates/trellis/scripts-shell-archive/common/paths.sh +208 -0
- package/dist/templates/trellis/scripts-shell-archive/common/phase.sh +150 -0
- package/dist/templates/trellis/scripts-shell-archive/common/registry.sh +247 -0
- package/dist/templates/trellis/scripts-shell-archive/common/task-queue.sh +142 -0
- package/dist/templates/trellis/scripts-shell-archive/common/task-utils.sh +151 -0
- package/dist/templates/trellis/scripts-shell-archive/common/worktree.sh +128 -0
- package/dist/templates/trellis/scripts-shell-archive/create-bootstrap.sh +299 -0
- package/dist/templates/trellis/scripts-shell-archive/get-context.sh +7 -0
- package/dist/templates/trellis/scripts-shell-archive/get-developer.sh +15 -0
- package/dist/templates/trellis/scripts-shell-archive/init-developer.sh +34 -0
- package/dist/templates/trellis/scripts-shell-archive/multi-agent/cleanup.sh +396 -0
- package/dist/templates/trellis/scripts-shell-archive/multi-agent/create-pr.sh +241 -0
- package/dist/templates/trellis/scripts-shell-archive/multi-agent/plan.sh +207 -0
- package/dist/templates/trellis/scripts-shell-archive/multi-agent/start.sh +317 -0
- package/dist/templates/trellis/scripts-shell-archive/multi-agent/status.sh +828 -0
- package/dist/templates/trellis/scripts-shell-archive/task.sh +1204 -0
- package/dist/templates/trellis/spec/roles/designer/index.md +243 -0
- package/dist/templates/trellis/spec/roles/designer/mock-data-standards.md +481 -0
- package/dist/templates/trellis/spec/roles/designer/prototype-guidelines.md +429 -0
- package/dist/templates/trellis/spec/roles/frontend-impl/api-integration.md +565 -0
- package/dist/templates/trellis/spec/roles/frontend-impl/index.md +321 -0
- package/dist/templates/trellis/spec/roles/frontend-impl/state-management.md +599 -0
- package/dist/templates/trellis/spec/roles/pm/index.md +112 -0
- package/dist/templates/trellis/spec/roles/pm/prd-template.md +124 -0
- package/dist/templates/trellis/tasks/.gitkeep +0 -0
- package/dist/templates/trellis/workflow.md +416 -0
- package/dist/templates/trellis/worktree.yaml +47 -0
- package/dist/types/ai-tools.d.ts +56 -0
- package/dist/types/ai-tools.d.ts.map +1 -0
- package/dist/types/ai-tools.js +103 -0
- package/dist/types/ai-tools.js.map +1 -0
- package/dist/types/migration.d.ts +86 -0
- package/dist/types/migration.d.ts.map +1 -0
- package/dist/types/migration.js +8 -0
- package/dist/types/migration.js.map +1 -0
- package/dist/utils/compare-versions.d.ts +12 -0
- package/dist/utils/compare-versions.d.ts.map +1 -0
- package/dist/utils/compare-versions.js +76 -0
- package/dist/utils/compare-versions.js.map +1 -0
- package/dist/utils/file-writer.d.ts +23 -0
- package/dist/utils/file-writer.d.ts.map +1 -0
- package/dist/utils/file-writer.js +140 -0
- package/dist/utils/file-writer.js.map +1 -0
- package/dist/utils/project-detector.d.ts +16 -0
- package/dist/utils/project-detector.d.ts.map +1 -0
- package/dist/utils/project-detector.js +186 -0
- package/dist/utils/project-detector.js.map +1 -0
- package/dist/utils/template-fetcher.d.ts +51 -0
- package/dist/utils/template-fetcher.d.ts.map +1 -0
- package/dist/utils/template-fetcher.js +174 -0
- package/dist/utils/template-fetcher.js.map +1 -0
- package/dist/utils/template-hash.d.ts +78 -0
- package/dist/utils/template-hash.d.ts.map +1 -0
- package/dist/utils/template-hash.js +233 -0
- package/dist/utils/template-hash.js.map +1 -0
- package/package.json +87 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Task queue utility functions.
|
|
4
|
+
|
|
5
|
+
Provides:
|
|
6
|
+
list_tasks_by_status - List tasks by status
|
|
7
|
+
list_pending_tasks - List tasks with pending status
|
|
8
|
+
list_tasks_by_assignee - List tasks by assignee
|
|
9
|
+
list_my_tasks - List tasks assigned to current developer
|
|
10
|
+
get_task_stats - Get P0/P1/P2/P3 counts
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Optional, List
|
|
17
|
+
|
|
18
|
+
from .paths import (
|
|
19
|
+
FILE_TASK_JSON,
|
|
20
|
+
get_repo_root,
|
|
21
|
+
get_developer,
|
|
22
|
+
get_tasks_dir,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _read_json_file(path: Path) -> Optional[dict]:
|
|
27
|
+
"""Read and parse a JSON file."""
|
|
28
|
+
try:
|
|
29
|
+
return json.loads(path.read_text(encoding="utf-8"))
|
|
30
|
+
except (FileNotFoundError, json.JSONDecodeError, OSError):
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# =============================================================================
|
|
35
|
+
# Public Functions
|
|
36
|
+
# =============================================================================
|
|
37
|
+
|
|
38
|
+
def list_tasks_by_status(
|
|
39
|
+
filter_status: Optional[str] = None,
|
|
40
|
+
repo_root: Optional[Path] = None
|
|
41
|
+
) -> list[dict]:
|
|
42
|
+
"""List tasks by status.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
filter_status: Optional status filter.
|
|
46
|
+
repo_root: Repository root path. Defaults to auto-detected.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
List of task info dicts with keys: priority, id, title, status, assignee.
|
|
50
|
+
"""
|
|
51
|
+
if repo_root is None:
|
|
52
|
+
repo_root = get_repo_root()
|
|
53
|
+
|
|
54
|
+
tasks_dir = get_tasks_dir(repo_root)
|
|
55
|
+
results = []
|
|
56
|
+
|
|
57
|
+
if not tasks_dir.is_dir():
|
|
58
|
+
return results
|
|
59
|
+
|
|
60
|
+
for d in tasks_dir.iterdir():
|
|
61
|
+
if not d.is_dir() or d.name == "archive":
|
|
62
|
+
continue
|
|
63
|
+
|
|
64
|
+
task_json = d / FILE_TASK_JSON
|
|
65
|
+
if not task_json.is_file():
|
|
66
|
+
continue
|
|
67
|
+
|
|
68
|
+
data = _read_json_file(task_json)
|
|
69
|
+
if not data:
|
|
70
|
+
continue
|
|
71
|
+
|
|
72
|
+
task_id = data.get("id", "")
|
|
73
|
+
title = data.get("title") or data.get("name", "")
|
|
74
|
+
priority = data.get("priority", "P2")
|
|
75
|
+
status = data.get("status", "planning")
|
|
76
|
+
assignee = data.get("assignee", "-")
|
|
77
|
+
|
|
78
|
+
# Apply filter
|
|
79
|
+
if filter_status and status != filter_status:
|
|
80
|
+
continue
|
|
81
|
+
|
|
82
|
+
results.append({
|
|
83
|
+
"priority": priority,
|
|
84
|
+
"id": task_id,
|
|
85
|
+
"title": title,
|
|
86
|
+
"status": status,
|
|
87
|
+
"assignee": assignee,
|
|
88
|
+
"dir": d.name,
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
return results
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def list_pending_tasks(repo_root: Optional[Path] = None) -> list[dict]:
|
|
95
|
+
"""List pending tasks.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
repo_root: Repository root path. Defaults to auto-detected.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
List of task info dicts.
|
|
102
|
+
"""
|
|
103
|
+
return list_tasks_by_status("planning", repo_root)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def list_tasks_by_assignee(
|
|
107
|
+
assignee: str,
|
|
108
|
+
filter_status: Optional[str] = None,
|
|
109
|
+
repo_root: Optional[Path] = None
|
|
110
|
+
) -> list[dict]:
|
|
111
|
+
"""List tasks assigned to a specific developer.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
assignee: Developer name.
|
|
115
|
+
filter_status: Optional status filter.
|
|
116
|
+
repo_root: Repository root path. Defaults to auto-detected.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
List of task info dicts.
|
|
120
|
+
"""
|
|
121
|
+
if repo_root is None:
|
|
122
|
+
repo_root = get_repo_root()
|
|
123
|
+
|
|
124
|
+
tasks_dir = get_tasks_dir(repo_root)
|
|
125
|
+
results = []
|
|
126
|
+
|
|
127
|
+
if not tasks_dir.is_dir():
|
|
128
|
+
return results
|
|
129
|
+
|
|
130
|
+
for d in tasks_dir.iterdir():
|
|
131
|
+
if not d.is_dir() or d.name == "archive":
|
|
132
|
+
continue
|
|
133
|
+
|
|
134
|
+
task_json = d / FILE_TASK_JSON
|
|
135
|
+
if not task_json.is_file():
|
|
136
|
+
continue
|
|
137
|
+
|
|
138
|
+
data = _read_json_file(task_json)
|
|
139
|
+
if not data:
|
|
140
|
+
continue
|
|
141
|
+
|
|
142
|
+
task_assignee = data.get("assignee", "-")
|
|
143
|
+
|
|
144
|
+
# Apply assignee filter
|
|
145
|
+
if task_assignee != assignee:
|
|
146
|
+
continue
|
|
147
|
+
|
|
148
|
+
task_id = data.get("id", "")
|
|
149
|
+
title = data.get("title") or data.get("name", "")
|
|
150
|
+
priority = data.get("priority", "P2")
|
|
151
|
+
status = data.get("status", "planning")
|
|
152
|
+
|
|
153
|
+
# Apply status filter
|
|
154
|
+
if filter_status and status != filter_status:
|
|
155
|
+
continue
|
|
156
|
+
|
|
157
|
+
results.append({
|
|
158
|
+
"priority": priority,
|
|
159
|
+
"id": task_id,
|
|
160
|
+
"title": title,
|
|
161
|
+
"status": status,
|
|
162
|
+
"assignee": task_assignee,
|
|
163
|
+
"dir": d.name,
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
return results
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def list_my_tasks(
|
|
170
|
+
filter_status: Optional[str] = None,
|
|
171
|
+
repo_root: Optional[Path] = None
|
|
172
|
+
) -> list[dict]:
|
|
173
|
+
"""List tasks assigned to current developer.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
filter_status: Optional status filter.
|
|
177
|
+
repo_root: Repository root path. Defaults to auto-detected.
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
List of task info dicts.
|
|
181
|
+
|
|
182
|
+
Raises:
|
|
183
|
+
ValueError: If developer not set.
|
|
184
|
+
"""
|
|
185
|
+
if repo_root is None:
|
|
186
|
+
repo_root = get_repo_root()
|
|
187
|
+
|
|
188
|
+
developer = get_developer(repo_root)
|
|
189
|
+
if not developer:
|
|
190
|
+
raise ValueError("Developer not set")
|
|
191
|
+
|
|
192
|
+
return list_tasks_by_assignee(developer, filter_status, repo_root)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def get_task_stats(repo_root: Optional[Path] = None) -> dict[str, int]:
|
|
196
|
+
"""Get task statistics.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
repo_root: Repository root path. Defaults to auto-detected.
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
Dict with keys: P0, P1, P2, P3, Total.
|
|
203
|
+
"""
|
|
204
|
+
if repo_root is None:
|
|
205
|
+
repo_root = get_repo_root()
|
|
206
|
+
|
|
207
|
+
tasks_dir = get_tasks_dir(repo_root)
|
|
208
|
+
stats = {"P0": 0, "P1": 0, "P2": 0, "P3": 0, "Total": 0}
|
|
209
|
+
|
|
210
|
+
if not tasks_dir.is_dir():
|
|
211
|
+
return stats
|
|
212
|
+
|
|
213
|
+
for d in tasks_dir.iterdir():
|
|
214
|
+
if not d.is_dir() or d.name == "archive":
|
|
215
|
+
continue
|
|
216
|
+
|
|
217
|
+
task_json = d / FILE_TASK_JSON
|
|
218
|
+
if not task_json.is_file():
|
|
219
|
+
continue
|
|
220
|
+
|
|
221
|
+
data = _read_json_file(task_json)
|
|
222
|
+
if not data:
|
|
223
|
+
continue
|
|
224
|
+
|
|
225
|
+
priority = data.get("priority", "P2")
|
|
226
|
+
if priority in stats:
|
|
227
|
+
stats[priority] += 1
|
|
228
|
+
stats["Total"] += 1
|
|
229
|
+
|
|
230
|
+
return stats
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def format_task_stats(stats: dict[str, int]) -> str:
|
|
234
|
+
"""Format task stats as string.
|
|
235
|
+
|
|
236
|
+
Args:
|
|
237
|
+
stats: Stats dict from get_task_stats.
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
Formatted string like "P0:0 P1:1 P2:2 P3:0 Total:3".
|
|
241
|
+
"""
|
|
242
|
+
return f"P0:{stats['P0']} P1:{stats['P1']} P2:{stats['P2']} P3:{stats['P3']} Total:{stats['Total']}"
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
# =============================================================================
|
|
246
|
+
# Main Entry (for testing)
|
|
247
|
+
# =============================================================================
|
|
248
|
+
|
|
249
|
+
if __name__ == "__main__":
|
|
250
|
+
stats = get_task_stats()
|
|
251
|
+
print(format_task_stats(stats))
|
|
252
|
+
print()
|
|
253
|
+
print("Pending tasks:")
|
|
254
|
+
for task in list_pending_tasks():
|
|
255
|
+
print(f" {task['priority']}|{task['id']}|{task['title']}|{task['status']}|{task['assignee']}")
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Task utility functions.
|
|
4
|
+
|
|
5
|
+
Provides:
|
|
6
|
+
is_safe_task_path - Validate task path is safe to operate on
|
|
7
|
+
find_task_by_name - Find task directory by name
|
|
8
|
+
archive_task_dir - Archive task to monthly directory
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
import shutil
|
|
13
|
+
import sys
|
|
14
|
+
from datetime import datetime
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
from .paths import get_repo_root
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# =============================================================================
|
|
21
|
+
# Path Safety
|
|
22
|
+
# =============================================================================
|
|
23
|
+
|
|
24
|
+
def is_safe_task_path(task_path: str, repo_root: Optional[Path] = None) -> bool:
|
|
25
|
+
"""Check if a relative task path is safe to operate on.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
task_path: Task path (relative to repo_root).
|
|
29
|
+
repo_root: Repository root path. Defaults to auto-detected.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
True if safe, False if dangerous.
|
|
33
|
+
"""
|
|
34
|
+
if repo_root is None:
|
|
35
|
+
repo_root = get_repo_root()
|
|
36
|
+
|
|
37
|
+
# Check empty or null
|
|
38
|
+
if not task_path or task_path == "null":
|
|
39
|
+
print("Error: empty or null task path", file=sys.stderr)
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
# Reject absolute paths
|
|
43
|
+
if task_path.startswith("/"):
|
|
44
|
+
print(f"Error: absolute path not allowed: {task_path}", file=sys.stderr)
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
# Reject ".", "..", paths starting with "./" or "../", or containing ".."
|
|
48
|
+
if task_path in (".", "..") or task_path.startswith("./") or task_path.startswith("../") or ".." in task_path:
|
|
49
|
+
print(f"Error: path traversal not allowed: {task_path}", file=sys.stderr)
|
|
50
|
+
return False
|
|
51
|
+
|
|
52
|
+
# Final check: ensure resolved path is not the repo root
|
|
53
|
+
abs_path = repo_root / task_path
|
|
54
|
+
if abs_path.exists():
|
|
55
|
+
try:
|
|
56
|
+
resolved = abs_path.resolve()
|
|
57
|
+
root_resolved = repo_root.resolve()
|
|
58
|
+
if resolved == root_resolved:
|
|
59
|
+
print(f"Error: path resolves to repo root: {task_path}", file=sys.stderr)
|
|
60
|
+
return False
|
|
61
|
+
except (OSError, IOError):
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
return True
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# =============================================================================
|
|
68
|
+
# Task Lookup
|
|
69
|
+
# =============================================================================
|
|
70
|
+
|
|
71
|
+
def find_task_by_name(task_name: str, tasks_dir: Path) -> Optional[Path]:
|
|
72
|
+
"""Find task directory by name (exact or suffix match).
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
task_name: Task name to find.
|
|
76
|
+
tasks_dir: Tasks directory path.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Absolute path to task directory, or None if not found.
|
|
80
|
+
"""
|
|
81
|
+
if not task_name or not tasks_dir or not tasks_dir.is_dir():
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
# Try exact match first
|
|
85
|
+
exact_match = tasks_dir / task_name
|
|
86
|
+
if exact_match.is_dir():
|
|
87
|
+
return exact_match
|
|
88
|
+
|
|
89
|
+
# Try suffix match (e.g., "my-task" matches "01-21-my-task")
|
|
90
|
+
for d in tasks_dir.iterdir():
|
|
91
|
+
if d.is_dir() and d.name.endswith(f"-{task_name}"):
|
|
92
|
+
return d
|
|
93
|
+
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# =============================================================================
|
|
98
|
+
# Archive Operations
|
|
99
|
+
# =============================================================================
|
|
100
|
+
|
|
101
|
+
def archive_task_dir(task_dir_abs: Path, repo_root: Optional[Path] = None) -> Optional[Path]:
|
|
102
|
+
"""Archive a task directory to archive/{YYYY-MM}/.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
task_dir_abs: Absolute path to task directory.
|
|
106
|
+
repo_root: Repository root path. Defaults to auto-detected.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Path to archived directory, or None on error.
|
|
110
|
+
"""
|
|
111
|
+
if not task_dir_abs.is_dir():
|
|
112
|
+
print(f"Error: task directory not found: {task_dir_abs}", file=sys.stderr)
|
|
113
|
+
return None
|
|
114
|
+
|
|
115
|
+
# Get tasks directory (parent of the task)
|
|
116
|
+
tasks_dir = task_dir_abs.parent
|
|
117
|
+
archive_dir = tasks_dir / "archive"
|
|
118
|
+
year_month = datetime.now().strftime("%Y-%m")
|
|
119
|
+
month_dir = archive_dir / year_month
|
|
120
|
+
|
|
121
|
+
# Create archive directory
|
|
122
|
+
try:
|
|
123
|
+
month_dir.mkdir(parents=True, exist_ok=True)
|
|
124
|
+
except (OSError, IOError) as e:
|
|
125
|
+
print(f"Error: Failed to create archive directory: {e}", file=sys.stderr)
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
# Move task to archive
|
|
129
|
+
task_name = task_dir_abs.name
|
|
130
|
+
dest = month_dir / task_name
|
|
131
|
+
|
|
132
|
+
try:
|
|
133
|
+
shutil.move(str(task_dir_abs), str(dest))
|
|
134
|
+
except (OSError, IOError, shutil.Error) as e:
|
|
135
|
+
print(f"Error: Failed to move task to archive: {e}", file=sys.stderr)
|
|
136
|
+
return None
|
|
137
|
+
|
|
138
|
+
return dest
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def archive_task_complete(
|
|
142
|
+
task_dir_abs: Path,
|
|
143
|
+
repo_root: Optional[Path] = None
|
|
144
|
+
) -> dict[str, str]:
|
|
145
|
+
"""Complete archive workflow: archive directory.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
task_dir_abs: Absolute path to task directory.
|
|
149
|
+
repo_root: Repository root path. Defaults to auto-detected.
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Dict with archive result info.
|
|
153
|
+
"""
|
|
154
|
+
if not task_dir_abs.is_dir():
|
|
155
|
+
print(f"Error: task directory not found: {task_dir_abs}", file=sys.stderr)
|
|
156
|
+
return {}
|
|
157
|
+
|
|
158
|
+
archive_dest = archive_task_dir(task_dir_abs, repo_root)
|
|
159
|
+
if archive_dest:
|
|
160
|
+
return {"archived_to": str(archive_dest)}
|
|
161
|
+
|
|
162
|
+
return {}
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
# =============================================================================
|
|
166
|
+
# Main Entry (for testing)
|
|
167
|
+
# =============================================================================
|
|
168
|
+
|
|
169
|
+
if __name__ == "__main__":
|
|
170
|
+
from .paths import get_tasks_dir
|
|
171
|
+
|
|
172
|
+
repo = get_repo_root()
|
|
173
|
+
tasks = get_tasks_dir(repo)
|
|
174
|
+
|
|
175
|
+
print(f"Tasks dir: {tasks}")
|
|
176
|
+
print(f"is_safe_task_path('.trellis/tasks/test'): {is_safe_task_path('.trellis/tasks/test', repo)}")
|
|
177
|
+
print(f"is_safe_task_path('../test'): {is_safe_task_path('../test', repo)}")
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Worktree utilities for Multi-Agent Pipeline.
|
|
4
|
+
|
|
5
|
+
Provides:
|
|
6
|
+
get_worktree_config - Get worktree.yaml path
|
|
7
|
+
get_worktree_base_dir - Get worktree storage directory
|
|
8
|
+
get_worktree_copy_files - Get files to copy list
|
|
9
|
+
get_worktree_post_create_hooks - Get post-create hooks
|
|
10
|
+
get_agents_dir - Get agents registry directory
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from .paths import (
|
|
17
|
+
DIR_WORKFLOW,
|
|
18
|
+
get_repo_root,
|
|
19
|
+
get_workspace_dir,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# =============================================================================
|
|
24
|
+
# YAML Simple Parser (no dependencies)
|
|
25
|
+
# =============================================================================
|
|
26
|
+
|
|
27
|
+
def parse_simple_yaml(content: str) -> dict:
|
|
28
|
+
"""Parse simple YAML (only supports key: value and lists).
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
content: YAML content string.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Parsed dict.
|
|
35
|
+
"""
|
|
36
|
+
result: dict = {}
|
|
37
|
+
current_list: Optional[list] = None
|
|
38
|
+
|
|
39
|
+
for line in content.splitlines():
|
|
40
|
+
stripped = line.strip()
|
|
41
|
+
if not stripped or stripped.startswith("#"):
|
|
42
|
+
continue
|
|
43
|
+
|
|
44
|
+
if stripped.startswith("- "):
|
|
45
|
+
if current_list is not None:
|
|
46
|
+
current_list.append(stripped[2:].strip().strip('"').strip("'"))
|
|
47
|
+
elif ":" in stripped:
|
|
48
|
+
key, _, value = stripped.partition(":")
|
|
49
|
+
key = key.strip()
|
|
50
|
+
value = value.strip().strip('"').strip("'")
|
|
51
|
+
if value:
|
|
52
|
+
result[key] = value
|
|
53
|
+
_ = None
|
|
54
|
+
current_list = None
|
|
55
|
+
else:
|
|
56
|
+
_ = key
|
|
57
|
+
current_list = []
|
|
58
|
+
result[key] = current_list
|
|
59
|
+
|
|
60
|
+
return result
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _yaml_get_value(config_file: Path, key: str) -> Optional[str]:
|
|
64
|
+
"""Read simple value from worktree.yaml.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
config_file: Path to config file.
|
|
68
|
+
key: Key to read.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Value string or None.
|
|
72
|
+
"""
|
|
73
|
+
try:
|
|
74
|
+
content = config_file.read_text(encoding="utf-8")
|
|
75
|
+
data = parse_simple_yaml(content)
|
|
76
|
+
value = data.get(key)
|
|
77
|
+
if isinstance(value, str):
|
|
78
|
+
return value
|
|
79
|
+
except (OSError, IOError):
|
|
80
|
+
pass
|
|
81
|
+
return None
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _yaml_get_list(config_file: Path, section: str) -> list[str]:
|
|
85
|
+
"""Read list from worktree.yaml.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
config_file: Path to config file.
|
|
89
|
+
section: Section name.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
List of items.
|
|
93
|
+
"""
|
|
94
|
+
try:
|
|
95
|
+
content = config_file.read_text(encoding="utf-8")
|
|
96
|
+
data = parse_simple_yaml(content)
|
|
97
|
+
value = data.get(section)
|
|
98
|
+
if isinstance(value, list):
|
|
99
|
+
return [str(item) for item in value]
|
|
100
|
+
except (OSError, IOError):
|
|
101
|
+
pass
|
|
102
|
+
return []
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# =============================================================================
|
|
106
|
+
# Worktree Configuration
|
|
107
|
+
# =============================================================================
|
|
108
|
+
|
|
109
|
+
# Worktree config file relative path (relative to repo root)
|
|
110
|
+
WORKTREE_CONFIG_PATH = f"{DIR_WORKFLOW}/worktree.yaml"
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def get_worktree_config(repo_root: Optional[Path] = None) -> Path:
|
|
114
|
+
"""Get worktree.yaml config file path.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
repo_root: Repository root path. Defaults to auto-detected.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
Absolute path to config file.
|
|
121
|
+
"""
|
|
122
|
+
if repo_root is None:
|
|
123
|
+
repo_root = get_repo_root()
|
|
124
|
+
return repo_root / WORKTREE_CONFIG_PATH
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def get_worktree_base_dir(repo_root: Optional[Path] = None) -> Path:
|
|
128
|
+
"""Get worktree base directory.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
repo_root: Repository root path. Defaults to auto-detected.
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
Absolute path to worktree base directory.
|
|
135
|
+
"""
|
|
136
|
+
if repo_root is None:
|
|
137
|
+
repo_root = get_repo_root()
|
|
138
|
+
|
|
139
|
+
config = get_worktree_config(repo_root)
|
|
140
|
+
worktree_dir = _yaml_get_value(config, "worktree_dir")
|
|
141
|
+
|
|
142
|
+
# Default value
|
|
143
|
+
if not worktree_dir:
|
|
144
|
+
worktree_dir = "../worktrees"
|
|
145
|
+
|
|
146
|
+
# Handle relative path
|
|
147
|
+
if worktree_dir.startswith("../") or worktree_dir.startswith("./"):
|
|
148
|
+
# Relative to repo_root
|
|
149
|
+
return repo_root / worktree_dir
|
|
150
|
+
else:
|
|
151
|
+
# Absolute path
|
|
152
|
+
return Path(worktree_dir)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def get_worktree_copy_files(repo_root: Optional[Path] = None) -> list[str]:
|
|
156
|
+
"""Get files to copy list.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
repo_root: Repository root path. Defaults to auto-detected.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
List of file paths to copy.
|
|
163
|
+
"""
|
|
164
|
+
if repo_root is None:
|
|
165
|
+
repo_root = get_repo_root()
|
|
166
|
+
config = get_worktree_config(repo_root)
|
|
167
|
+
return _yaml_get_list(config, "copy")
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def get_worktree_post_create_hooks(repo_root: Optional[Path] = None) -> list[str]:
|
|
171
|
+
"""Get post_create hooks.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
repo_root: Repository root path. Defaults to auto-detected.
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
List of commands to run.
|
|
178
|
+
"""
|
|
179
|
+
if repo_root is None:
|
|
180
|
+
repo_root = get_repo_root()
|
|
181
|
+
config = get_worktree_config(repo_root)
|
|
182
|
+
return _yaml_get_list(config, "post_create")
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
# =============================================================================
|
|
186
|
+
# Agents Registry
|
|
187
|
+
# =============================================================================
|
|
188
|
+
|
|
189
|
+
def get_agents_dir(repo_root: Optional[Path] = None) -> Optional[Path]:
|
|
190
|
+
"""Get agents directory for current developer.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
repo_root: Repository root path. Defaults to auto-detected.
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
Absolute path to agents directory, or None if no workspace.
|
|
197
|
+
"""
|
|
198
|
+
if repo_root is None:
|
|
199
|
+
repo_root = get_repo_root()
|
|
200
|
+
|
|
201
|
+
workspace_dir = get_workspace_dir(repo_root)
|
|
202
|
+
if workspace_dir:
|
|
203
|
+
return workspace_dir / ".agents"
|
|
204
|
+
return None
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
# =============================================================================
|
|
208
|
+
# Main Entry (for testing)
|
|
209
|
+
# =============================================================================
|
|
210
|
+
|
|
211
|
+
if __name__ == "__main__":
|
|
212
|
+
repo = get_repo_root()
|
|
213
|
+
print(f"Repository root: {repo}")
|
|
214
|
+
print(f"Worktree config: {get_worktree_config(repo)}")
|
|
215
|
+
print(f"Worktree base dir: {get_worktree_base_dir(repo)}")
|
|
216
|
+
print(f"Copy files: {get_worktree_copy_files(repo)}")
|
|
217
|
+
print(f"Post create hooks: {get_worktree_post_create_hooks(repo)}")
|
|
218
|
+
print(f"Agents dir: {get_agents_dir(repo)}")
|