@delorenj/pjangler 1.1.3
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 +34 -0
- package/dist/index.js +2620 -0
- package/dist/mcp-server.js +1491 -0
- package/package.json +46 -0
- package/templates/commonproject/.augment/commands/bmad/agents/bmb-agent-builder.md +14 -0
- package/templates/commonproject/.augment/commands/bmad/agents/bmb-module-builder.md +14 -0
- package/templates/commonproject/.augment/commands/bmad/agents/bmb-workflow-builder.md +14 -0
- package/templates/commonproject/.augment/commands/bmad/agents/bmm-analyst.md +14 -0
- package/templates/commonproject/.augment/commands/bmad/agents/bmm-architect.md +14 -0
- package/templates/commonproject/.augment/commands/bmad/agents/bmm-dev.md +14 -0
- package/templates/commonproject/.augment/commands/bmad/agents/bmm-pm.md +14 -0
- package/templates/commonproject/.augment/commands/bmad/agents/bmm-quick-flow-solo-dev.md +14 -0
- package/templates/commonproject/.augment/commands/bmad/agents/bmm-sm.md +14 -0
- package/templates/commonproject/.augment/commands/bmad/agents/bmm-tea.md +14 -0
- package/templates/commonproject/.augment/commands/bmad/agents/bmm-tech-writer.md +14 -0
- package/templates/commonproject/.augment/commands/bmad/agents/bmm-ux-designer.md +14 -0
- package/templates/commonproject/.augment/commands/bmad/agents/cis-brainstorming-coach.md +14 -0
- package/templates/commonproject/.augment/commands/bmad/agents/cis-creative-problem-solver.md +14 -0
- package/templates/commonproject/.augment/commands/bmad/agents/cis-design-thinking-coach.md +14 -0
- package/templates/commonproject/.augment/commands/bmad/agents/cis-innovation-strategist.md +14 -0
- package/templates/commonproject/.augment/commands/bmad/agents/cis-presentation-master.md +14 -0
- package/templates/commonproject/.augment/commands/bmad/agents/cis-storyteller.md +14 -0
- package/templates/commonproject/.augment/commands/bmad/agents/core-bmad-master.md +14 -0
- package/templates/commonproject/.augment/commands/bmad/tasks/core-index-docs.md +74 -0
- package/templates/commonproject/.augment/commands/bmad/tasks/core-shard-doc.md +118 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmb-agent.md +5 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmb-module.md +5 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmb-workflow.md +5 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-check-implementation-readiness.md +5 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-code-review.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-correct-course.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-create-architecture.md +5 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-create-epics-and-stories.md +5 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-create-excalidraw-dataflow.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-create-excalidraw-diagram.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-create-excalidraw-flowchart.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-create-excalidraw-wireframe.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-create-product-brief.md +5 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-create-story.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-create-ux-design.md +5 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-dev-story.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-document-project.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-generate-project-context.md +5 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-prd.md +5 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-quick-dev.md +5 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-quick-spec.md +5 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-research.md +5 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-retrospective.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-sprint-planning.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-sprint-status.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-testarch-atdd.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-testarch-automate.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-testarch-ci.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-testarch-framework.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-testarch-nfr.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-testarch-test-design.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-testarch-test-review.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-testarch-trace.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-workflow-init.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/bmm-workflow-status.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/cis-design-thinking.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/cis-innovation-strategy.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/cis-problem-solving.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/cis-storytelling.md +13 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/core-brainstorming.md +5 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/core-party-mode.md +5 -0
- package/templates/commonproject/.augment/commands/bmad/workflows/custom-ticket-lifecycle.md +5 -0
- package/templates/commonproject/.claude/commands/bmad/bmb/agents/agent-builder.md +14 -0
- package/templates/commonproject/.claude/commands/bmad/bmb/agents/module-builder.md +14 -0
- package/templates/commonproject/.claude/commands/bmad/bmb/agents/workflow-builder.md +14 -0
- package/templates/commonproject/.claude/commands/bmad/bmb/workflows/agent.md +5 -0
- package/templates/commonproject/.claude/commands/bmad/bmb/workflows/module.md +5 -0
- package/templates/commonproject/.claude/commands/bmad/bmb/workflows/workflow.md +5 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/agents/analyst.md +14 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/agents/architect.md +14 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/agents/dev.md +14 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/agents/pm.md +14 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/agents/quick-flow-solo-dev.md +14 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/agents/sm.md +14 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/agents/tea.md +14 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/agents/tech-writer.md +14 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/agents/ux-designer.md +14 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/check-implementation-readiness.md +5 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/code-review.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/correct-course.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/create-architecture.md +5 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/create-epics-and-stories.md +5 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/create-excalidraw-dataflow.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/create-excalidraw-diagram.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/create-excalidraw-flowchart.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/create-excalidraw-wireframe.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/create-product-brief.md +5 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/create-story.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/create-ux-design.md +5 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/dev-story.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/document-project.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/generate-project-context.md +5 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/prd.md +5 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/quick-dev.md +5 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/quick-spec.md +5 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/research.md +5 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/retrospective.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/sprint-planning.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/sprint-status.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/testarch-atdd.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/testarch-automate.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/testarch-ci.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/testarch-framework.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/testarch-nfr.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/testarch-test-design.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/testarch-test-review.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/testarch-trace.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/workflow-init.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/bmm/workflows/workflow-status.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/cis/agents/brainstorming-coach.md +14 -0
- package/templates/commonproject/.claude/commands/bmad/cis/agents/creative-problem-solver.md +14 -0
- package/templates/commonproject/.claude/commands/bmad/cis/agents/design-thinking-coach.md +14 -0
- package/templates/commonproject/.claude/commands/bmad/cis/agents/innovation-strategist.md +14 -0
- package/templates/commonproject/.claude/commands/bmad/cis/agents/presentation-master.md +14 -0
- package/templates/commonproject/.claude/commands/bmad/cis/agents/storyteller.md +14 -0
- package/templates/commonproject/.claude/commands/bmad/cis/workflows/design-thinking.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/cis/workflows/innovation-strategy.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/cis/workflows/problem-solving.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/cis/workflows/storytelling.md +13 -0
- package/templates/commonproject/.claude/commands/bmad/core/agents/bmad-master.md +14 -0
- package/templates/commonproject/.claude/commands/bmad/core/tasks/index-docs.md +9 -0
- package/templates/commonproject/.claude/commands/bmad/core/tasks/shard-doc.md +9 -0
- package/templates/commonproject/.claude/commands/bmad/core/workflows/brainstorming.md +5 -0
- package/templates/commonproject/.claude/commands/bmad/core/workflows/party-mode.md +5 -0
- package/templates/commonproject/.claude/commands/bmad/custom/workflows/ticket-lifecycle.md +5 -0
- package/templates/commonproject/.copier-answers.yml +4 -0
- package/templates/commonproject/.gemini/commands/bmad-agent-bmb-agent-builder.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-agent-bmb-module-builder.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-agent-bmb-workflow-builder.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-agent-bmm-analyst.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-agent-bmm-architect.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-agent-bmm-dev.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-agent-bmm-pm.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-agent-bmm-quick-flow-solo-dev.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-agent-bmm-sm.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-agent-bmm-tea.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-agent-bmm-tech-writer.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-agent-bmm-ux-designer.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-agent-cis-brainstorming-coach.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-agent-cis-creative-problem-solver.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-agent-cis-design-thinking-coach.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-agent-cis-innovation-strategist.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-agent-cis-presentation-master.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-agent-cis-storyteller.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-agent-core-bmad-master.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-task-core-index-docs.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-task-core-review-adversarial-general.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-task-core-shard-doc.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-task-core-workflow.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmb-agent.toml +4 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmb-module.toml +4 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmb-workflow.toml +4 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-check-implementation-readiness.toml +4 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-code-review.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-correct-course.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-create-architecture.toml +4 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-create-epics-and-stories.toml +4 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-create-excalidraw-dataflow.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-create-excalidraw-diagram.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-create-excalidraw-flowchart.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-create-excalidraw-wireframe.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-create-product-brief.toml +4 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-create-story.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-create-ux-design.toml +4 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-dev-story.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-document-project.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-generate-project-context.toml +4 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-prd.toml +4 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-quick-dev.toml +4 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-quick-spec.toml +4 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-research.toml +4 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-retrospective.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-sprint-planning.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-sprint-status.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-testarch-atdd.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-testarch-automate.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-testarch-ci.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-testarch-framework.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-testarch-nfr.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-testarch-test-design.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-testarch-test-review.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-testarch-trace.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-workflow-init.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-bmm-workflow-status.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-cis-design-thinking.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-cis-innovation-strategy.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-cis-problem-solving.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-cis-storytelling.toml +12 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-core-brainstorming.toml +4 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-core-party-mode.toml +4 -0
- package/templates/commonproject/.gemini/commands/bmad-workflow-custom-ticket-lifecycle.toml +4 -0
- package/templates/commonproject/.github/agents/bmd-custom-bmb-agent-builder.agent.md +17 -0
- package/templates/commonproject/.github/agents/bmd-custom-bmb-module-builder.agent.md +17 -0
- package/templates/commonproject/.github/agents/bmd-custom-bmb-workflow-builder.agent.md +17 -0
- package/templates/commonproject/.github/agents/bmd-custom-bmm-analyst.agent.md +17 -0
- package/templates/commonproject/.github/agents/bmd-custom-bmm-architect.agent.md +17 -0
- package/templates/commonproject/.github/agents/bmd-custom-bmm-dev.agent.md +17 -0
- package/templates/commonproject/.github/agents/bmd-custom-bmm-pm.agent.md +17 -0
- package/templates/commonproject/.github/agents/bmd-custom-bmm-quick-flow-solo-dev.agent.md +17 -0
- package/templates/commonproject/.github/agents/bmd-custom-bmm-sm.agent.md +17 -0
- package/templates/commonproject/.github/agents/bmd-custom-bmm-tea.agent.md +17 -0
- package/templates/commonproject/.github/agents/bmd-custom-bmm-tech-writer.agent.md +17 -0
- package/templates/commonproject/.github/agents/bmd-custom-bmm-ux-designer.agent.md +17 -0
- package/templates/commonproject/.github/agents/bmd-custom-cis-brainstorming-coach.agent.md +17 -0
- package/templates/commonproject/.github/agents/bmd-custom-cis-creative-problem-solver.agent.md +17 -0
- package/templates/commonproject/.github/agents/bmd-custom-cis-design-thinking-coach.agent.md +17 -0
- package/templates/commonproject/.github/agents/bmd-custom-cis-innovation-strategist.agent.md +17 -0
- package/templates/commonproject/.github/agents/bmd-custom-cis-presentation-master.agent.md +17 -0
- package/templates/commonproject/.github/agents/bmd-custom-cis-storyteller.agent.md +17 -0
- package/templates/commonproject/.github/agents/bmd-custom-core-bmad-master.agent.md +17 -0
- package/templates/commonproject/.mcp.json +12 -0
- package/templates/commonproject/.mise/scripts/claude +13 -0
- package/templates/commonproject/.mise/scripts/create-plane-project.sh +123 -0
- package/templates/commonproject/.mise/scripts/init-project.sh +314 -0
- package/templates/commonproject/.mise/scripts/link-agentfiles.sh +9 -0
- package/templates/commonproject/.opencode/agent/bmad-agent-bmb-agent-builder.md +14 -0
- package/templates/commonproject/.opencode/agent/bmad-agent-bmb-module-builder.md +14 -0
- package/templates/commonproject/.opencode/agent/bmad-agent-bmb-workflow-builder.md +14 -0
- package/templates/commonproject/.opencode/agent/bmad-agent-bmm-analyst.md +14 -0
- package/templates/commonproject/.opencode/agent/bmad-agent-bmm-architect.md +14 -0
- package/templates/commonproject/.opencode/agent/bmad-agent-bmm-dev.md +14 -0
- package/templates/commonproject/.opencode/agent/bmad-agent-bmm-pm.md +14 -0
- package/templates/commonproject/.opencode/agent/bmad-agent-bmm-quick-flow-solo-dev.md +14 -0
- package/templates/commonproject/.opencode/agent/bmad-agent-bmm-sm.md +14 -0
- package/templates/commonproject/.opencode/agent/bmad-agent-bmm-tea.md +14 -0
- package/templates/commonproject/.opencode/agent/bmad-agent-bmm-tech-writer.md +14 -0
- package/templates/commonproject/.opencode/agent/bmad-agent-bmm-ux-designer.md +14 -0
- package/templates/commonproject/.opencode/agent/bmad-agent-cis-brainstorming-coach.md +14 -0
- package/templates/commonproject/.opencode/agent/bmad-agent-cis-creative-problem-solver.md +14 -0
- package/templates/commonproject/.opencode/agent/bmad-agent-cis-design-thinking-coach.md +14 -0
- package/templates/commonproject/.opencode/agent/bmad-agent-cis-innovation-strategist.md +14 -0
- package/templates/commonproject/.opencode/agent/bmad-agent-cis-presentation-master.md +14 -0
- package/templates/commonproject/.opencode/agent/bmad-agent-cis-storyteller.md +14 -0
- package/templates/commonproject/.opencode/agent/bmad-agent-core-bmad-master.md +14 -0
- package/templates/commonproject/.opencode/command/bmad-bmb-agent.md +5 -0
- package/templates/commonproject/.opencode/command/bmad-bmb-module.md +5 -0
- package/templates/commonproject/.opencode/command/bmad-bmb-workflow.md +5 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-check-implementation-readiness.md +5 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-code-review.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-correct-course.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-create-architecture.md +5 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-create-epics-and-stories.md +5 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-create-excalidraw-dataflow.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-create-excalidraw-diagram.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-create-excalidraw-flowchart.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-create-excalidraw-wireframe.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-create-product-brief.md +5 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-create-story.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-create-ux-design.md +5 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-dev-story.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-document-project.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-generate-project-context.md +5 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-prd.md +5 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-quick-dev.md +5 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-quick-spec.md +5 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-research.md +5 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-retrospective.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-sprint-planning.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-sprint-status.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-testarch-atdd.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-testarch-automate.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-testarch-ci.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-testarch-framework.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-testarch-nfr.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-testarch-test-design.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-testarch-test-review.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-testarch-trace.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-workflow-init.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-bmm-workflow-status.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-cis-design-thinking.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-cis-innovation-strategy.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-cis-problem-solving.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-cis-storytelling.md +13 -0
- package/templates/commonproject/.opencode/command/bmad-core-brainstorming.md +5 -0
- package/templates/commonproject/.opencode/command/bmad-core-party-mode.md +5 -0
- package/templates/commonproject/.opencode/command/bmad-custom-ticket-lifecycle.md +5 -0
- package/templates/commonproject/.opencode/command/bmad-task-core-index-docs.md +9 -0
- package/templates/commonproject/.opencode/command/bmad-task-core-shard-doc.md +9 -0
- package/templates/commonproject/.plane.json +6 -0
- package/templates/commonproject/.scripts/test-template.sh +63 -0
- package/templates/commonproject/AGENTS.md +86 -0
- package/templates/commonproject/README.md +172 -0
- package/templates/commonproject/_bmad/_config/agent-manifest.csv +20 -0
- package/templates/commonproject/_bmad/_config/agents/bmb-agent-builder.customize.yaml +41 -0
- package/templates/commonproject/_bmad/_config/agents/bmb-module-builder.customize.yaml +41 -0
- package/templates/commonproject/_bmad/_config/agents/bmb-workflow-builder.customize.yaml +41 -0
- package/templates/commonproject/_bmad/_config/agents/bmm-analyst.customize.yaml +41 -0
- package/templates/commonproject/_bmad/_config/agents/bmm-architect.customize.yaml +41 -0
- package/templates/commonproject/_bmad/_config/agents/bmm-dev.customize.yaml +41 -0
- package/templates/commonproject/_bmad/_config/agents/bmm-pm.customize.yaml +41 -0
- package/templates/commonproject/_bmad/_config/agents/bmm-quick-flow-solo-dev.customize.yaml +41 -0
- package/templates/commonproject/_bmad/_config/agents/bmm-sm.customize.yaml +41 -0
- package/templates/commonproject/_bmad/_config/agents/bmm-tea.customize.yaml +41 -0
- package/templates/commonproject/_bmad/_config/agents/bmm-tech-writer.customize.yaml +41 -0
- package/templates/commonproject/_bmad/_config/agents/bmm-ux-designer.customize.yaml +41 -0
- package/templates/commonproject/_bmad/_config/agents/cis-brainstorming-coach.customize.yaml +41 -0
- package/templates/commonproject/_bmad/_config/agents/cis-creative-problem-solver.customize.yaml +41 -0
- package/templates/commonproject/_bmad/_config/agents/cis-design-thinking-coach.customize.yaml +41 -0
- package/templates/commonproject/_bmad/_config/agents/cis-innovation-strategist.customize.yaml +41 -0
- package/templates/commonproject/_bmad/_config/agents/cis-presentation-master.customize.yaml +41 -0
- package/templates/commonproject/_bmad/_config/agents/cis-storyteller.customize.yaml +41 -0
- package/templates/commonproject/_bmad/_config/agents/core-bmad-master.customize.yaml +41 -0
- package/templates/commonproject/_bmad/_config/custom/cache-manifest.yaml +4 -0
- package/templates/commonproject/_bmad/_config/custom/custom/module.yaml +8 -0
- package/templates/commonproject/_bmad/_config/custom/custom/workflows/ticket-lifecycle/data/ac-sufficiency-rubric.md +33 -0
- package/templates/commonproject/_bmad/_config/custom/custom/workflows/ticket-lifecycle/data/audit-comment-template.md +54 -0
- package/templates/commonproject/_bmad/_config/custom/custom/workflows/ticket-lifecycle/data/event-schemas.md +58 -0
- package/templates/commonproject/_bmad/_config/custom/custom/workflows/ticket-lifecycle/steps-c/step-01-init.md +173 -0
- package/templates/commonproject/_bmad/_config/custom/custom/workflows/ticket-lifecycle/steps-c/step-02-triage.md +159 -0
- package/templates/commonproject/_bmad/_config/custom/custom/workflows/ticket-lifecycle/steps-c/step-03-refine.md +149 -0
- package/templates/commonproject/_bmad/_config/custom/custom/workflows/ticket-lifecycle/steps-c/step-04-implement.md +147 -0
- package/templates/commonproject/_bmad/_config/custom/custom/workflows/ticket-lifecycle/steps-c/step-05-review.md +146 -0
- package/templates/commonproject/_bmad/_config/custom/custom/workflows/ticket-lifecycle/steps-c/step-06-qa.md +199 -0
- package/templates/commonproject/_bmad/_config/custom/custom/workflows/ticket-lifecycle/steps-c/step-07-complete.md +141 -0
- package/templates/commonproject/_bmad/_config/custom/custom/workflows/ticket-lifecycle/steps-e/step-01-assess.md +102 -0
- package/templates/commonproject/_bmad/_config/custom/custom/workflows/ticket-lifecycle/steps-e/step-02-apply.md +85 -0
- package/templates/commonproject/_bmad/_config/custom/custom/workflows/ticket-lifecycle/steps-v/step-01-validate.md +154 -0
- package/templates/commonproject/_bmad/_config/custom/custom/workflows/ticket-lifecycle/workflow-plan-ticket-lifecycle.md +346 -0
- package/templates/commonproject/_bmad/_config/custom/custom/workflows/ticket-lifecycle/workflow.md +84 -0
- package/templates/commonproject/_bmad/_config/custom/custom/workflows/ticket-lifecycle/workflow.yaml +39 -0
- package/templates/commonproject/_bmad/_config/files-manifest.csv +483 -0
- package/templates/commonproject/_bmad/_config/ides/claude-code.yaml +6 -0
- package/templates/commonproject/_bmad/_config/ides/codex.yaml +5 -0
- package/templates/commonproject/_bmad/_config/manifest.yaml +18 -0
- package/templates/commonproject/_bmad/_config/task-manifest.csv +5 -0
- package/templates/commonproject/_bmad/_config/tool-manifest.csv +1 -0
- package/templates/commonproject/_bmad/_config/workflow-manifest.csv +43 -0
- package/templates/commonproject/_bmad/_memory/config.yaml +11 -0
- package/templates/commonproject/_bmad/_memory/storyteller-sidecar/stories-told.md +7 -0
- package/templates/commonproject/_bmad/_memory/storyteller-sidecar/story-preferences.md +7 -0
- package/templates/commonproject/_bmad/bmb/README.md +25 -0
- package/templates/commonproject/_bmad/bmb/agents/agent-builder.md +64 -0
- package/templates/commonproject/_bmad/bmb/agents/module-builder.md +65 -0
- package/templates/commonproject/_bmad/bmb/agents/workflow-builder.md +66 -0
- package/templates/commonproject/_bmad/bmb/config.yaml +12 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/data/agent-compilation.md +273 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/data/agent-menu-patterns.md +233 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/data/agent-metadata.md +208 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/data/brainstorm-context.md +146 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/data/communication-presets.csv +61 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/data/critical-actions.md +120 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/data/expert-agent-architecture.md +236 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/data/expert-agent-validation.md +174 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/data/module-agent-validation.md +126 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/data/persona-properties.md +266 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/data/principles-crafting.md +292 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/data/reference/module-examples/architect.md +68 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/data/simple-agent-architecture.md +204 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/data/simple-agent-validation.md +133 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/data/understanding-agent-types.md +222 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-c/step-01-brainstorm.md +128 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-c/step-02-discovery.md +170 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-c/step-03-type-metadata.md +296 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-c/step-04-persona.md +212 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-c/step-05-commands-menu.md +178 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-c/step-06-activation.md +279 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-c/step-07a-build-simple.md +187 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-c/step-07b-build-expert.md +201 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-c/step-07c-build-module.md +258 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-c/step-08-celebrate.md +249 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-e/e-01-load-existing.md +221 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-e/e-02-discover-edits.md +193 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-e/e-03-placeholder.md +1 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-e/e-04-type-metadata.md +124 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-e/e-05-persona.md +134 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-e/e-06-commands-menu.md +122 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-e/e-07-activation.md +125 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-e/e-08a-edit-simple.md +137 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-e/e-08b-edit-expert.md +119 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-e/e-08c-edit-module.md +123 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-e/e-09-celebrate.md +155 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-v/v-01-load-review.md +136 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-v/v-02a-validate-metadata.md +116 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-v/v-02b-validate-persona.md +124 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-v/v-02c-validate-menu.md +145 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-v/v-02d-validate-structure.md +136 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-v/v-02e-validate-sidecar.md +136 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/steps-v/v-03-summary.md +104 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/templates/agent-plan.template.md +5 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/templates/expert-agent-template/expert-agent.template.md +77 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/templates/simple-agent.template.md +72 -0
- package/templates/commonproject/_bmad/bmb/workflows/agent/workflow.md +123 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/data/agent-architecture.md +179 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/data/agent-spec-template.md +79 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/data/module-installer-standards.md +348 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/data/module-standards.md +280 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/data/module-yaml-conventions.md +392 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-b/step-01-welcome.md +147 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-b/step-02-spark.md +140 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-b/step-03-module-type.md +148 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-b/step-04-vision.md +82 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-b/step-05-identity.md +96 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-b/step-06-users.md +85 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-b/step-07-value.md +75 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-b/step-08-agents.md +96 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-b/step-09-workflows.md +82 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-b/step-10-tools.md +90 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-b/step-11-scenarios.md +83 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-b/step-12-creative.md +94 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-b/step-13-review.md +104 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-b/step-14-finalize.md +117 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-c/step-01-load-brief.md +178 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-c/step-01b-continue.md +83 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-c/step-02-structure.md +109 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-c/step-03-config.md +118 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-c/step-04-installer.md +160 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-c/step-05-agents.md +167 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-c/step-06-workflows.md +183 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-c/step-07-docs.md +402 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-c/step-08-complete.md +123 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-e/step-01-load-target.md +81 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-e/step-02-select-edit.md +77 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-e/step-03-apply-edit.md +77 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-e/step-04-review.md +80 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-e/step-05-confirm.md +75 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-v/step-01-load-target.md +96 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-v/step-02-file-structure.md +94 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-v/step-03-module-yaml.md +99 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-v/step-04-agent-specs.md +152 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-v/step-05-workflow-specs.md +152 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-v/step-06-documentation.md +143 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-v/step-07-installation.md +113 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/steps-v/step-08-report.md +197 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/templates/brief-template.md +154 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/templates/workflow-spec-template.md +96 -0
- package/templates/commonproject/_bmad/bmb/workflows/module/workflow.md +100 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/data/architecture.md +152 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/data/common-workflow-tools.csv +19 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/data/csv-data-file-standards.md +81 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/data/frontmatter-standards.md +225 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/data/input-discovery-standards.md +269 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/data/intent-vs-prescriptive-spectrum.md +50 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/data/menu-handling-standards.md +167 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/data/output-format-standards.md +188 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/data/step-file-rules.md +235 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/data/step-type-patterns.md +311 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/data/subprocess-optimization-patterns.md +386 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/data/trimodal-workflow-structure.md +209 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/data/workflow-chaining-standards.md +271 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/data/workflow-examples.md +276 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/data/workflow-type-criteria.md +172 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-c/step-00-conversion.md +262 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-c/step-01-discovery.md +194 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-c/step-01b-continuation.md +3 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-c/step-02-classification.md +269 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-c/step-03-requirements.md +282 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-c/step-04-tools.md +281 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-c/step-05-plan-review.md +242 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-c/step-06-design.md +329 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-c/step-07-foundation.md +238 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-c/step-08-build-step-01.md +377 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-c/step-09-build-next-step.md +350 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-c/step-10-confirmation.md +320 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-c/step-11-completion.md +191 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-e/step-e-01-assess-workflow.md +237 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-e/step-e-02-discover-edits.md +248 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-e/step-e-03-fix-validation.md +252 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-e/step-e-04-direct-edit.md +275 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-e/step-e-05-apply-edit.md +154 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-e/step-e-06-validate-after.md +190 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-e/step-e-07-complete.md +206 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-v/step-01-validate-max-mode.md +109 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-v/step-01-validate.md +221 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-v/step-01b-structure.md +152 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-v/step-02-frontmatter-validation.md +199 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-v/step-02b-path-violations.md +265 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-v/step-03-menu-validation.md +164 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-v/step-04-step-type-validation.md +211 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-v/step-05-output-format-validation.md +200 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-v/step-06-validation-design-check.md +195 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-v/step-07-instruction-style-check.md +209 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-v/step-08-collaborative-experience-check.md +199 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-v/step-08b-subprocess-optimization.md +179 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-v/step-09-cohesive-review.md +186 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-v/step-10-report-complete.md +154 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/steps-v/step-11-plan-validation.md +237 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/templates/minimal-output-template.md +11 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/templates/step-01-init-continuable-template.md +241 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/templates/step-1b-template.md +223 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/templates/step-template.md +290 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/templates/workflow-template.md +102 -0
- package/templates/commonproject/_bmad/bmb/workflows/workflow/workflow.md +109 -0
- package/templates/commonproject/_bmad/bmm/agents/analyst.md +82 -0
- package/templates/commonproject/_bmad/bmm/agents/architect.md +74 -0
- package/templates/commonproject/_bmad/bmm/agents/dev.md +76 -0
- package/templates/commonproject/_bmad/bmm/agents/pm.md +78 -0
- package/templates/commonproject/_bmad/bmm/agents/quick-flow-solo-dev.md +74 -0
- package/templates/commonproject/_bmad/bmm/agents/sm.md +77 -0
- package/templates/commonproject/_bmad/bmm/agents/tea.md +77 -0
- package/templates/commonproject/_bmad/bmm/agents/tech-writer.md +78 -0
- package/templates/commonproject/_bmad/bmm/agents/ux-designer.md +74 -0
- package/templates/commonproject/_bmad/bmm/config.yaml +18 -0
- package/templates/commonproject/_bmad/bmm/data/README.md +29 -0
- package/templates/commonproject/_bmad/bmm/data/documentation-standards.md +262 -0
- package/templates/commonproject/_bmad/bmm/data/project-context-template.md +40 -0
- package/templates/commonproject/_bmad/bmm/teams/default-party.csv +21 -0
- package/templates/commonproject/_bmad/bmm/teams/team-fullstack.yaml +12 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/api-request.md +442 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/api-testing-patterns.md +843 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/auth-session.md +552 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/burn-in.md +273 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/ci-burn-in.md +675 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/component-tdd.md +486 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/contract-testing.md +957 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/data-factories.md +500 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/email-auth.md +721 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/error-handling.md +725 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/feature-flags.md +750 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/file-utils.md +463 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/fixture-architecture.md +401 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/fixtures-composition.md +382 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/intercept-network-call.md +430 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/log.md +429 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/network-error-monitor.md +405 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/network-first.md +486 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/network-recorder.md +527 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/nfr-criteria.md +670 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/overview.md +286 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/playwright-config.md +730 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/probability-impact.md +601 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/recurse.md +421 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/risk-governance.md +615 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/selective-testing.md +732 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/selector-resilience.md +527 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/test-healing-patterns.md +644 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/test-levels-framework.md +473 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/test-priorities-matrix.md +373 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/test-quality.md +664 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/timing-debugging.md +372 -0
- package/templates/commonproject/_bmad/bmm/testarch/knowledge/visual-debugging.md +524 -0
- package/templates/commonproject/_bmad/bmm/testarch/tea-index.csv +34 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/create-product-brief/product-brief.template.md +10 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-01-init.md +177 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-01b-continue.md +161 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-02-vision.md +199 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-03-users.md +202 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-04-metrics.md +205 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-05-scope.md +219 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-06-complete.md +194 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/create-product-brief/workflow.md +58 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/research/domain-steps/step-01-init.md +137 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/research/domain-steps/step-02-domain-analysis.md +229 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/research/domain-steps/step-03-competitive-landscape.md +238 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/research/domain-steps/step-04-regulatory-focus.md +206 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/research/domain-steps/step-05-technical-trends.md +234 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/research/domain-steps/step-06-research-synthesis.md +443 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/research/market-steps/step-01-init.md +182 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/research/market-steps/step-02-customer-behavior.md +237 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/research/market-steps/step-02-customer-insights.md +200 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/research/market-steps/step-03-customer-pain-points.md +249 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/research/market-steps/step-04-customer-decisions.md +259 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/research/market-steps/step-05-competitive-analysis.md +177 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/research/market-steps/step-06-research-completion.md +475 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/research/research.template.md +29 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/research/technical-steps/step-01-init.md +137 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/research/technical-steps/step-02-technical-overview.md +239 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/research/technical-steps/step-03-integration-patterns.md +248 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/research/technical-steps/step-04-architectural-patterns.md +202 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/research/technical-steps/step-05-implementation-research.md +239 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/research/technical-steps/step-06-research-synthesis.md +486 -0
- package/templates/commonproject/_bmad/bmm/workflows/1-analysis/research/workflow.md +173 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01-init.md +135 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01b-continue.md +127 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-02-discovery.md +190 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-03-core-experience.md +216 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-04-emotional-response.md +219 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-05-inspiration.md +234 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-06-design-system.md +252 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-07-defining-experience.md +254 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-08-visual-foundation.md +224 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-09-design-directions.md +224 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-10-user-journeys.md +241 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-11-component-strategy.md +248 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-12-ux-patterns.md +237 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-13-responsive-accessibility.md +264 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-14-complete.md +228 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/ux-design-template.md +13 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +43 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/data/domain-complexity.csv +13 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/data/prd-purpose.md +197 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/data/project-types.csv +11 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-c/step-01-init.md +191 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-c/step-01b-continue.md +153 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-c/step-02-discovery.md +224 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-c/step-03-success.md +226 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-c/step-04-journeys.md +213 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-c/step-05-domain.md +207 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-c/step-06-innovation.md +226 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-c/step-07-project-type.md +237 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-c/step-08-scoping.md +228 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-c/step-09-functional.md +231 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-c/step-10-nonfunctional.md +242 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-c/step-11-polish.md +217 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-c/step-12-complete.md +180 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-e/step-e-01-discovery.md +247 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-e/step-e-01b-legacy-conversion.md +208 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-e/step-e-02-review.md +249 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-e/step-e-03-edit.md +253 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-e/step-e-04-complete.md +168 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-01-discovery.md +218 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-02-format-detection.md +191 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-02b-parity-check.md +209 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-03-density-validation.md +174 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-04-brief-coverage-validation.md +214 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-05-measurability-validation.md +228 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-06-traceability-validation.md +217 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-07-implementation-leakage-validation.md +205 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-08-domain-compliance-validation.md +243 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-09-project-type-validation.md +263 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-10-smart-validation.md +209 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-11-holistic-quality-validation.md +264 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-12-completeness-validation.md +242 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-13-report-complete.md +232 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/templates/prd-template.md +10 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/validation-report-prd-workflow.md +433 -0
- package/templates/commonproject/_bmad/bmm/workflows/2-plan-workflows/prd/workflow.md +150 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-01-document-discovery.md +190 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-02-prd-analysis.md +178 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-03-epic-coverage-validation.md +179 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-04-ux-alignment.md +139 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-05-epic-quality-review.md +252 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-06-final-assessment.md +133 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/templates/readiness-report-template.md +4 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md +55 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/create-architecture/architecture-decision-template.md +12 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/create-architecture/data/domain-complexity.csv +11 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/create-architecture/data/project-types.csv +7 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-01-init.md +153 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-01b-continue.md +164 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-02-context.md +224 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-03-starter.md +331 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md +318 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-05-patterns.md +359 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-06-structure.md +379 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-07-validation.md +359 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-08-complete.md +352 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/create-architecture/workflow.md +50 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-01-validate-prerequisites.md +259 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-02-design-epics.md +233 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-03-create-stories.md +272 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-04-final-validation.md +145 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/create-epics-and-stories/templates/epics-template.md +57 -0
- package/templates/commonproject/_bmad/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +59 -0
- package/templates/commonproject/_bmad/bmm/workflows/4-implementation/code-review/checklist.md +23 -0
- package/templates/commonproject/_bmad/bmm/workflows/4-implementation/code-review/instructions.xml +227 -0
- package/templates/commonproject/_bmad/bmm/workflows/4-implementation/code-review/workflow.yaml +50 -0
- package/templates/commonproject/_bmad/bmm/workflows/4-implementation/correct-course/checklist.md +279 -0
- package/templates/commonproject/_bmad/bmm/workflows/4-implementation/correct-course/instructions.md +206 -0
- package/templates/commonproject/_bmad/bmm/workflows/4-implementation/correct-course/workflow.yaml +58 -0
- package/templates/commonproject/_bmad/bmm/workflows/4-implementation/create-story/checklist.md +358 -0
- package/templates/commonproject/_bmad/bmm/workflows/4-implementation/create-story/instructions.xml +345 -0
- package/templates/commonproject/_bmad/bmm/workflows/4-implementation/create-story/template.md +49 -0
- package/templates/commonproject/_bmad/bmm/workflows/4-implementation/create-story/workflow.yaml +59 -0
- package/templates/commonproject/_bmad/bmm/workflows/4-implementation/dev-story/checklist.md +80 -0
- package/templates/commonproject/_bmad/bmm/workflows/4-implementation/dev-story/instructions.xml +410 -0
- package/templates/commonproject/_bmad/bmm/workflows/4-implementation/dev-story/workflow.yaml +25 -0
- package/templates/commonproject/_bmad/bmm/workflows/4-implementation/retrospective/instructions.md +1443 -0
- package/templates/commonproject/_bmad/bmm/workflows/4-implementation/retrospective/workflow.yaml +57 -0
- package/templates/commonproject/_bmad/bmm/workflows/4-implementation/sprint-planning/checklist.md +33 -0
- package/templates/commonproject/_bmad/bmm/workflows/4-implementation/sprint-planning/instructions.md +225 -0
- package/templates/commonproject/_bmad/bmm/workflows/4-implementation/sprint-planning/sprint-status-template.yaml +55 -0
- package/templates/commonproject/_bmad/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +52 -0
- package/templates/commonproject/_bmad/bmm/workflows/4-implementation/sprint-status/instructions.md +229 -0
- package/templates/commonproject/_bmad/bmm/workflows/4-implementation/sprint-status/workflow.yaml +35 -0
- package/templates/commonproject/_bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-01-mode-detection.md +156 -0
- package/templates/commonproject/_bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-02-context-gathering.md +120 -0
- package/templates/commonproject/_bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-03-execute.md +113 -0
- package/templates/commonproject/_bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-04-self-check.md +113 -0
- package/templates/commonproject/_bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-05-adversarial-review.md +106 -0
- package/templates/commonproject/_bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-06-resolve-findings.md +140 -0
- package/templates/commonproject/_bmad/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md +52 -0
- package/templates/commonproject/_bmad/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-01-understand.md +189 -0
- package/templates/commonproject/_bmad/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-02-investigate.md +144 -0
- package/templates/commonproject/_bmad/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-03-generate.md +128 -0
- package/templates/commonproject/_bmad/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-04-review.md +173 -0
- package/templates/commonproject/_bmad/bmm/workflows/bmad-quick-flow/quick-spec/tech-spec-template.md +74 -0
- package/templates/commonproject/_bmad/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md +79 -0
- package/templates/commonproject/_bmad/bmm/workflows/document-project/checklist.md +245 -0
- package/templates/commonproject/_bmad/bmm/workflows/document-project/documentation-requirements.csv +12 -0
- package/templates/commonproject/_bmad/bmm/workflows/document-project/instructions.md +221 -0
- package/templates/commonproject/_bmad/bmm/workflows/document-project/templates/deep-dive-template.md +345 -0
- package/templates/commonproject/_bmad/bmm/workflows/document-project/templates/index-template.md +169 -0
- package/templates/commonproject/_bmad/bmm/workflows/document-project/templates/project-overview-template.md +103 -0
- package/templates/commonproject/_bmad/bmm/workflows/document-project/templates/project-scan-report-schema.json +160 -0
- package/templates/commonproject/_bmad/bmm/workflows/document-project/templates/source-tree-template.md +135 -0
- package/templates/commonproject/_bmad/bmm/workflows/document-project/workflow.yaml +28 -0
- package/templates/commonproject/_bmad/bmm/workflows/document-project/workflows/deep-dive-instructions.md +298 -0
- package/templates/commonproject/_bmad/bmm/workflows/document-project/workflows/deep-dive.yaml +31 -0
- package/templates/commonproject/_bmad/bmm/workflows/document-project/workflows/full-scan-instructions.md +1106 -0
- package/templates/commonproject/_bmad/bmm/workflows/document-project/workflows/full-scan.yaml +31 -0
- package/templates/commonproject/_bmad/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-library.json +90 -0
- package/templates/commonproject/_bmad/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-templates.yaml +127 -0
- package/templates/commonproject/_bmad/bmm/workflows/excalidraw-diagrams/create-dataflow/checklist.md +39 -0
- package/templates/commonproject/_bmad/bmm/workflows/excalidraw-diagrams/create-dataflow/instructions.md +130 -0
- package/templates/commonproject/_bmad/bmm/workflows/excalidraw-diagrams/create-dataflow/workflow.yaml +26 -0
- package/templates/commonproject/_bmad/bmm/workflows/excalidraw-diagrams/create-diagram/checklist.md +43 -0
- package/templates/commonproject/_bmad/bmm/workflows/excalidraw-diagrams/create-diagram/instructions.md +141 -0
- package/templates/commonproject/_bmad/bmm/workflows/excalidraw-diagrams/create-diagram/workflow.yaml +26 -0
- package/templates/commonproject/_bmad/bmm/workflows/excalidraw-diagrams/create-flowchart/checklist.md +49 -0
- package/templates/commonproject/_bmad/bmm/workflows/excalidraw-diagrams/create-flowchart/instructions.md +241 -0
- package/templates/commonproject/_bmad/bmm/workflows/excalidraw-diagrams/create-flowchart/workflow.yaml +26 -0
- package/templates/commonproject/_bmad/bmm/workflows/excalidraw-diagrams/create-wireframe/checklist.md +38 -0
- package/templates/commonproject/_bmad/bmm/workflows/excalidraw-diagrams/create-wireframe/instructions.md +133 -0
- package/templates/commonproject/_bmad/bmm/workflows/excalidraw-diagrams/create-wireframe/workflow.yaml +26 -0
- package/templates/commonproject/_bmad/bmm/workflows/generate-project-context/project-context-template.md +21 -0
- package/templates/commonproject/_bmad/bmm/workflows/generate-project-context/steps/step-01-discover.md +184 -0
- package/templates/commonproject/_bmad/bmm/workflows/generate-project-context/steps/step-02-generate.md +318 -0
- package/templates/commonproject/_bmad/bmm/workflows/generate-project-context/steps/step-03-complete.md +278 -0
- package/templates/commonproject/_bmad/bmm/workflows/generate-project-context/workflow.md +49 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/atdd/atdd-checklist-template.md +364 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/atdd/checklist.md +374 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/atdd/instructions.md +806 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/atdd/workflow.yaml +45 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/automate/checklist.md +582 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/automate/instructions.md +1324 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/automate/workflow.yaml +52 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/ci/checklist.md +248 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/ci/github-actions-template.yaml +198 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/ci/gitlab-ci-template.yaml +149 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/ci/instructions.md +536 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/ci/workflow.yaml +45 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/framework/checklist.md +321 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/framework/instructions.md +481 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/framework/workflow.yaml +47 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/nfr-assess/checklist.md +407 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/nfr-assess/instructions.md +722 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/nfr-assess/nfr-report-template.md +445 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/nfr-assess/workflow.yaml +47 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/test-design/checklist.md +235 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/test-design/instructions.md +788 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/test-design/test-design-template.md +294 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/test-design/workflow.yaml +54 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/test-review/checklist.md +472 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/test-review/instructions.md +628 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/test-review/test-review-template.md +390 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/test-review/workflow.yaml +46 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/trace/checklist.md +655 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/trace/instructions.md +1047 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/trace/trace-template.md +675 -0
- package/templates/commonproject/_bmad/bmm/workflows/testarch/trace/workflow.yaml +55 -0
- package/templates/commonproject/_bmad/bmm/workflows/workflow-status/init/instructions.md +346 -0
- package/templates/commonproject/_bmad/bmm/workflows/workflow-status/init/workflow.yaml +29 -0
- package/templates/commonproject/_bmad/bmm/workflows/workflow-status/instructions.md +395 -0
- package/templates/commonproject/_bmad/bmm/workflows/workflow-status/paths/enterprise-brownfield.yaml +103 -0
- package/templates/commonproject/_bmad/bmm/workflows/workflow-status/paths/enterprise-greenfield.yaml +100 -0
- package/templates/commonproject/_bmad/bmm/workflows/workflow-status/paths/method-brownfield.yaml +103 -0
- package/templates/commonproject/_bmad/bmm/workflows/workflow-status/paths/method-greenfield.yaml +100 -0
- package/templates/commonproject/_bmad/bmm/workflows/workflow-status/project-levels.yaml +59 -0
- package/templates/commonproject/_bmad/bmm/workflows/workflow-status/workflow-status-template.yaml +24 -0
- package/templates/commonproject/_bmad/bmm/workflows/workflow-status/workflow.yaml +30 -0
- package/templates/commonproject/_bmad/cis/agents/brainstorming-coach.md +66 -0
- package/templates/commonproject/_bmad/cis/agents/creative-problem-solver.md +66 -0
- package/templates/commonproject/_bmad/cis/agents/design-thinking-coach.md +66 -0
- package/templates/commonproject/_bmad/cis/agents/innovation-strategist.md +66 -0
- package/templates/commonproject/_bmad/cis/agents/presentation-master.md +72 -0
- package/templates/commonproject/_bmad/cis/agents/storyteller/storyteller.md +63 -0
- package/templates/commonproject/_bmad/cis/config.yaml +11 -0
- package/templates/commonproject/_bmad/cis/teams/creative-squad.yaml +7 -0
- package/templates/commonproject/_bmad/cis/teams/default-party.csv +12 -0
- package/templates/commonproject/_bmad/cis/workflows/README.md +139 -0
- package/templates/commonproject/_bmad/cis/workflows/design-thinking/README.md +56 -0
- package/templates/commonproject/_bmad/cis/workflows/design-thinking/design-methods.csv +31 -0
- package/templates/commonproject/_bmad/cis/workflows/design-thinking/instructions.md +202 -0
- package/templates/commonproject/_bmad/cis/workflows/design-thinking/template.md +111 -0
- package/templates/commonproject/_bmad/cis/workflows/design-thinking/workflow.yaml +27 -0
- package/templates/commonproject/_bmad/cis/workflows/innovation-strategy/README.md +56 -0
- package/templates/commonproject/_bmad/cis/workflows/innovation-strategy/innovation-frameworks.csv +31 -0
- package/templates/commonproject/_bmad/cis/workflows/innovation-strategy/instructions.md +276 -0
- package/templates/commonproject/_bmad/cis/workflows/innovation-strategy/template.md +189 -0
- package/templates/commonproject/_bmad/cis/workflows/innovation-strategy/workflow.yaml +27 -0
- package/templates/commonproject/_bmad/cis/workflows/problem-solving/README.md +56 -0
- package/templates/commonproject/_bmad/cis/workflows/problem-solving/instructions.md +252 -0
- package/templates/commonproject/_bmad/cis/workflows/problem-solving/solving-methods.csv +31 -0
- package/templates/commonproject/_bmad/cis/workflows/problem-solving/template.md +165 -0
- package/templates/commonproject/_bmad/cis/workflows/problem-solving/workflow.yaml +27 -0
- package/templates/commonproject/_bmad/cis/workflows/storytelling/README.md +58 -0
- package/templates/commonproject/_bmad/cis/workflows/storytelling/instructions.md +293 -0
- package/templates/commonproject/_bmad/cis/workflows/storytelling/story-types.csv +26 -0
- package/templates/commonproject/_bmad/cis/workflows/storytelling/template.md +113 -0
- package/templates/commonproject/_bmad/cis/workflows/storytelling/workflow.yaml +27 -0
- package/templates/commonproject/_bmad/core/agents/bmad-master.md +63 -0
- package/templates/commonproject/_bmad/core/config.yaml +9 -0
- package/templates/commonproject/_bmad/core/resources/excalidraw/README.md +160 -0
- package/templates/commonproject/_bmad/core/resources/excalidraw/excalidraw-helpers.md +127 -0
- package/templates/commonproject/_bmad/core/resources/excalidraw/library-loader.md +50 -0
- package/templates/commonproject/_bmad/core/resources/excalidraw/validate-json-instructions.md +79 -0
- package/templates/commonproject/_bmad/core/tasks/index-docs.xml +65 -0
- package/templates/commonproject/_bmad/core/tasks/review-adversarial-general.xml +41 -0
- package/templates/commonproject/_bmad/core/tasks/shard-doc.xml +109 -0
- package/templates/commonproject/_bmad/core/tasks/workflow.xml +235 -0
- package/templates/commonproject/_bmad/core/workflows/advanced-elicitation/methods.csv +51 -0
- package/templates/commonproject/_bmad/core/workflows/advanced-elicitation/workflow.xml +117 -0
- package/templates/commonproject/_bmad/core/workflows/brainstorming/brain-methods.csv +62 -0
- package/templates/commonproject/_bmad/core/workflows/brainstorming/steps/step-01-session-setup.md +197 -0
- package/templates/commonproject/_bmad/core/workflows/brainstorming/steps/step-01b-continue.md +122 -0
- package/templates/commonproject/_bmad/core/workflows/brainstorming/steps/step-02a-user-selected.md +225 -0
- package/templates/commonproject/_bmad/core/workflows/brainstorming/steps/step-02b-ai-recommended.md +237 -0
- package/templates/commonproject/_bmad/core/workflows/brainstorming/steps/step-02c-random-selection.md +209 -0
- package/templates/commonproject/_bmad/core/workflows/brainstorming/steps/step-02d-progressive-flow.md +264 -0
- package/templates/commonproject/_bmad/core/workflows/brainstorming/steps/step-03-technique-execution.md +399 -0
- package/templates/commonproject/_bmad/core/workflows/brainstorming/steps/step-04-idea-organization.md +303 -0
- package/templates/commonproject/_bmad/core/workflows/brainstorming/template.md +15 -0
- package/templates/commonproject/_bmad/core/workflows/brainstorming/workflow.md +58 -0
- package/templates/commonproject/_bmad/core/workflows/party-mode/steps/step-01-agent-loading.md +139 -0
- package/templates/commonproject/_bmad/core/workflows/party-mode/steps/step-02-discussion-orchestration.md +204 -0
- package/templates/commonproject/_bmad/core/workflows/party-mode/steps/step-03-graceful-exit.md +159 -0
- package/templates/commonproject/_bmad/core/workflows/party-mode/workflow.md +206 -0
- package/templates/commonproject/_bmad/custom/config.yaml +11 -0
- package/templates/commonproject/_bmad/custom/workflows/ticket-lifecycle/data/ac-sufficiency-rubric.md +33 -0
- package/templates/commonproject/_bmad/custom/workflows/ticket-lifecycle/data/audit-comment-template.md +54 -0
- package/templates/commonproject/_bmad/custom/workflows/ticket-lifecycle/data/event-schemas.md +58 -0
- package/templates/commonproject/_bmad/custom/workflows/ticket-lifecycle/steps-c/step-01-init.md +173 -0
- package/templates/commonproject/_bmad/custom/workflows/ticket-lifecycle/steps-c/step-02-triage.md +159 -0
- package/templates/commonproject/_bmad/custom/workflows/ticket-lifecycle/steps-c/step-03-refine.md +149 -0
- package/templates/commonproject/_bmad/custom/workflows/ticket-lifecycle/steps-c/step-04-implement.md +147 -0
- package/templates/commonproject/_bmad/custom/workflows/ticket-lifecycle/steps-c/step-05-review.md +146 -0
- package/templates/commonproject/_bmad/custom/workflows/ticket-lifecycle/steps-c/step-06-qa.md +199 -0
- package/templates/commonproject/_bmad/custom/workflows/ticket-lifecycle/steps-c/step-07-complete.md +141 -0
- package/templates/commonproject/_bmad/custom/workflows/ticket-lifecycle/steps-e/step-01-assess.md +102 -0
- package/templates/commonproject/_bmad/custom/workflows/ticket-lifecycle/steps-e/step-02-apply.md +85 -0
- package/templates/commonproject/_bmad/custom/workflows/ticket-lifecycle/steps-v/step-01-validate.md +154 -0
- package/templates/commonproject/_bmad/custom/workflows/ticket-lifecycle/workflow-plan-ticket-lifecycle.md +346 -0
- package/templates/commonproject/_bmad/custom/workflows/ticket-lifecycle/workflow.md +84 -0
- package/templates/commonproject/_bmad/custom/workflows/ticket-lifecycle/workflow.yaml +39 -0
- package/templates/commonproject/copier.yml +90 -0
- package/templates/commonproject/mise.toml +38 -0
- package/templates/commonproject/template/.copier-answers.yml.jinja +2 -0
- package/templates/commonproject/template/.env.op +2 -0
- package/templates/commonproject/template/.mise/scripts/link-agentfiles.sh +9 -0
- package/templates/commonproject/template/.project.json.jinja +19 -0
- package/templates/commonproject/template/AGENTS.md.jinja +7 -0
- package/templates/commonproject/template/mise.toml.jinja +26 -0
- package/templates/hermes-agent/README.md +149 -0
- package/templates/hermes-agent/copier.yml +167 -0
- package/templates/hermes-agent/docs/architecture.md +111 -0
- package/templates/hermes-agent/docs/operations.md +238 -0
- package/templates/hermes-agent/docs/runbooks/runtime-checkpoint-repair.md +167 -0
- package/templates/hermes-agent/docs/scrum-master/README.md +118 -0
- package/templates/hermes-agent/docs/scrum-master/architecture.md +203 -0
- package/templates/hermes-agent/docs/scrum-master/development.md +313 -0
- package/templates/hermes-agent/docs/scrum-master/providers.md +203 -0
- package/templates/hermes-agent/docs/scrum-master-sentinel.md +17 -0
- package/templates/hermes-agent/install-local.sh +214 -0
- package/templates/hermes-agent/runtime-scaffold/.gitattributes +10 -0
- package/templates/hermes-agent/runtime-scaffold/README.md +52 -0
- package/templates/hermes-agent/runtime-scaffold/bloodbank-consumer.py +147 -0
- package/templates/hermes-agent/runtime-scaffold/decisions/.gitkeep +2 -0
- package/templates/hermes-agent/runtime-scaffold/logs/.gitkeep +1 -0
- package/templates/hermes-agent/runtime-scaffold/memories/MEMORY.md +47 -0
- package/templates/hermes-agent/runtime-scaffold/memories/USER.md +51 -0
- package/templates/hermes-agent/scripts/backfill-fleet-sot.sh +279 -0
- package/templates/hermes-agent/scripts/repair-runtime-checkpoint.sh +122 -0
- package/templates/hermes-agent/scripts/retrofit-70-envfile.sh +129 -0
- package/templates/hermes-agent/template/.gitignore.jinja +6 -0
- package/templates/hermes-agent/template/.runtime-scaffold/.gitattributes +10 -0
- package/templates/hermes-agent/template/.runtime-scaffold/README.md +52 -0
- package/templates/hermes-agent/template/.runtime-scaffold/bloodbank-consumer.py +147 -0
- package/templates/hermes-agent/template/.runtime-scaffold/decisions/.gitkeep +2 -0
- package/templates/hermes-agent/template/.runtime-scaffold/logs/.gitkeep +1 -0
- package/templates/hermes-agent/template/.runtime-scaffold/memories/MEMORY.md +47 -0
- package/templates/hermes-agent/template/.runtime-scaffold/memories/USER.md +51 -0
- package/templates/hermes-agent/template/.scripts/00-banner.sh +21 -0
- package/templates/hermes-agent/template/.scripts/01-config.sh +49 -0
- package/templates/hermes-agent/template/.scripts/05-fleet-env.sh +54 -0
- package/templates/hermes-agent/template/.scripts/10-hermes-profile.sh +130 -0
- package/templates/hermes-agent/template/.scripts/20-runtime-repo.sh +144 -0
- package/templates/hermes-agent/template/.scripts/30-telegram.sh +84 -0
- package/templates/hermes-agent/template/.scripts/40-plane.sh +51 -0
- package/templates/hermes-agent/template/.scripts/42-ticket-provider.sh +187 -0
- package/templates/hermes-agent/template/.scripts/60-bloodbank.sh +75 -0
- package/templates/hermes-agent/template/.scripts/70-systemd.sh +120 -0
- package/templates/hermes-agent/template/.scripts/75-scrum-master.sh +117 -0
- package/templates/hermes-agent/template/.scripts/80-registry.sh +53 -0
- package/templates/hermes-agent/template/.scripts/90-chain-scrum-master.sh +45 -0
- package/templates/hermes-agent/template/.scripts/99-summary.sh +38 -0
- package/templates/hermes-agent/template/.scripts/_lib.sh +208 -0
- package/templates/hermes-agent/template/.scripts/checkpoint.sh +17 -0
- package/templates/hermes-agent/template/.scripts/config.example.toml +55 -0
- package/templates/hermes-agent/template/.scripts/lib/ticket-provider.sh +102 -0
- package/templates/hermes-agent/template/.scripts/providers/linear.sh +176 -0
- package/templates/hermes-agent/template/.scripts/providers/plane.sh +196 -0
- package/templates/hermes-agent/template/.scripts/providers/trello.sh +160 -0
- package/templates/hermes-agent/template/.scripts/scrum-master/bin/emit-event.py +106 -0
- package/templates/hermes-agent/template/.scripts/scrum-master/bin/issue-autonomous-review.sh +125 -0
- package/templates/hermes-agent/template/.scripts/scrum-master/bin/issue-close-gate.sh +62 -0
- package/templates/hermes-agent/template/.scripts/scrum-master/continuous-ticket-sentinel.prompt.md.jinja +94 -0
- package/templates/hermes-agent/template/.scripts/scrum-master/continuous-ticket-sentinel.sh +207 -0
- package/templates/hermes-agent/template/.scripts/scrum-master/docs/autonomous-delegated-review.md +168 -0
- package/templates/hermes-agent/template/.scripts/scrum-master/docs/bloodbank-events.md +49 -0
- package/templates/hermes-agent/template/.scripts/scrum-master/docs/continuous-ticket-orchestration.md +75 -0
- package/templates/hermes-agent/template/SOUL.md.jinja +119 -0
- package/templates/hermes-agent/template/hermes.jinja +74 -0
- package/templates/hermes-agent/template/role.yaml.jinja +75 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2620 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
+
}) : x)(function(x) {
|
|
5
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
// src/index.ts
|
|
10
|
+
import { Command as Command3 } from "commander";
|
|
11
|
+
|
|
12
|
+
// src/commands/hermes/types.ts
|
|
13
|
+
var HERMES_AGENT_TEMPLATE = "gh:delorenj/hermes-agent-template";
|
|
14
|
+
var SOUL_TONES = ["direct", "playful", "formal", "terse"];
|
|
15
|
+
var ROLE_CHOICES = [
|
|
16
|
+
{ value: "pm", label: "Project Manager (pm)", hint: "triage, planning, ticket authorship" },
|
|
17
|
+
{
|
|
18
|
+
value: "scrum-master",
|
|
19
|
+
label: "Scrum Master (Ticket Sentinel)",
|
|
20
|
+
hint: "continuous ticket sentinel + autonomous delegated review"
|
|
21
|
+
},
|
|
22
|
+
{ value: "dev", label: "Developer (dev)", hint: "implements tickets" },
|
|
23
|
+
{ value: "review", label: "Reviewer (review)", hint: "adversarial code review" },
|
|
24
|
+
{ value: "ops", label: "Ops (ops)", hint: "deploy / infra" },
|
|
25
|
+
{ value: "qa", label: "QA (qa)", hint: "test authorship + verification" }
|
|
26
|
+
];
|
|
27
|
+
var TICKET_PROVIDERS = [
|
|
28
|
+
{ value: "plane", label: "Plane", hint: "self-hosted at plane.delo.sh (default)" },
|
|
29
|
+
{ value: "linear", label: "Linear", hint: "team board (created in Linear UI)" },
|
|
30
|
+
{ value: "trello", label: "Trello", hint: "board = project" }
|
|
31
|
+
];
|
|
32
|
+
function deriveAgentId(repo, role) {
|
|
33
|
+
return `${repo}-${role}`.toLowerCase();
|
|
34
|
+
}
|
|
35
|
+
function deriveProfileName(repo, role) {
|
|
36
|
+
return deriveAgentId(repo, role);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// src/commands/hermes/EnsureTemplateConfig.ts
|
|
40
|
+
import { homedir, platform } from "node:os";
|
|
41
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
42
|
+
import { join, dirname } from "node:path";
|
|
43
|
+
|
|
44
|
+
// src/commands/Command.ts
|
|
45
|
+
var Command = class {
|
|
46
|
+
context;
|
|
47
|
+
constructor(context) {
|
|
48
|
+
this.context = context;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Format message with [DRY RUN] prefix if in dry-run mode
|
|
52
|
+
*/
|
|
53
|
+
formatMessage(message) {
|
|
54
|
+
return this.context.dryRun ? `[DRY RUN] ${message}` : message;
|
|
55
|
+
}
|
|
56
|
+
fileExists(filePath) {
|
|
57
|
+
const { existsSync: existsSync6 } = __require("fs");
|
|
58
|
+
const { join: join7 } = __require("path");
|
|
59
|
+
const fullPath = join7(this.context.targetDir, filePath);
|
|
60
|
+
return existsSync6(fullPath);
|
|
61
|
+
}
|
|
62
|
+
writeFile(filePath, content) {
|
|
63
|
+
if (this.context.dryRun) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const { writeFileSync: writeFileSync3, mkdirSync: mkdirSync4 } = __require("fs");
|
|
67
|
+
const { join: join7, dirname: dirname4 } = __require("path");
|
|
68
|
+
const fullPath = join7(this.context.targetDir, filePath);
|
|
69
|
+
const dir = dirname4(fullPath);
|
|
70
|
+
mkdirSync4(dir, { recursive: true });
|
|
71
|
+
writeFileSync3(fullPath, content);
|
|
72
|
+
}
|
|
73
|
+
createDirectory(dirPath) {
|
|
74
|
+
if (this.context.dryRun) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const { mkdirSync: mkdirSync4 } = __require("fs");
|
|
78
|
+
const { join: join7 } = __require("path");
|
|
79
|
+
const fullPath = join7(this.context.targetDir, dirPath);
|
|
80
|
+
mkdirSync4(fullPath, { recursive: true });
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// src/commands/hermes/EnsureTemplateConfig.ts
|
|
85
|
+
function resolveTemplateConfigPath() {
|
|
86
|
+
const fromEnv = process.env.HERMES_TEMPLATE_CONFIG;
|
|
87
|
+
if (fromEnv && fromEnv.trim()) return fromEnv.trim();
|
|
88
|
+
const xdg = process.env.XDG_CONFIG_HOME?.trim();
|
|
89
|
+
const base = xdg && xdg.length ? xdg : join(homedir(), ".config");
|
|
90
|
+
return join(base, "hermes-agent-template", "config.toml");
|
|
91
|
+
}
|
|
92
|
+
function detectHermesBin(home) {
|
|
93
|
+
const candidates = [
|
|
94
|
+
join(home, "code", "hermes-agent", "venv", "bin", "hermes"),
|
|
95
|
+
join(home, "code", "hermes-agent", ".venv", "bin", "hermes"),
|
|
96
|
+
join(home, ".local", "bin", "hermes")
|
|
97
|
+
];
|
|
98
|
+
for (const c of candidates) {
|
|
99
|
+
if (existsSync(c)) return c;
|
|
100
|
+
}
|
|
101
|
+
return candidates[0];
|
|
102
|
+
}
|
|
103
|
+
function renderHostConfig() {
|
|
104
|
+
const home = homedir();
|
|
105
|
+
const hermesBin = detectHermesBin(home);
|
|
106
|
+
const hermesRepo = join(home, "code", "hermes-agent");
|
|
107
|
+
const scaffoldDir = join(home, "code", "hermes-agent-template", "runtime-scaffold");
|
|
108
|
+
const skillsDir = join(home, ".agents", "skills");
|
|
109
|
+
return `# hermes-agent-template \u2014 host configuration
|
|
110
|
+
# Bootstrapped by \`pjangler config bootstrap\` for $HOME=${home} (platform=${platform()}).
|
|
111
|
+
#
|
|
112
|
+
# [fleet] paths below were derived from THIS machine. The identity values in
|
|
113
|
+
# [github]/[plane]/[bloodbank] are intentionally left to be confirmed before a
|
|
114
|
+
# CLOUD provision (\`pjangler hermes\` without --local); they are unused by the
|
|
115
|
+
# default local-only provision.
|
|
116
|
+
#
|
|
117
|
+
# Resolution precedence per value: env var > ~/.hermes/fleet.env > this file > fallback.
|
|
118
|
+
|
|
119
|
+
[fleet]
|
|
120
|
+
home = "~/.hermes"
|
|
121
|
+
hermes_bin = "${hermesBin}"
|
|
122
|
+
hermes_repo = "${hermesRepo}"
|
|
123
|
+
runtime_scaffold_dir = "${scaffoldDir}"
|
|
124
|
+
fleet_env = "~/.hermes/fleet.env"
|
|
125
|
+
registry_file = "~/.hermes/agents-registry.yaml"
|
|
126
|
+
canonical_skills_dir = "${skillsDir}"
|
|
127
|
+
symlinked_runtime_skills = []
|
|
128
|
+
|
|
129
|
+
[github]
|
|
130
|
+
# Owner of the per-agent runtime repos (creates <owner>/agent-hm-<repo>-<role>).
|
|
131
|
+
# REQUIRED before a cloud provision. Leave empty for local-only runs.
|
|
132
|
+
runtime_repo_owner = ""
|
|
133
|
+
|
|
134
|
+
[plane]
|
|
135
|
+
# Plane instance + workspace (one project per agent). Confirm before cloud provision.
|
|
136
|
+
base = "https://plane.delo.sh"
|
|
137
|
+
workspace = "33god"
|
|
138
|
+
|
|
139
|
+
[bloodbank]
|
|
140
|
+
# NATS endpoint the consumer connects to. For a remote fleet node, point this at
|
|
141
|
+
# the bloodbank host over Tailscale rather than localhost.
|
|
142
|
+
nats_host = "127.0.0.1"
|
|
143
|
+
nats_port = 4222
|
|
144
|
+
compose_dir = "~/code/33GOD/bloodbank"
|
|
145
|
+
`;
|
|
146
|
+
}
|
|
147
|
+
var EnsureTemplateConfig = class extends Command {
|
|
148
|
+
async invoke() {
|
|
149
|
+
const ctx = this.context;
|
|
150
|
+
const force = ctx.forceConfig === true || process.env.PJANGLER_FORCE_CONFIG === "1";
|
|
151
|
+
const path = resolveTemplateConfigPath();
|
|
152
|
+
const exists = existsSync(path);
|
|
153
|
+
if (exists && !force) {
|
|
154
|
+
console.log(`\u2713 Config present: ${path}`);
|
|
155
|
+
return { success: true, message: "" };
|
|
156
|
+
}
|
|
157
|
+
if (ctx.dryRun) {
|
|
158
|
+
console.log(`[DRY RUN] Would ${exists ? "overwrite" : "create"} config: ${path}`);
|
|
159
|
+
return { success: true, message: "" };
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
163
|
+
writeFileSync(path, renderHostConfig());
|
|
164
|
+
} catch (err) {
|
|
165
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
166
|
+
return { success: false, message: `\u2717 Failed to write ${path}: ${msg}` };
|
|
167
|
+
}
|
|
168
|
+
console.log(`\u2713 Bootstrapped config: ${path}`);
|
|
169
|
+
console.log(" Review [github].runtime_repo_owner + [plane] + [bloodbank] before a cloud provision.");
|
|
170
|
+
return { success: true, message: "" };
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// src/recipes/Recipe.ts
|
|
175
|
+
var Recipe = class {
|
|
176
|
+
context;
|
|
177
|
+
ingredients = [];
|
|
178
|
+
constructor(context) {
|
|
179
|
+
this.context = context;
|
|
180
|
+
}
|
|
181
|
+
addIngredient(CommandClass) {
|
|
182
|
+
this.ingredients.push(new CommandClass(this.context));
|
|
183
|
+
return this;
|
|
184
|
+
}
|
|
185
|
+
async execute() {
|
|
186
|
+
const dryRunPrefix = this.context.dryRun ? "[DRY RUN] " : "";
|
|
187
|
+
console.log(`${dryRunPrefix}\u{1F680} Initializing ${this.constructor.name.replace("Recipe", "").toLowerCase()} subsystem...`);
|
|
188
|
+
if (this.context.dryRun) {
|
|
189
|
+
console.log("\u26A0\uFE0F Dry-run mode: No files will be modified");
|
|
190
|
+
console.log("");
|
|
191
|
+
}
|
|
192
|
+
for (const command of this.ingredients) {
|
|
193
|
+
const result = await command.invoke();
|
|
194
|
+
if (result.success) {
|
|
195
|
+
console.log(result.message);
|
|
196
|
+
} else {
|
|
197
|
+
console.log(result.message);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (!this.context.dryRun) {
|
|
201
|
+
this.printNextSteps();
|
|
202
|
+
} else {
|
|
203
|
+
console.log("");
|
|
204
|
+
console.log("\u2713 Dry-run complete - no files were modified");
|
|
205
|
+
console.log(" Remove --dry-run flag to apply changes");
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// src/commands/AddMiseToml.ts
|
|
211
|
+
var AddMiseToml = class extends Command {
|
|
212
|
+
async invoke() {
|
|
213
|
+
const filePath = "mise.toml";
|
|
214
|
+
if (this.fileExists(filePath) && !this.context.force) {
|
|
215
|
+
return {
|
|
216
|
+
success: false,
|
|
217
|
+
message: this.formatMessage("\u26A0\uFE0F mise.toml already exists"),
|
|
218
|
+
filePath
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
const content = `# Mise configuration
|
|
222
|
+
[tools]
|
|
223
|
+
python = "3.11"
|
|
224
|
+
node = "20"
|
|
225
|
+
|
|
226
|
+
[env]
|
|
227
|
+
NODE_ENV = "development"
|
|
228
|
+
`;
|
|
229
|
+
this.writeFile(filePath, content);
|
|
230
|
+
return {
|
|
231
|
+
success: true,
|
|
232
|
+
message: this.formatMessage(this.context.dryRun ? "Would create mise.toml" : "\u2705 Created mise.toml"),
|
|
233
|
+
filePath
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
// src/commands/AddDotenv.ts
|
|
239
|
+
var AddDotenv = class extends Command {
|
|
240
|
+
async invoke() {
|
|
241
|
+
const filePath = ".env";
|
|
242
|
+
if (this.fileExists(filePath) && !this.context.force) {
|
|
243
|
+
return {
|
|
244
|
+
success: false,
|
|
245
|
+
message: this.formatMessage("\u26A0\uFE0F .env already exists"),
|
|
246
|
+
filePath
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
const content = `# Environment variables
|
|
250
|
+
DATABASE_URL=""
|
|
251
|
+
API_KEY=""
|
|
252
|
+
SECRET_KEY=""
|
|
253
|
+
`;
|
|
254
|
+
this.writeFile(filePath, content);
|
|
255
|
+
return {
|
|
256
|
+
success: true,
|
|
257
|
+
message: this.formatMessage(this.context.dryRun ? "Would create .env" : "\u2705 Created .env"),
|
|
258
|
+
filePath
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// src/commands/AddMiseTasksStructure.ts
|
|
264
|
+
var AddMiseTasksStructure = class extends Command {
|
|
265
|
+
async invoke() {
|
|
266
|
+
this.createDirectory(".mise/tasks/scripts");
|
|
267
|
+
return {
|
|
268
|
+
success: true,
|
|
269
|
+
message: this.formatMessage(this.context.dryRun ? "Would create .mise directory structure" : "\u2705 Created .mise directory structure"),
|
|
270
|
+
filePath: ".mise/tasks/scripts"
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
// src/commands/AddMiseBaseToml.ts
|
|
276
|
+
var AddMiseBaseToml = class extends Command {
|
|
277
|
+
async invoke() {
|
|
278
|
+
const filePath = ".mise/tasks/base.toml";
|
|
279
|
+
if (this.fileExists(filePath) && !this.context.force) {
|
|
280
|
+
return {
|
|
281
|
+
success: false,
|
|
282
|
+
message: this.formatMessage("\u26A0\uFE0F .mise/tasks/base.toml already exists"),
|
|
283
|
+
filePath
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
const content = `# Base tasks configuration
|
|
287
|
+
[tasks.setup]
|
|
288
|
+
run = "python scripts/base.py"
|
|
289
|
+
description = "Setup base environment"
|
|
290
|
+
|
|
291
|
+
[tasks.clean]
|
|
292
|
+
run = "rm -rf node_modules dist build"
|
|
293
|
+
description = "Clean build artifacts"
|
|
294
|
+
|
|
295
|
+
[tasks.dev]
|
|
296
|
+
run = "mise run setup"
|
|
297
|
+
description = "Initialize development environment"
|
|
298
|
+
`;
|
|
299
|
+
this.writeFile(filePath, content);
|
|
300
|
+
return {
|
|
301
|
+
success: true,
|
|
302
|
+
message: this.formatMessage(this.context.dryRun ? "Would create .mise/tasks/base.toml" : "\u2705 Created .mise/tasks/base.toml"),
|
|
303
|
+
filePath
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
// src/commands/AddMiseBaseScript.ts
|
|
309
|
+
var AddMiseBaseScript = class extends Command {
|
|
310
|
+
async invoke() {
|
|
311
|
+
const filePath = ".mise/tasks/scripts/base.py";
|
|
312
|
+
if (this.fileExists(filePath) && !this.context.force) {
|
|
313
|
+
return {
|
|
314
|
+
success: false,
|
|
315
|
+
message: this.formatMessage("\u26A0\uFE0F .mise/tasks/scripts/base.py already exists"),
|
|
316
|
+
filePath
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
const content = `#!/usr/bin/env python3
|
|
320
|
+
"""Base setup script"""
|
|
321
|
+
import os
|
|
322
|
+
import sys
|
|
323
|
+
from pathlib import Path
|
|
324
|
+
|
|
325
|
+
def main():
|
|
326
|
+
print("\u{1F527} Setting up base environment...")
|
|
327
|
+
|
|
328
|
+
dirs_to_create = ["logs", "temp", "data"]
|
|
329
|
+
for dir_name in dirs_to_create:
|
|
330
|
+
Path(dir_name).mkdir(exist_ok=True)
|
|
331
|
+
print(f" Created {dir_name}/ directory")
|
|
332
|
+
|
|
333
|
+
print(" Base environment setup complete!")
|
|
334
|
+
print(" Run 'mise run dev' to start development")
|
|
335
|
+
|
|
336
|
+
if __name__ == "__main__":
|
|
337
|
+
main()
|
|
338
|
+
`;
|
|
339
|
+
this.writeFile(filePath, content);
|
|
340
|
+
return {
|
|
341
|
+
success: true,
|
|
342
|
+
message: this.formatMessage(this.context.dryRun ? "Would create .mise/tasks/scripts/base.py" : "\u2705 Created .mise/tasks/scripts/base.py"),
|
|
343
|
+
filePath
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
// src/recipes/MiseRecipe.ts
|
|
349
|
+
var MiseRecipe = class extends Recipe {
|
|
350
|
+
constructor(context) {
|
|
351
|
+
super(context);
|
|
352
|
+
this.addIngredient(AddMiseToml).addIngredient(AddDotenv).addIngredient(AddMiseTasksStructure).addIngredient(AddMiseBaseToml).addIngredient(AddMiseBaseScript);
|
|
353
|
+
}
|
|
354
|
+
printNextSteps() {
|
|
355
|
+
console.log("\u{1F389} Mise subsystem initialized successfully!");
|
|
356
|
+
console.log(" Next steps:");
|
|
357
|
+
console.log(" 1. mise install");
|
|
358
|
+
console.log(" 2. mise run dev");
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
// src/commands/AddDockerfile.ts
|
|
363
|
+
var AddDockerfile = class extends Command {
|
|
364
|
+
async invoke() {
|
|
365
|
+
const filePath = "Dockerfile";
|
|
366
|
+
if (this.fileExists(filePath) && !this.context.force) {
|
|
367
|
+
return {
|
|
368
|
+
success: false,
|
|
369
|
+
message: this.formatMessage("\u26A0\uFE0F Dockerfile already exists"),
|
|
370
|
+
filePath
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
const content = `FROM node:20-alpine
|
|
374
|
+
|
|
375
|
+
WORKDIR /app
|
|
376
|
+
|
|
377
|
+
COPY package.json bun.lockb ./
|
|
378
|
+
RUN bun install
|
|
379
|
+
|
|
380
|
+
COPY . .
|
|
381
|
+
|
|
382
|
+
RUN bun run build
|
|
383
|
+
|
|
384
|
+
EXPOSE 3000
|
|
385
|
+
|
|
386
|
+
CMD ["bun", "run", "start"]
|
|
387
|
+
`;
|
|
388
|
+
this.writeFile(filePath, content);
|
|
389
|
+
return {
|
|
390
|
+
success: true,
|
|
391
|
+
message: this.formatMessage(this.context.dryRun ? "Would create Dockerfile" : "\u2705 Created Dockerfile"),
|
|
392
|
+
filePath
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
// src/commands/AddDockerCompose.ts
|
|
398
|
+
var AddDockerCompose = class extends Command {
|
|
399
|
+
async invoke() {
|
|
400
|
+
const filePath = "docker-compose.yml";
|
|
401
|
+
if (this.fileExists(filePath) && !this.context.force) {
|
|
402
|
+
return {
|
|
403
|
+
success: false,
|
|
404
|
+
message: this.formatMessage("\u26A0\uFE0F docker-compose.yml already exists"),
|
|
405
|
+
filePath
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
const content = `version: '3.8'
|
|
409
|
+
|
|
410
|
+
services:
|
|
411
|
+
app:
|
|
412
|
+
build: .
|
|
413
|
+
ports:
|
|
414
|
+
- "3000:3000"
|
|
415
|
+
environment:
|
|
416
|
+
- NODE_ENV=production
|
|
417
|
+
volumes:
|
|
418
|
+
- ./logs:/app/logs
|
|
419
|
+
restart: unless-stopped
|
|
420
|
+
|
|
421
|
+
redis:
|
|
422
|
+
image: redis:7-alpine
|
|
423
|
+
ports:
|
|
424
|
+
- "6379:6379"
|
|
425
|
+
volumes:
|
|
426
|
+
- redis_data:/data
|
|
427
|
+
restart: unless-stopped
|
|
428
|
+
|
|
429
|
+
volumes:
|
|
430
|
+
redis_data:
|
|
431
|
+
`;
|
|
432
|
+
this.writeFile(filePath, content);
|
|
433
|
+
return {
|
|
434
|
+
success: true,
|
|
435
|
+
message: this.formatMessage(this.context.dryRun ? "Would create docker-compose.yml" : "\u2705 Created docker-compose.yml"),
|
|
436
|
+
filePath
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
// src/commands/AddDockerignore.ts
|
|
442
|
+
var AddDockerignore = class extends Command {
|
|
443
|
+
async invoke() {
|
|
444
|
+
const filePath = ".dockerignore";
|
|
445
|
+
if (this.fileExists(filePath) && !this.context.force) {
|
|
446
|
+
return {
|
|
447
|
+
success: false,
|
|
448
|
+
message: this.formatMessage("\u26A0\uFE0F .dockerignore already exists"),
|
|
449
|
+
filePath
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
const content = `node_modules
|
|
453
|
+
npm-debug.log
|
|
454
|
+
dist
|
|
455
|
+
build
|
|
456
|
+
.env
|
|
457
|
+
.git
|
|
458
|
+
*.md
|
|
459
|
+
`;
|
|
460
|
+
this.writeFile(filePath, content);
|
|
461
|
+
return {
|
|
462
|
+
success: true,
|
|
463
|
+
message: this.formatMessage(this.context.dryRun ? "Would create .dockerignore" : "\u2705 Created .dockerignore"),
|
|
464
|
+
filePath
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
// src/recipes/DockerRecipe.ts
|
|
470
|
+
var DockerRecipe = class extends Recipe {
|
|
471
|
+
constructor(context) {
|
|
472
|
+
super(context);
|
|
473
|
+
this.addIngredient(AddDockerfile).addIngredient(AddDockerCompose).addIngredient(AddDockerignore);
|
|
474
|
+
}
|
|
475
|
+
printNextSteps() {
|
|
476
|
+
console.log("\u{1F389} Docker subsystem initialized successfully!");
|
|
477
|
+
console.log(" Next steps:");
|
|
478
|
+
console.log(" 1. docker-compose up -d");
|
|
479
|
+
console.log(" 2. docker-compose logs -f");
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
// src/commands/NodeCommands.ts
|
|
484
|
+
var AddPackageJson = class extends Command {
|
|
485
|
+
async invoke() {
|
|
486
|
+
const filePath = "package.json";
|
|
487
|
+
if (this.fileExists(filePath) && !this.context.force) {
|
|
488
|
+
return {
|
|
489
|
+
success: false,
|
|
490
|
+
message: "\u26A0\uFE0F package.json already exists",
|
|
491
|
+
filePath
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
const content = `{
|
|
495
|
+
"name": "my-project",
|
|
496
|
+
"version": "1.0.0",
|
|
497
|
+
"description": "A new project",
|
|
498
|
+
"main": "index.js",
|
|
499
|
+
"scripts": {
|
|
500
|
+
"start": "node index.js",
|
|
501
|
+
"dev": "node --watch index.js",
|
|
502
|
+
"test": "echo \\"Error: no test specified\\" && exit 1"
|
|
503
|
+
},
|
|
504
|
+
"keywords": [],
|
|
505
|
+
"author": "",
|
|
506
|
+
"license": "ISC"
|
|
507
|
+
}
|
|
508
|
+
`;
|
|
509
|
+
this.writeFile(filePath, content);
|
|
510
|
+
return {
|
|
511
|
+
success: true,
|
|
512
|
+
message: "\u2705 Created package.json",
|
|
513
|
+
filePath
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
var AddReadme = class extends Command {
|
|
518
|
+
async invoke() {
|
|
519
|
+
const filePath = "README.md";
|
|
520
|
+
if (this.fileExists(filePath) && !this.context.force) {
|
|
521
|
+
return {
|
|
522
|
+
success: false,
|
|
523
|
+
message: "\u26A0\uFE0F README.md already exists",
|
|
524
|
+
filePath
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
const content = `# My Project
|
|
528
|
+
|
|
529
|
+
A new project initialized with pjangler.
|
|
530
|
+
|
|
531
|
+
## Getting Started
|
|
532
|
+
|
|
533
|
+
1. Install dependencies: \`mise install\`
|
|
534
|
+
2. Start development: \`mise run dev\`
|
|
535
|
+
|
|
536
|
+
## Project Structure
|
|
537
|
+
|
|
538
|
+
- \`mise.toml\` - Environment configuration
|
|
539
|
+
- \`.mise/tasks/\` - Task definitions
|
|
540
|
+
- \`src/\` - Source code
|
|
541
|
+
`;
|
|
542
|
+
this.writeFile(filePath, content);
|
|
543
|
+
return {
|
|
544
|
+
success: true,
|
|
545
|
+
message: "\u2705 Created README.md",
|
|
546
|
+
filePath
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
var AddSrcDirectory = class extends Command {
|
|
551
|
+
async invoke() {
|
|
552
|
+
this.createDirectory("src");
|
|
553
|
+
const indexJsPath = "src/index.js";
|
|
554
|
+
const content = `console.log("Hello, World!");
|
|
555
|
+
`;
|
|
556
|
+
this.writeFile(indexJsPath, content);
|
|
557
|
+
return {
|
|
558
|
+
success: true,
|
|
559
|
+
message: "\u2705 Created src/ directory with index.js",
|
|
560
|
+
filePath: "src/index.js"
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
// src/recipes/NodeRecipe.ts
|
|
566
|
+
var NodeRecipe = class extends Recipe {
|
|
567
|
+
constructor(context) {
|
|
568
|
+
super(context);
|
|
569
|
+
this.addIngredient(AddPackageJson).addIngredient(AddReadme).addIngredient(AddSrcDirectory);
|
|
570
|
+
}
|
|
571
|
+
printNextSteps() {
|
|
572
|
+
console.log("\u{1F389} Node.js project initialized successfully!");
|
|
573
|
+
console.log(" Next steps:");
|
|
574
|
+
console.log(" 1. mise install");
|
|
575
|
+
console.log(" 2. mise run dev");
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
// src/commands/hermes/PromptForAgentConfig.ts
|
|
580
|
+
import { basename, join as join2 } from "node:path";
|
|
581
|
+
import { readFileSync } from "node:fs";
|
|
582
|
+
import * as p from "@clack/prompts";
|
|
583
|
+
function detectTicketProvider(targetDir) {
|
|
584
|
+
try {
|
|
585
|
+
const t = JSON.parse(readFileSync(join2(targetDir, ".project.json"), "utf8"))?.ticket_provider?.type;
|
|
586
|
+
return t === "plane" || t === "linear" || t === "trello" ? t : void 0;
|
|
587
|
+
} catch {
|
|
588
|
+
return void 0;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
var PromptForAgentConfig = class extends Command {
|
|
592
|
+
async invoke() {
|
|
593
|
+
const ctx = this.context;
|
|
594
|
+
const defaultRepo = basename(ctx.targetDir).toLowerCase();
|
|
595
|
+
const defaultRole = "pm";
|
|
596
|
+
if (ctx.yes) {
|
|
597
|
+
ctx.targetRepo = (ctx.targetRepo ?? defaultRepo).toLowerCase();
|
|
598
|
+
ctx.role ??= defaultRole;
|
|
599
|
+
ctx.agentPurpose ??= `${ctx.role} agent for ${ctx.targetRepo}`;
|
|
600
|
+
ctx.soulTone ??= "direct";
|
|
601
|
+
ctx.modelProvider ??= "";
|
|
602
|
+
ctx.modelName ??= "";
|
|
603
|
+
ctx.ticketProvider ??= detectTicketProvider(ctx.targetDir) ?? "plane";
|
|
604
|
+
ctx.withScrumMaster ??= false;
|
|
605
|
+
ctx.skipTelegram ??= true;
|
|
606
|
+
ctx.skipEmail ??= true;
|
|
607
|
+
ctx.agentId = deriveAgentId(ctx.targetRepo, ctx.role);
|
|
608
|
+
ctx.profileName = deriveProfileName(ctx.targetRepo, ctx.role);
|
|
609
|
+
return {
|
|
610
|
+
success: true,
|
|
611
|
+
message: this.formatMessage(
|
|
612
|
+
`\u2713 Non-interactive mode \u2014 using defaults (repo=${ctx.targetRepo}, role=${ctx.role}, profile=${ctx.profileName})`
|
|
613
|
+
)
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
p.intro("\u2695 hermes-agent \xB7 add a new agent role to this repo");
|
|
617
|
+
if (!ctx.targetRepo) {
|
|
618
|
+
const answer = await p.text({
|
|
619
|
+
message: "Target repo name",
|
|
620
|
+
placeholder: defaultRepo,
|
|
621
|
+
initialValue: defaultRepo,
|
|
622
|
+
validate: (v) => v && v.trim() ? void 0 : "required"
|
|
623
|
+
});
|
|
624
|
+
if (p.isCancel(answer)) return this.cancelled();
|
|
625
|
+
ctx.targetRepo = String(answer).trim().toLowerCase();
|
|
626
|
+
}
|
|
627
|
+
if (!ctx.role) {
|
|
628
|
+
const answer = await p.select({
|
|
629
|
+
message: "Role",
|
|
630
|
+
options: ROLE_CHOICES.map((r) => ({ value: r.value, label: r.label, hint: r.hint })),
|
|
631
|
+
initialValue: defaultRole
|
|
632
|
+
});
|
|
633
|
+
if (p.isCancel(answer)) return this.cancelled();
|
|
634
|
+
ctx.role = String(answer).trim();
|
|
635
|
+
}
|
|
636
|
+
if (ctx.ticketProvider === void 0) {
|
|
637
|
+
const detected = detectTicketProvider(ctx.targetDir);
|
|
638
|
+
const answer = await p.select({
|
|
639
|
+
message: "Ticket board provider",
|
|
640
|
+
options: TICKET_PROVIDERS.map((t) => ({
|
|
641
|
+
value: t.value,
|
|
642
|
+
label: t.label,
|
|
643
|
+
hint: t.value === detected ? `${t.hint} \u2014 current .project.json` : t.hint
|
|
644
|
+
})),
|
|
645
|
+
initialValue: detected ?? "plane"
|
|
646
|
+
});
|
|
647
|
+
if (p.isCancel(answer)) return this.cancelled();
|
|
648
|
+
ctx.ticketProvider = answer;
|
|
649
|
+
}
|
|
650
|
+
if (ctx.role === "pm" && ctx.withScrumMaster === void 0) {
|
|
651
|
+
const answer = await p.confirm({
|
|
652
|
+
message: "Also provision the paired Scrum Master (Ticket Sentinel) for this repo?",
|
|
653
|
+
initialValue: true
|
|
654
|
+
});
|
|
655
|
+
if (p.isCancel(answer)) return this.cancelled();
|
|
656
|
+
ctx.withScrumMaster = answer === true;
|
|
657
|
+
}
|
|
658
|
+
if (!ctx.agentPurpose) {
|
|
659
|
+
const answer = await p.text({
|
|
660
|
+
message: "One-line purpose",
|
|
661
|
+
placeholder: `${ctx.role} agent for ${ctx.targetRepo}`,
|
|
662
|
+
initialValue: `${ctx.role} agent for ${ctx.targetRepo}`
|
|
663
|
+
});
|
|
664
|
+
if (p.isCancel(answer)) return this.cancelled();
|
|
665
|
+
ctx.agentPurpose = String(answer).trim();
|
|
666
|
+
}
|
|
667
|
+
if (!ctx.soulTone) {
|
|
668
|
+
const answer = await p.select({
|
|
669
|
+
message: "Personality tone",
|
|
670
|
+
options: SOUL_TONES.map((t) => ({
|
|
671
|
+
value: t,
|
|
672
|
+
label: t,
|
|
673
|
+
hint: t === "direct" ? "decision-forward, no preamble (default)" : t === "terse" ? "minimum words, conclusion-first" : t === "playful" ? "warm, mildly funny" : "precise, structured"
|
|
674
|
+
})),
|
|
675
|
+
initialValue: "direct"
|
|
676
|
+
});
|
|
677
|
+
if (p.isCancel(answer)) return this.cancelled();
|
|
678
|
+
ctx.soulTone = answer;
|
|
679
|
+
}
|
|
680
|
+
if (ctx.modelProvider === void 0) {
|
|
681
|
+
const answer = await p.text({
|
|
682
|
+
message: "Provider override (empty = inherit shared default profile)",
|
|
683
|
+
placeholder: ""
|
|
684
|
+
});
|
|
685
|
+
if (p.isCancel(answer)) return this.cancelled();
|
|
686
|
+
ctx.modelProvider = String(answer).trim();
|
|
687
|
+
}
|
|
688
|
+
if (ctx.modelName === void 0) {
|
|
689
|
+
const answer = await p.text({
|
|
690
|
+
message: "Model name override (empty = inherit shared default profile)",
|
|
691
|
+
placeholder: ""
|
|
692
|
+
});
|
|
693
|
+
if (p.isCancel(answer)) return this.cancelled();
|
|
694
|
+
ctx.modelName = String(answer).trim();
|
|
695
|
+
}
|
|
696
|
+
if (ctx.skipTelegram === void 0) {
|
|
697
|
+
const wire = await p.confirm({
|
|
698
|
+
message: `Wire up the Telegram bot (@${ctx.targetRepo}_${ctx.role}_bot) now?`,
|
|
699
|
+
initialValue: true
|
|
700
|
+
});
|
|
701
|
+
if (p.isCancel(wire)) return this.cancelled();
|
|
702
|
+
ctx.skipTelegram = !wire;
|
|
703
|
+
}
|
|
704
|
+
if (ctx.skipEmail === void 0) {
|
|
705
|
+
const wire = await p.confirm({
|
|
706
|
+
message: `Provision the delo.sh email address (${ctx.targetRepo}-${ctx.role}@delo.sh) now?`,
|
|
707
|
+
initialValue: true
|
|
708
|
+
});
|
|
709
|
+
if (p.isCancel(wire)) return this.cancelled();
|
|
710
|
+
ctx.skipEmail = !wire;
|
|
711
|
+
}
|
|
712
|
+
ctx.agentId = deriveAgentId(ctx.targetRepo, ctx.role);
|
|
713
|
+
ctx.profileName = deriveProfileName(ctx.targetRepo, ctx.role);
|
|
714
|
+
return {
|
|
715
|
+
success: true,
|
|
716
|
+
message: this.formatMessage(
|
|
717
|
+
`\u2713 Collected agent config (agent_id=${ctx.agentId}, profile=${ctx.profileName})`
|
|
718
|
+
)
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
cancelled() {
|
|
722
|
+
p.cancel("Aborted by user.");
|
|
723
|
+
return { success: false, message: "Aborted by user." };
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
// src/commands/hermes/RunCopierTemplate.ts
|
|
728
|
+
import { spawnSync } from "node:child_process";
|
|
729
|
+
import { homedir as homedir2 } from "node:os";
|
|
730
|
+
import { join as join3, dirname as dirname2 } from "node:path";
|
|
731
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2 } from "node:fs";
|
|
732
|
+
import { fileURLToPath } from "node:url";
|
|
733
|
+
import * as p2 from "@clack/prompts";
|
|
734
|
+
function resolveVendoredTemplate(name) {
|
|
735
|
+
let dir;
|
|
736
|
+
try {
|
|
737
|
+
dir = dirname2(fileURLToPath(import.meta.url));
|
|
738
|
+
} catch {
|
|
739
|
+
return void 0;
|
|
740
|
+
}
|
|
741
|
+
for (let i = 0; i < 8; i++) {
|
|
742
|
+
const candidate = join3(dir, "templates", name);
|
|
743
|
+
if (existsSync2(join3(candidate, "copier.yml"))) return candidate;
|
|
744
|
+
const parent = dirname2(dir);
|
|
745
|
+
if (parent === dir) break;
|
|
746
|
+
dir = parent;
|
|
747
|
+
}
|
|
748
|
+
return void 0;
|
|
749
|
+
}
|
|
750
|
+
var RunCopierTemplate = class extends Command {
|
|
751
|
+
async invoke() {
|
|
752
|
+
const ctx = this.context;
|
|
753
|
+
const { targetRepo, role, agentPurpose, soulTone, modelProvider, modelName } = ctx;
|
|
754
|
+
const ticketProvider = ctx.ticketProvider ?? "plane";
|
|
755
|
+
const profileName = ctx.profileName ?? (targetRepo && role ? deriveProfileName(targetRepo, role) : void 0);
|
|
756
|
+
const withScrumMaster = role === "pm" && ctx.withScrumMaster === true;
|
|
757
|
+
if (!targetRepo || !role) {
|
|
758
|
+
return {
|
|
759
|
+
success: false,
|
|
760
|
+
message: "PromptForAgentConfig must run before RunCopierTemplate (targetRepo/role unset)"
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
const roleDir = join3(ctx.targetDir, "agents", "hermes", role);
|
|
764
|
+
ctx.roleDir = roleDir;
|
|
765
|
+
ctx.runtimeRepo = `delorenj/agent-hm-${targetRepo}-${role}`;
|
|
766
|
+
const which = spawnSync("which", ["copier"], { encoding: "utf8" });
|
|
767
|
+
if (which.status !== 0) {
|
|
768
|
+
return {
|
|
769
|
+
success: false,
|
|
770
|
+
message: "\u2717 copier not found on PATH. Install with: `uv tool install copier` or `pip install copier`"
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
if (existsSync2(join3(roleDir, "role.yaml")) && !ctx.force) {
|
|
774
|
+
if (ctx.yes) {
|
|
775
|
+
ctx.force = true;
|
|
776
|
+
} else {
|
|
777
|
+
const proceed = await p2.confirm({
|
|
778
|
+
message: `${role}/role.yaml already exists \u2014 re-render with --overwrite?`,
|
|
779
|
+
initialValue: false
|
|
780
|
+
});
|
|
781
|
+
if (p2.isCancel(proceed) || !proceed) {
|
|
782
|
+
return {
|
|
783
|
+
success: false,
|
|
784
|
+
message: `Skipped: ${roleDir} already provisioned (use --force to re-render)`
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
ctx.force = true;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
const env = {
|
|
791
|
+
...process.env,
|
|
792
|
+
SKIP_TELEGRAM: "1",
|
|
793
|
+
SKIP_EMAIL: "1",
|
|
794
|
+
// We DO want copier to run runtime-repo + plane + bloodbank + systemd.
|
|
795
|
+
SKIP_RUNTIME_REPO: ctx.skipRuntimeRepo ? "1" : "0",
|
|
796
|
+
SKIP_PLANE: ctx.skipPlane ? "1" : "0",
|
|
797
|
+
SKIP_BLOODBANK: ctx.skipBloodbank ? "1" : "0",
|
|
798
|
+
SKIP_SYSTEMD: ctx.skipSystemd ? "1" : "0"
|
|
799
|
+
};
|
|
800
|
+
const LOCAL_TEMPLATE = join3(homedir2(), "code", "hermes-agent-template");
|
|
801
|
+
const vendored = resolveVendoredTemplate("hermes-agent");
|
|
802
|
+
const templateSrc = process.env.PJANGLER_HERMES_TEMPLATE || vendored || (existsSync2(join3(LOCAL_TEMPLATE, "copier.yml")) ? LOCAL_TEMPLATE : HERMES_AGENT_TEMPLATE);
|
|
803
|
+
const args = [
|
|
804
|
+
"copy",
|
|
805
|
+
templateSrc,
|
|
806
|
+
roleDir,
|
|
807
|
+
"--data",
|
|
808
|
+
`target_repo=${targetRepo}`,
|
|
809
|
+
"--data",
|
|
810
|
+
`role=${role}`,
|
|
811
|
+
"--data",
|
|
812
|
+
`agent_purpose=${agentPurpose ?? ""}`,
|
|
813
|
+
"--data",
|
|
814
|
+
`model_provider=${modelProvider ?? ""}`,
|
|
815
|
+
"--data",
|
|
816
|
+
`model_name=${modelName ?? ""}`,
|
|
817
|
+
"--data",
|
|
818
|
+
`profile_name=${profileName ?? ""}`,
|
|
819
|
+
"--data",
|
|
820
|
+
`soul_tone=${soulTone ?? "direct"}`,
|
|
821
|
+
"--data",
|
|
822
|
+
`ticket_provider=${ticketProvider}`,
|
|
823
|
+
"--data",
|
|
824
|
+
`with_scrum_master=${withScrumMaster}`,
|
|
825
|
+
"--trust",
|
|
826
|
+
"--vcs-ref=HEAD"
|
|
827
|
+
];
|
|
828
|
+
if (ctx.force) args.push("--overwrite");
|
|
829
|
+
if (ctx.dryRun) {
|
|
830
|
+
return {
|
|
831
|
+
success: true,
|
|
832
|
+
message: this.formatMessage(`Would run: copier ${args.join(" ")}`)
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
mkdirSync2(join3(ctx.targetDir, "agents", "hermes"), { recursive: true });
|
|
836
|
+
const spinner4 = p2.spinner();
|
|
837
|
+
spinner4.start(`Running copier copy (target: agents/hermes/${role})`);
|
|
838
|
+
const result = spawnSync("copier", args, {
|
|
839
|
+
stdio: "inherit",
|
|
840
|
+
// pass the interactive output through; copier prints its own progress
|
|
841
|
+
env,
|
|
842
|
+
cwd: ctx.targetDir
|
|
843
|
+
});
|
|
844
|
+
spinner4.stop(result.status === 0 ? "\u2713 copier run complete" : "\u2717 copier failed");
|
|
845
|
+
if (result.status !== 0) {
|
|
846
|
+
return {
|
|
847
|
+
success: false,
|
|
848
|
+
message: `\u2717 copier exited with status ${result.status}. Check the output above; re-run with the same flags after fixing.`
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
return {
|
|
852
|
+
success: true,
|
|
853
|
+
message: `\u2713 Provisioned ${roleDir} (runtime: gh:${ctx.runtimeRepo})`
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
};
|
|
857
|
+
|
|
858
|
+
// src/commands/hermes/WireTelegram.ts
|
|
859
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
860
|
+
import { join as join4 } from "node:path";
|
|
861
|
+
import { existsSync as existsSync3, unlinkSync } from "node:fs";
|
|
862
|
+
import * as p3 from "@clack/prompts";
|
|
863
|
+
var WireTelegram = class extends Command {
|
|
864
|
+
async invoke() {
|
|
865
|
+
const ctx = this.context;
|
|
866
|
+
if (ctx.skipTelegram) {
|
|
867
|
+
return { success: true, message: "\u2192 Telegram wire-up skipped" };
|
|
868
|
+
}
|
|
869
|
+
if (ctx.dryRun) {
|
|
870
|
+
return { success: true, message: this.formatMessage("Would run BotFather token capture") };
|
|
871
|
+
}
|
|
872
|
+
const { targetRepo, role, roleDir } = ctx;
|
|
873
|
+
if (!targetRepo || !role || !roleDir) {
|
|
874
|
+
return { success: false, message: "Cannot wire telegram: missing target_repo/role/roleDir" };
|
|
875
|
+
}
|
|
876
|
+
const botHandle = `${targetRepo.toLowerCase().replace(/-/g, "_")}_${role.toLowerCase()}_bot`;
|
|
877
|
+
const displayName = `${cap(targetRepo)} ${role.length <= 3 ? role.toUpperCase() : cap(role)}`;
|
|
878
|
+
const vaultTitle = `Telegram-Hermes-${targetRepo.toLowerCase()}-${role.toLowerCase()}`;
|
|
879
|
+
const vaultRef = `op://DeLoSecrets/${vaultTitle}/token`;
|
|
880
|
+
let token = process.env.TELEGRAM_BOT_TOKEN;
|
|
881
|
+
let source = token ? "env" : null;
|
|
882
|
+
if (!token) {
|
|
883
|
+
const tryOp = spawnSync2("op", ["read", vaultRef], { encoding: "utf8" });
|
|
884
|
+
if (tryOp.status === 0) {
|
|
885
|
+
token = tryOp.stdout.trim();
|
|
886
|
+
source = "op";
|
|
887
|
+
p3.log.info(`\u2713 Telegram token loaded from ${vaultRef}`);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
if (!token) {
|
|
891
|
+
p3.log.step("BotFather steps");
|
|
892
|
+
p3.log.info(
|
|
893
|
+
[
|
|
894
|
+
" 1. Open Telegram, message @BotFather",
|
|
895
|
+
" 2. /newbot",
|
|
896
|
+
` 3. Display name: ${displayName}`,
|
|
897
|
+
` 4. Username: ${botHandle} (must end in _bot)`,
|
|
898
|
+
" 5. Copy the HTTP API token from the reply.",
|
|
899
|
+
" 6. /setjoingroups Disable",
|
|
900
|
+
" 7. /setprivacy Disable"
|
|
901
|
+
].join("\n")
|
|
902
|
+
);
|
|
903
|
+
const tokenAnswer = await p3.password({
|
|
904
|
+
message: `Paste the bot token for @${botHandle}`,
|
|
905
|
+
mask: "\u2022",
|
|
906
|
+
validate: (v) => {
|
|
907
|
+
const s = String(v ?? "").trim();
|
|
908
|
+
if (!s) return "required";
|
|
909
|
+
if (!/^[0-9]+:.+/.test(s)) return "expected '<digits>:<secret>' shape";
|
|
910
|
+
}
|
|
911
|
+
});
|
|
912
|
+
if (p3.isCancel(tokenAnswer)) {
|
|
913
|
+
return { success: true, message: "\u2192 Telegram skipped (no token). Re-run later." };
|
|
914
|
+
}
|
|
915
|
+
token = String(tokenAnswer).trim();
|
|
916
|
+
source = "prompt";
|
|
917
|
+
const persist = await p3.confirm({
|
|
918
|
+
message: `Save to ${vaultRef} for next time?`,
|
|
919
|
+
initialValue: true
|
|
920
|
+
});
|
|
921
|
+
if (!p3.isCancel(persist) && persist) {
|
|
922
|
+
const create = spawnSync2(
|
|
923
|
+
"op",
|
|
924
|
+
[
|
|
925
|
+
"item",
|
|
926
|
+
"create",
|
|
927
|
+
"--category=API Credential",
|
|
928
|
+
"--vault=DeLoSecrets",
|
|
929
|
+
`--title=${vaultTitle}`,
|
|
930
|
+
`token=${token}`,
|
|
931
|
+
`bot_handle=${botHandle}`
|
|
932
|
+
],
|
|
933
|
+
{ stdio: "inherit" }
|
|
934
|
+
);
|
|
935
|
+
if (create.status !== 0) {
|
|
936
|
+
p3.log.warn("Could not store in 1Password \u2014 token is still set for this run.");
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
const allowedAnswer = await p3.text({
|
|
941
|
+
message: "Your Telegram user id (allow-list for this bot)",
|
|
942
|
+
placeholder: process.env.TELEGRAM_ALLOWED_USERS ?? "",
|
|
943
|
+
initialValue: process.env.TELEGRAM_ALLOWED_USERS ?? "",
|
|
944
|
+
validate: (v) => /^[0-9](?:[0-9,]*[0-9])?$/.test(String(v).trim()) ? void 0 : "comma-separated numeric ids"
|
|
945
|
+
});
|
|
946
|
+
if (p3.isCancel(allowedAnswer)) {
|
|
947
|
+
return { success: false, message: "\u2717 Aborted; Telegram step deferred." };
|
|
948
|
+
}
|
|
949
|
+
const script = join4(roleDir, ".scripts", "30-telegram.sh");
|
|
950
|
+
if (!existsSync3(script)) {
|
|
951
|
+
return {
|
|
952
|
+
success: false,
|
|
953
|
+
message: `\u2717 ${script} not found. Did copier finish? Re-run with --skip-runtime-repo=0 if you skipped it.`
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
const marker = join4(roleDir, ".scripts", ".done-30-telegram");
|
|
957
|
+
if (existsSync3(marker)) unlinkSync(marker);
|
|
958
|
+
const spinner4 = p3.spinner();
|
|
959
|
+
spinner4.start("Verifying token + wiring profile");
|
|
960
|
+
const result = spawnSync2("bash", [script], {
|
|
961
|
+
stdio: "inherit",
|
|
962
|
+
env: {
|
|
963
|
+
...process.env,
|
|
964
|
+
SKIP_TELEGRAM: "0",
|
|
965
|
+
TELEGRAM_BOT_TOKEN: token,
|
|
966
|
+
TELEGRAM_ALLOWED_USERS: String(allowedAnswer).trim()
|
|
967
|
+
},
|
|
968
|
+
cwd: roleDir
|
|
969
|
+
});
|
|
970
|
+
spinner4.stop(result.status === 0 ? "\u2713 Telegram wired" : "\u2717 Telegram step failed");
|
|
971
|
+
if (result.status !== 0) {
|
|
972
|
+
return { success: false, message: "Telegram wire-up failed. See output above." };
|
|
973
|
+
}
|
|
974
|
+
const sourceLabel = source === "env" ? " (token: env)" : source === "op" ? " (token: op)" : "";
|
|
975
|
+
return { success: true, message: `\u2713 Telegram: @${botHandle} ready${sourceLabel}` };
|
|
976
|
+
}
|
|
977
|
+
};
|
|
978
|
+
function cap(s) {
|
|
979
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
// src/commands/hermes/WireEmail.ts
|
|
983
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
984
|
+
import { join as join5 } from "node:path";
|
|
985
|
+
import { existsSync as existsSync4, unlinkSync as unlinkSync2 } from "node:fs";
|
|
986
|
+
import * as p4 from "@clack/prompts";
|
|
987
|
+
var WireEmail = class extends Command {
|
|
988
|
+
async invoke() {
|
|
989
|
+
const ctx = this.context;
|
|
990
|
+
if (ctx.skipEmail) {
|
|
991
|
+
return { success: true, message: "\u2192 Email wire-up skipped" };
|
|
992
|
+
}
|
|
993
|
+
if (ctx.dryRun) {
|
|
994
|
+
return { success: true, message: this.formatMessage("Would create CF Email Routing rule") };
|
|
995
|
+
}
|
|
996
|
+
const { targetRepo, role, roleDir } = ctx;
|
|
997
|
+
if (!targetRepo || !role || !roleDir) {
|
|
998
|
+
return { success: false, message: "Cannot wire email: missing target_repo/role/roleDir" };
|
|
999
|
+
}
|
|
1000
|
+
const script = join5(roleDir, ".scripts", "50-email.sh");
|
|
1001
|
+
if (!existsSync4(script)) {
|
|
1002
|
+
return { success: false, message: `\u2717 ${script} not found` };
|
|
1003
|
+
}
|
|
1004
|
+
let token = process.env.CF_EMAIL_ROUTING_TOKEN;
|
|
1005
|
+
if (!token) {
|
|
1006
|
+
const tryOp = spawnSync3(
|
|
1007
|
+
"op",
|
|
1008
|
+
["read", "op://DeLoSecrets/Cloudflare-EmailRouting/token"],
|
|
1009
|
+
{ encoding: "utf8" }
|
|
1010
|
+
);
|
|
1011
|
+
if (tryOp.status === 0) {
|
|
1012
|
+
token = tryOp.stdout.trim();
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
if (!token) {
|
|
1016
|
+
p4.log.warn("CF Email Routing token not found. Required scopes:");
|
|
1017
|
+
p4.log.info(
|
|
1018
|
+
[
|
|
1019
|
+
" Zone (delo.sh) \u2192 Email Routing Rules : Edit",
|
|
1020
|
+
" Zone (delo.sh) \u2192 Email Routing Settings : Read",
|
|
1021
|
+
" Account \u2192 Email Routing Addresses : Read",
|
|
1022
|
+
"Create at: https://dash.cloudflare.com/profile/api-tokens"
|
|
1023
|
+
].join("\n")
|
|
1024
|
+
);
|
|
1025
|
+
const provideNow = await p4.confirm({
|
|
1026
|
+
message: "Paste a token now? (skipping leaves email unwired until you re-run.)",
|
|
1027
|
+
initialValue: false
|
|
1028
|
+
});
|
|
1029
|
+
if (p4.isCancel(provideNow) || !provideNow) {
|
|
1030
|
+
return { success: true, message: "\u2192 Email skipped (no token). Re-run later." };
|
|
1031
|
+
}
|
|
1032
|
+
const tokenAnswer = await p4.password({
|
|
1033
|
+
message: "CF token (will be passed via env, not stored)",
|
|
1034
|
+
mask: "\u2022",
|
|
1035
|
+
validate: (v) => String(v ?? "").trim() ? void 0 : "required"
|
|
1036
|
+
});
|
|
1037
|
+
if (p4.isCancel(tokenAnswer)) {
|
|
1038
|
+
return { success: true, message: "\u2192 Email skipped (cancelled)" };
|
|
1039
|
+
}
|
|
1040
|
+
token = String(tokenAnswer).trim();
|
|
1041
|
+
const persist = await p4.confirm({
|
|
1042
|
+
message: "Save to op://DeLoSecrets/Cloudflare-EmailRouting/token for next time?",
|
|
1043
|
+
initialValue: true
|
|
1044
|
+
});
|
|
1045
|
+
if (!p4.isCancel(persist) && persist) {
|
|
1046
|
+
const create = spawnSync3(
|
|
1047
|
+
"op",
|
|
1048
|
+
[
|
|
1049
|
+
"item",
|
|
1050
|
+
"create",
|
|
1051
|
+
"--category=API Credential",
|
|
1052
|
+
"--vault=DeLoSecrets",
|
|
1053
|
+
"--title=Cloudflare-EmailRouting",
|
|
1054
|
+
`token=${token}`
|
|
1055
|
+
],
|
|
1056
|
+
{ stdio: "inherit" }
|
|
1057
|
+
);
|
|
1058
|
+
if (create.status !== 0) {
|
|
1059
|
+
p4.log.warn("Could not store in 1Password \u2014 token is still set for this run.");
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
const marker = join5(roleDir, ".scripts", ".done-50-email");
|
|
1064
|
+
if (existsSync4(marker)) unlinkSync2(marker);
|
|
1065
|
+
const spinner4 = p4.spinner();
|
|
1066
|
+
spinner4.start("Creating Cloudflare Email Routing rule");
|
|
1067
|
+
const result = spawnSync3("bash", [script], {
|
|
1068
|
+
stdio: "inherit",
|
|
1069
|
+
env: { ...process.env, SKIP_EMAIL: "0", CF_EMAIL_ROUTING_TOKEN: token },
|
|
1070
|
+
cwd: roleDir
|
|
1071
|
+
});
|
|
1072
|
+
spinner4.stop(result.status === 0 ? "\u2713 Email rule created" : "\u2717 Email step failed");
|
|
1073
|
+
if (result.status !== 0) {
|
|
1074
|
+
return { success: false, message: "Email rule creation failed. See output above." };
|
|
1075
|
+
}
|
|
1076
|
+
return {
|
|
1077
|
+
success: true,
|
|
1078
|
+
message: `\u2713 Email: ${targetRepo}-${role}@delo.sh \u2192 jaradd@gmail.com`
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
};
|
|
1082
|
+
|
|
1083
|
+
// src/commands/hermes/PrintHermesSummary.ts
|
|
1084
|
+
import * as p5 from "@clack/prompts";
|
|
1085
|
+
var PrintHermesSummary = class extends Command {
|
|
1086
|
+
async invoke() {
|
|
1087
|
+
const ctx = this.context;
|
|
1088
|
+
const { targetRepo, role, agentId, runtimeRepo, skipTelegram, skipEmail } = ctx;
|
|
1089
|
+
const botHandle = `${targetRepo?.toLowerCase().replace(/-/g, "_")}_${role?.toLowerCase()}_bot`;
|
|
1090
|
+
const email = `${targetRepo}-${role}@delo.sh`;
|
|
1091
|
+
const gw = `hermes-${agentId}-gateway.service`;
|
|
1092
|
+
const csm = `hermes-${agentId}-consumer.service`;
|
|
1093
|
+
const ckpt = `hermes-${agentId}-checkpoint.timer`;
|
|
1094
|
+
const lines = [];
|
|
1095
|
+
lines.push(`agent_id ${agentId}`);
|
|
1096
|
+
lines.push(`role dir ${ctx.roleDir}`);
|
|
1097
|
+
lines.push(`runtime gh:${runtimeRepo}`);
|
|
1098
|
+
lines.push(`telegram @${botHandle}${skipTelegram ? " (NOT yet wired)" : ""}`);
|
|
1099
|
+
lines.push(`email ${email}${skipEmail ? " (NOT yet wired)" : ""}`);
|
|
1100
|
+
lines.push("");
|
|
1101
|
+
lines.push("Start daemons:");
|
|
1102
|
+
lines.push(` systemctl --user start ${csm}`);
|
|
1103
|
+
lines.push(` systemctl --user start ${ckpt}`);
|
|
1104
|
+
if (!skipTelegram) {
|
|
1105
|
+
lines.push(` systemctl --user start ${gw}`);
|
|
1106
|
+
} else {
|
|
1107
|
+
lines.push(` # gateway needs Telegram wired first (re-run with --skip-telegram=0)`);
|
|
1108
|
+
}
|
|
1109
|
+
lines.push("");
|
|
1110
|
+
lines.push("Talk locally:");
|
|
1111
|
+
lines.push(` ${ctx.roleDir}/hermes chat "status"`);
|
|
1112
|
+
if (skipTelegram || skipEmail) {
|
|
1113
|
+
lines.push("");
|
|
1114
|
+
lines.push("Deferred \u2014 re-run pjangler hermes-agent without --yes (or with explicit flags):");
|
|
1115
|
+
if (skipTelegram) lines.push(" pjangler hermes-agent --skip-telegram=false # wire just telegram");
|
|
1116
|
+
if (skipEmail) lines.push(" pjangler hermes-agent --skip-email=false # wire just email");
|
|
1117
|
+
}
|
|
1118
|
+
p5.note(lines.join("\n"), `Provisioned ${agentId}`);
|
|
1119
|
+
p5.outro("Done.");
|
|
1120
|
+
return { success: true, message: "" };
|
|
1121
|
+
}
|
|
1122
|
+
};
|
|
1123
|
+
|
|
1124
|
+
// src/recipes/HermesAgentRecipe.ts
|
|
1125
|
+
var HermesAgentRecipe = class extends Recipe {
|
|
1126
|
+
constructor(context) {
|
|
1127
|
+
super(context);
|
|
1128
|
+
this.addIngredient(EnsureTemplateConfig).addIngredient(PromptForAgentConfig).addIngredient(RunCopierTemplate).addIngredient(WireTelegram).addIngredient(WireEmail).addIngredient(PrintHermesSummary);
|
|
1129
|
+
}
|
|
1130
|
+
// Override execute() to suppress the base class's per-command logging since
|
|
1131
|
+
// our commands already render their own UI via @clack/prompts.
|
|
1132
|
+
async execute() {
|
|
1133
|
+
for (const command of this.ingredients) {
|
|
1134
|
+
const result = await command.invoke();
|
|
1135
|
+
if (!result.success && result.message.startsWith("\u2717")) {
|
|
1136
|
+
console.error(result.message);
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
if (result.message && !result.message.startsWith("\u2713 Collected")) {
|
|
1140
|
+
if (result.message.startsWith("\u2192") || result.message.startsWith("\u2713 Provisioned")) {
|
|
1141
|
+
console.log(result.message);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
printNextSteps() {
|
|
1147
|
+
}
|
|
1148
|
+
};
|
|
1149
|
+
|
|
1150
|
+
// src/utils/registry.ts
|
|
1151
|
+
var RECIPE_REGISTRY = {
|
|
1152
|
+
mise: {
|
|
1153
|
+
name: "mise",
|
|
1154
|
+
description: "Mise task runner and environment setup",
|
|
1155
|
+
class: MiseRecipe,
|
|
1156
|
+
commands: ["AddMiseToml", "AddDotenv", "AddMiseTasksStructure", "AddMiseBaseToml", "AddMiseBaseScript"]
|
|
1157
|
+
},
|
|
1158
|
+
docker: {
|
|
1159
|
+
name: "docker",
|
|
1160
|
+
description: "Docker containerization setup",
|
|
1161
|
+
class: DockerRecipe,
|
|
1162
|
+
commands: ["AddDockerfile", "AddDockerCompose", "AddDockerignore"]
|
|
1163
|
+
},
|
|
1164
|
+
node: {
|
|
1165
|
+
name: "node",
|
|
1166
|
+
description: "Node.js project template",
|
|
1167
|
+
class: NodeRecipe,
|
|
1168
|
+
commands: ["NodeCommands"]
|
|
1169
|
+
// Placeholder - actual commands in NodeCommands.ts
|
|
1170
|
+
},
|
|
1171
|
+
"hermes-agent": {
|
|
1172
|
+
name: "hermes-agent",
|
|
1173
|
+
description: "Add a Hermes agent role to this repo (copier + BotFather + CF email + submodule)",
|
|
1174
|
+
class: HermesAgentRecipe,
|
|
1175
|
+
commands: [
|
|
1176
|
+
"EnsureTemplateConfig",
|
|
1177
|
+
"PromptForAgentConfig",
|
|
1178
|
+
"RunCopierTemplate",
|
|
1179
|
+
"WireTelegram",
|
|
1180
|
+
"WireEmail",
|
|
1181
|
+
"PrintHermesSummary"
|
|
1182
|
+
]
|
|
1183
|
+
}
|
|
1184
|
+
};
|
|
1185
|
+
var COMMAND_REGISTRY = {
|
|
1186
|
+
AddDockerfile: {
|
|
1187
|
+
name: "AddDockerfile",
|
|
1188
|
+
description: "Create Dockerfile for containerization",
|
|
1189
|
+
group: "docker",
|
|
1190
|
+
class: AddDockerfile
|
|
1191
|
+
},
|
|
1192
|
+
AddDockerCompose: {
|
|
1193
|
+
name: "AddDockerCompose",
|
|
1194
|
+
description: "Create docker-compose.yml for multi-service setup",
|
|
1195
|
+
group: "docker",
|
|
1196
|
+
class: AddDockerCompose
|
|
1197
|
+
},
|
|
1198
|
+
AddDockerignore: {
|
|
1199
|
+
name: "AddDockerignore",
|
|
1200
|
+
description: "Create .dockerignore file",
|
|
1201
|
+
group: "docker",
|
|
1202
|
+
class: AddDockerignore
|
|
1203
|
+
},
|
|
1204
|
+
AddMiseToml: {
|
|
1205
|
+
name: "AddMiseToml",
|
|
1206
|
+
description: "Create mise.toml for version management",
|
|
1207
|
+
group: "mise",
|
|
1208
|
+
class: AddMiseToml
|
|
1209
|
+
},
|
|
1210
|
+
AddMiseBaseToml: {
|
|
1211
|
+
name: "AddMiseBaseToml",
|
|
1212
|
+
description: "Create base mise configuration",
|
|
1213
|
+
group: "mise",
|
|
1214
|
+
class: AddMiseBaseToml
|
|
1215
|
+
},
|
|
1216
|
+
AddMiseTasksStructure: {
|
|
1217
|
+
name: "AddMiseTasksStructure",
|
|
1218
|
+
description: "Create .mise/tasks directory structure",
|
|
1219
|
+
group: "mise",
|
|
1220
|
+
class: AddMiseTasksStructure
|
|
1221
|
+
},
|
|
1222
|
+
AddMiseBaseScript: {
|
|
1223
|
+
name: "AddMiseBaseScript",
|
|
1224
|
+
description: "Create base mise task scripts",
|
|
1225
|
+
group: "mise",
|
|
1226
|
+
class: AddMiseBaseScript
|
|
1227
|
+
},
|
|
1228
|
+
AddDotenv: {
|
|
1229
|
+
name: "AddDotenv",
|
|
1230
|
+
description: "Create .env.example file",
|
|
1231
|
+
group: "environment",
|
|
1232
|
+
class: AddDotenv
|
|
1233
|
+
}
|
|
1234
|
+
};
|
|
1235
|
+
function getRecipeNames() {
|
|
1236
|
+
return Object.keys(RECIPE_REGISTRY);
|
|
1237
|
+
}
|
|
1238
|
+
function getRecipeInfo(name) {
|
|
1239
|
+
return RECIPE_REGISTRY[name] || null;
|
|
1240
|
+
}
|
|
1241
|
+
function getCommandNames() {
|
|
1242
|
+
return Object.keys(COMMAND_REGISTRY);
|
|
1243
|
+
}
|
|
1244
|
+
function getCommandInfo(name) {
|
|
1245
|
+
return COMMAND_REGISTRY[name] || null;
|
|
1246
|
+
}
|
|
1247
|
+
function getCommandsByGroup() {
|
|
1248
|
+
const grouped = {};
|
|
1249
|
+
for (const cmdInfo of Object.values(COMMAND_REGISTRY)) {
|
|
1250
|
+
if (!grouped[cmdInfo.group]) {
|
|
1251
|
+
grouped[cmdInfo.group] = [];
|
|
1252
|
+
}
|
|
1253
|
+
grouped[cmdInfo.group].push(cmdInfo);
|
|
1254
|
+
}
|
|
1255
|
+
return grouped;
|
|
1256
|
+
}
|
|
1257
|
+
function createRecipe(name, context) {
|
|
1258
|
+
const info = getRecipeInfo(name);
|
|
1259
|
+
if (!info) return null;
|
|
1260
|
+
return new info.class(context);
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
// src/parity/index.ts
|
|
1264
|
+
import { existsSync as existsSync5, lstatSync, mkdirSync as mkdirSync3, readFileSync as readFileSync2, readlinkSync, readdirSync, renameSync, symlinkSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2, chmodSync, copyFileSync } from "node:fs";
|
|
1265
|
+
import { dirname as dirname3, join as join6, relative, resolve } from "node:path";
|
|
1266
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1267
|
+
import { homedir as homedir3 } from "node:os";
|
|
1268
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
1269
|
+
var LINK_AGENTFILES_BLOCK = `# This block will handle the linking of
|
|
1270
|
+
# agent files to the main AGENTS.md file.
|
|
1271
|
+
#
|
|
1272
|
+
# TODO: Ensure this works for all levels of nesting.
|
|
1273
|
+
# i.e. All linked agent files MUST be siblings at
|
|
1274
|
+
# any given level of nesting.
|
|
1275
|
+
[hooks]
|
|
1276
|
+
enter = [
|
|
1277
|
+
"{{config_root}}/.mise/scripts/link-agentfiles.sh",
|
|
1278
|
+
"op inject -i .env.op > .env",
|
|
1279
|
+
]
|
|
1280
|
+
|
|
1281
|
+
[[watch_files]]
|
|
1282
|
+
patterns = ["AGENTS.md"]
|
|
1283
|
+
task = "link-agentfiles"
|
|
1284
|
+
|
|
1285
|
+
[tasks.link-agentfiles]
|
|
1286
|
+
description = "Symlink all agent files to AGENTS.md"
|
|
1287
|
+
run = "{{config_root}}/.mise/scripts/link-agentfiles.sh"`;
|
|
1288
|
+
var VERSIONING_BLOCK = `# >>> mise-versioning >>> (managed block \u2014 do not edit by hand; re-run init to update)
|
|
1289
|
+
[tasks."version"]
|
|
1290
|
+
description = "Print the current version (vX.Y.Z)"
|
|
1291
|
+
run = "{{config_root}}/.mise/scripts/versioning.sh current"
|
|
1292
|
+
|
|
1293
|
+
[tasks."version:bump"]
|
|
1294
|
+
description = "Bump patch version: vX.Y.Z -> vX.Y.(Z+1)"
|
|
1295
|
+
alias = "version:bump-patch"
|
|
1296
|
+
run = "{{config_root}}/.mise/scripts/versioning.sh bump patch"
|
|
1297
|
+
|
|
1298
|
+
[tasks."version:bump-minor"]
|
|
1299
|
+
description = "Bump minor version: vX.Y.Z -> vX.(Y+1).0"
|
|
1300
|
+
run = "{{config_root}}/.mise/scripts/versioning.sh bump minor"
|
|
1301
|
+
|
|
1302
|
+
[tasks."version:bump-major"]
|
|
1303
|
+
description = "Bump major version: vX.Y.Z -> v(X+1).0.0"
|
|
1304
|
+
run = "{{config_root}}/.mise/scripts/versioning.sh bump major"
|
|
1305
|
+
|
|
1306
|
+
[tasks."version:check"]
|
|
1307
|
+
description = "Verify every versioned file is in parity"
|
|
1308
|
+
run = "{{config_root}}/.mise/scripts/versioning.sh check"
|
|
1309
|
+
|
|
1310
|
+
[tasks."version:sync"]
|
|
1311
|
+
description = "Force every versioned file up to the highest version"
|
|
1312
|
+
run = "{{config_root}}/.mise/scripts/versioning.sh sync"
|
|
1313
|
+
# <<< mise-versioning <<<`;
|
|
1314
|
+
function resolvePjanglerRoot() {
|
|
1315
|
+
let dir = dirname3(fileURLToPath2(import.meta.url));
|
|
1316
|
+
while (dir !== dirname3(dir)) {
|
|
1317
|
+
if (existsSync5(join6(dir, "package.json")) && existsSync5(join6(dir, "templates", "commonproject", "copier.yml"))) {
|
|
1318
|
+
return dir;
|
|
1319
|
+
}
|
|
1320
|
+
dir = dirname3(dir);
|
|
1321
|
+
}
|
|
1322
|
+
throw new Error("Unable to resolve pjangler root");
|
|
1323
|
+
}
|
|
1324
|
+
function normalizeNewlines(value) {
|
|
1325
|
+
return value.replace(/\r\n/g, "\n");
|
|
1326
|
+
}
|
|
1327
|
+
function readText(path) {
|
|
1328
|
+
return normalizeNewlines(readFileSync2(path, "utf8"));
|
|
1329
|
+
}
|
|
1330
|
+
function safeReadText(path) {
|
|
1331
|
+
return existsSync5(path) ? readText(path) : null;
|
|
1332
|
+
}
|
|
1333
|
+
function ensureParent(path) {
|
|
1334
|
+
mkdirSync3(dirname3(path), { recursive: true });
|
|
1335
|
+
}
|
|
1336
|
+
function writeText(path, content) {
|
|
1337
|
+
ensureParent(path);
|
|
1338
|
+
writeFileSync2(path, content);
|
|
1339
|
+
}
|
|
1340
|
+
function tryParseJson(text3) {
|
|
1341
|
+
if (!text3) return null;
|
|
1342
|
+
try {
|
|
1343
|
+
return JSON.parse(text3);
|
|
1344
|
+
} catch {
|
|
1345
|
+
return null;
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
function slugifyRepoName(name) {
|
|
1349
|
+
return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "project";
|
|
1350
|
+
}
|
|
1351
|
+
function titleCaseSlug(slug) {
|
|
1352
|
+
return slug.split(/[-_]/g).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
1353
|
+
}
|
|
1354
|
+
function readSymlinkTarget(path) {
|
|
1355
|
+
if (!existsSync5(path)) return null;
|
|
1356
|
+
try {
|
|
1357
|
+
return readlinkSync(path);
|
|
1358
|
+
} catch {
|
|
1359
|
+
return null;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
function ensureSymlink(path, target, dryRun) {
|
|
1363
|
+
if (existsSync5(path)) {
|
|
1364
|
+
const stat = lstatSync(path);
|
|
1365
|
+
if (stat.isSymbolicLink()) {
|
|
1366
|
+
const current = readSymlinkTarget(path);
|
|
1367
|
+
if (current === target) return { changed: false };
|
|
1368
|
+
if (!dryRun) {
|
|
1369
|
+
unlinkSync3(path);
|
|
1370
|
+
symlinkSync(target, path);
|
|
1371
|
+
}
|
|
1372
|
+
return { changed: true };
|
|
1373
|
+
}
|
|
1374
|
+
return { changed: false, blocked: `${relative(process.cwd(), path) || path} exists and is not a symlink` };
|
|
1375
|
+
}
|
|
1376
|
+
if (!dryRun) symlinkSync(target, path);
|
|
1377
|
+
return { changed: true };
|
|
1378
|
+
}
|
|
1379
|
+
function yamlGet(text3, keyPath) {
|
|
1380
|
+
const parts = keyPath.split(".");
|
|
1381
|
+
const lines = text3.split("\n");
|
|
1382
|
+
let start = 0;
|
|
1383
|
+
let indent = 0;
|
|
1384
|
+
for (let idx = 0; idx < parts.length; idx += 1) {
|
|
1385
|
+
const key = parts[idx];
|
|
1386
|
+
let found = false;
|
|
1387
|
+
for (let i = start; i < lines.length; i += 1) {
|
|
1388
|
+
const line = lines[i];
|
|
1389
|
+
if (!line.trim() || line.trim().startsWith("#")) continue;
|
|
1390
|
+
const match = line.match(/^(\s*)([^:#]+):\s*(.*)$/);
|
|
1391
|
+
if (!match) continue;
|
|
1392
|
+
const currentIndent = match[1].length;
|
|
1393
|
+
const currentKey = match[2].trim();
|
|
1394
|
+
const rest = match[3].trim();
|
|
1395
|
+
if (idx > 0 && currentIndent < indent) break;
|
|
1396
|
+
if (currentIndent !== indent || currentKey !== key) continue;
|
|
1397
|
+
found = true;
|
|
1398
|
+
if (idx === parts.length - 1) {
|
|
1399
|
+
return rest.replace(/^['"]|['"]$/g, "").trim();
|
|
1400
|
+
}
|
|
1401
|
+
start = i + 1;
|
|
1402
|
+
indent = currentIndent + 2;
|
|
1403
|
+
break;
|
|
1404
|
+
}
|
|
1405
|
+
if (!found) return "";
|
|
1406
|
+
}
|
|
1407
|
+
return "";
|
|
1408
|
+
}
|
|
1409
|
+
function discoverRoles(repoRoot) {
|
|
1410
|
+
const rolesDir = join6(repoRoot, "agents", "hermes");
|
|
1411
|
+
if (!existsSync5(rolesDir)) return [];
|
|
1412
|
+
return readdirSync(rolesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => {
|
|
1413
|
+
const roleDir = join6(rolesDir, entry.name);
|
|
1414
|
+
const roleYamlPath = join6(roleDir, "role.yaml");
|
|
1415
|
+
if (!existsSync5(roleYamlPath)) return null;
|
|
1416
|
+
const text3 = readText(roleYamlPath);
|
|
1417
|
+
const runtimeRepoRaw = yamlGet(text3, "runtime.github_repo");
|
|
1418
|
+
return {
|
|
1419
|
+
role: yamlGet(text3, "role") || entry.name,
|
|
1420
|
+
roleDir,
|
|
1421
|
+
roleYamlPath,
|
|
1422
|
+
repo: yamlGet(text3, "repo"),
|
|
1423
|
+
agentId: yamlGet(text3, "agent_id"),
|
|
1424
|
+
profileName: yamlGet(text3, "profile") || yamlGet(text3, "agent_id"),
|
|
1425
|
+
displayName: yamlGet(text3, "display_name"),
|
|
1426
|
+
purpose: yamlGet(text3, "purpose"),
|
|
1427
|
+
botHandle: yamlGet(text3, "telegram.bot_username"),
|
|
1428
|
+
runtimeRepo: runtimeRepoRaw.includes("/") ? runtimeRepoRaw.split("/").slice(-1)[0] ?? runtimeRepoRaw : runtimeRepoRaw,
|
|
1429
|
+
runtimeOwner: yamlGet(text3, "runtime.github_owner"),
|
|
1430
|
+
planeWorkspace: yamlGet(text3, "ticket_provider.workspace") || yamlGet(text3, "plane.workspace"),
|
|
1431
|
+
ticketProviderName: yamlGet(text3, "ticket_provider.name"),
|
|
1432
|
+
ticketProviderBoardId: yamlGet(text3, "ticket_provider.board_id"),
|
|
1433
|
+
ticketProviderBoardUrl: yamlGet(text3, "ticket_provider.board_url"),
|
|
1434
|
+
ticketProviderIdentifier: yamlGet(text3, "plane.identifier")
|
|
1435
|
+
};
|
|
1436
|
+
}).filter((value) => Boolean(value));
|
|
1437
|
+
}
|
|
1438
|
+
function registryPath(homeDir) {
|
|
1439
|
+
return join6(homeDir, ".hermes", "agents-registry.yaml");
|
|
1440
|
+
}
|
|
1441
|
+
function systemctlUser(args) {
|
|
1442
|
+
const result = spawnSync4("systemctl", ["--user", ...args], { encoding: "utf8" });
|
|
1443
|
+
return {
|
|
1444
|
+
ok: result.status === 0,
|
|
1445
|
+
stdout: result.stdout.trim(),
|
|
1446
|
+
stderr: result.stderr.trim()
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
function templateVersioningScript(ctx) {
|
|
1450
|
+
return readText(join6(ctx.pjanglerRoot, ".mise", "scripts", "versioning.sh"));
|
|
1451
|
+
}
|
|
1452
|
+
function templateVersionFilesConf(ctx, repoRoot) {
|
|
1453
|
+
const packageJson = join6(repoRoot, "package.json");
|
|
1454
|
+
return existsSync5(packageJson) ? "# mise-versioning manifest: <type> <path>\n# types: json toml cargo csproj gradle plain gittag\njson package.json\ngittag .\n" : "# mise-versioning manifest: <type> <path>\n# types: json toml cargo csproj gradle plain gittag\ngittag .\n";
|
|
1455
|
+
}
|
|
1456
|
+
function replaceOrAppendManagedBlock(text3, startMarker, block, beforePattern) {
|
|
1457
|
+
if (startMarker.test(text3)) {
|
|
1458
|
+
return text3.replace(/# >>> mise-versioning >>>[\s\S]*?# <<< mise-versioning <<</, block);
|
|
1459
|
+
}
|
|
1460
|
+
if (beforePattern) {
|
|
1461
|
+
const match = text3.match(beforePattern);
|
|
1462
|
+
if (match && typeof match.index === "number") {
|
|
1463
|
+
return `${text3.slice(0, match.index).replace(/\s*$/, "\n\n")}${block}
|
|
1464
|
+
|
|
1465
|
+
${text3.slice(match.index)}`;
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
return `${text3.replace(/\s*$/, "")}
|
|
1469
|
+
|
|
1470
|
+
${block}
|
|
1471
|
+
`;
|
|
1472
|
+
}
|
|
1473
|
+
function upsertLinkAgentfilesBlock(text3) {
|
|
1474
|
+
const existing = /# This block will handle the linking of[\s\S]*?\[tasks\.link-agentfiles\][\s\S]*?run = "\{\{config_root\}\}\/\.mise\/scripts\/link-agentfiles\.sh"/;
|
|
1475
|
+
if (existing.test(text3)) {
|
|
1476
|
+
return text3.replace(existing, LINK_AGENTFILES_BLOCK);
|
|
1477
|
+
}
|
|
1478
|
+
const versioningIndex = text3.indexOf("# >>> mise-versioning >>>");
|
|
1479
|
+
if (versioningIndex >= 0) {
|
|
1480
|
+
return `${text3.slice(0, versioningIndex).replace(/\s*$/, "\n\n")}${LINK_AGENTFILES_BLOCK}
|
|
1481
|
+
|
|
1482
|
+
${text3.slice(versioningIndex)}`;
|
|
1483
|
+
}
|
|
1484
|
+
return `${text3.replace(/\s*$/, "")}
|
|
1485
|
+
|
|
1486
|
+
${LINK_AGENTFILES_BLOCK}
|
|
1487
|
+
`;
|
|
1488
|
+
}
|
|
1489
|
+
function readProjectJson(ctx) {
|
|
1490
|
+
return tryParseJson(safeReadText(join6(ctx.repoRoot, ".project.json")));
|
|
1491
|
+
}
|
|
1492
|
+
function canonicalProjectJson(ctx) {
|
|
1493
|
+
const roles = discoverRoles(ctx.repoRoot);
|
|
1494
|
+
const existing = readProjectJson(ctx) ?? {};
|
|
1495
|
+
const slug = String(existing.project_slug ?? slugifyRepoName(dirname3(ctx.repoRoot) === ctx.repoRoot ? ctx.repoRoot.split("/").pop() ?? "project" : ctx.repoRoot.split("/").pop() ?? "project"));
|
|
1496
|
+
const firstRole = roles[0];
|
|
1497
|
+
const ticketProvider = {
|
|
1498
|
+
type: String((existing.ticket_provider?.type ?? firstRole?.ticketProviderName ?? "plane") || "plane"),
|
|
1499
|
+
workspace: String((existing.ticket_provider?.workspace ?? firstRole?.planeWorkspace ?? "") || ""),
|
|
1500
|
+
identifier: String((existing.ticket_provider?.identifier ?? firstRole?.ticketProviderIdentifier ?? "") || ""),
|
|
1501
|
+
board_id: String((existing.ticket_provider?.board_id ?? firstRole?.ticketProviderBoardId ?? "") || ""),
|
|
1502
|
+
board_url: String((existing.ticket_provider?.board_url ?? firstRole?.ticketProviderBoardUrl ?? "") || "")
|
|
1503
|
+
};
|
|
1504
|
+
return {
|
|
1505
|
+
project_name: String(existing.project_name ?? titleCaseSlug(slug)),
|
|
1506
|
+
project_description: String(existing.project_description ?? ""),
|
|
1507
|
+
project_slug: slug,
|
|
1508
|
+
repo_path: ctx.repoRoot,
|
|
1509
|
+
ticket_provider: ticketProvider,
|
|
1510
|
+
agents: Object.fromEntries(
|
|
1511
|
+
roles.map((role) => [
|
|
1512
|
+
role.agentId || `${slug}-${role.role}`,
|
|
1513
|
+
{
|
|
1514
|
+
role: role.role,
|
|
1515
|
+
role_dir: relative(ctx.repoRoot, role.roleDir)
|
|
1516
|
+
}
|
|
1517
|
+
])
|
|
1518
|
+
)
|
|
1519
|
+
};
|
|
1520
|
+
}
|
|
1521
|
+
function projectJsonFinding(ctx) {
|
|
1522
|
+
const projectPath = join6(ctx.repoRoot, ".project.json");
|
|
1523
|
+
const planeJsonPath = join6(ctx.repoRoot, ".plane.json");
|
|
1524
|
+
const details = [];
|
|
1525
|
+
const data = readProjectJson(ctx);
|
|
1526
|
+
const roles = discoverRoles(ctx.repoRoot);
|
|
1527
|
+
if (!existsSync5(projectPath)) {
|
|
1528
|
+
return { id: "sot.project-json", title: "Canonical .project.json", status: "fail", summary: ".project.json missing", details: [], fixable: true };
|
|
1529
|
+
}
|
|
1530
|
+
if (!data) {
|
|
1531
|
+
return { id: "sot.project-json", title: "Canonical .project.json", status: "fail", summary: ".project.json is not valid JSON", details: [], fixable: true };
|
|
1532
|
+
}
|
|
1533
|
+
for (const key of ["project_name", "project_description", "project_slug", "repo_path", "ticket_provider", "agents"]) {
|
|
1534
|
+
if (!(key in data)) details.push(`missing key: ${key}`);
|
|
1535
|
+
}
|
|
1536
|
+
if (data.repo_path !== ctx.repoRoot) details.push(`repo_path should be ${ctx.repoRoot}`);
|
|
1537
|
+
const agents = data.agents ?? {};
|
|
1538
|
+
for (const role of roles) {
|
|
1539
|
+
const agent = agents[role.agentId];
|
|
1540
|
+
if (!agent) {
|
|
1541
|
+
details.push(`agents.${role.agentId} missing`);
|
|
1542
|
+
continue;
|
|
1543
|
+
}
|
|
1544
|
+
if (agent.role !== role.role) details.push(`agents.${role.agentId}.role should be ${role.role}`);
|
|
1545
|
+
if (agent.role_dir !== relative(ctx.repoRoot, role.roleDir)) {
|
|
1546
|
+
details.push(`agents.${role.agentId}.role_dir should be ${relative(ctx.repoRoot, role.roleDir)}`);
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
const ticketProvider = data.ticket_provider ?? {};
|
|
1550
|
+
for (const key of ["type", "workspace", "identifier", "board_id", "board_url"]) {
|
|
1551
|
+
if (!(key in ticketProvider)) details.push(`ticket_provider.${key} missing`);
|
|
1552
|
+
}
|
|
1553
|
+
if (existsSync5(planeJsonPath)) details.push(".plane.json should not exist once .project.json is canonical");
|
|
1554
|
+
return {
|
|
1555
|
+
id: "sot.project-json",
|
|
1556
|
+
title: "Canonical .project.json",
|
|
1557
|
+
status: details.length === 0 ? "pass" : "fail",
|
|
1558
|
+
summary: details.length === 0 ? ".project.json matches canonical parity contract" : `${details.length} parity issue(s) detected`,
|
|
1559
|
+
details,
|
|
1560
|
+
fixable: true
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
function renderSoul(role) {
|
|
1564
|
+
const telegram = role.botHandle ? `@${role.botHandle}` : "(unwired)";
|
|
1565
|
+
const tone = role.role === "pm" ? `Direct and brief. Decision-forward. No throat-clearing, no apologies, no "I'll help you with that" preambles.` : role.role === "scrum-master" ? "Operational, skeptical, and schedule-aware. Prefer explicit next actions, evidence, and status transitions." : "Direct and brief.";
|
|
1566
|
+
const roleSpecific = role.role === "pm" ? `You are the project manager. You triage incoming work, create or refine tickets, and delegate implementation. You do not ship product code.` : role.role === "scrum-master" ? `You own the continuous-ticket sentinel. You watch the ticket board, enforce workflow policy, and keep work moving without inventing requirements.` : `You operate as the ${role.role} agent for this repo.`;
|
|
1567
|
+
const runtimeOwner = role.runtimeOwner || "delorenj";
|
|
1568
|
+
return `# ${role.displayName || role.agentId}
|
|
1569
|
+
|
|
1570
|
+
You are **${role.displayName || role.agentId}** \u2014 a Hermes agent provisioned to work inside the
|
|
1571
|
+
\`${role.repo}\` repository.
|
|
1572
|
+
|
|
1573
|
+
## Identity
|
|
1574
|
+
|
|
1575
|
+
| | |
|
|
1576
|
+
| --- | --- |
|
|
1577
|
+
| Agent ID | \`${role.agentId}\` |
|
|
1578
|
+
| Profile | \`${role.profileName || role.agentId}\` |
|
|
1579
|
+
| Repo | \`${role.repo}\` |
|
|
1580
|
+
| Role | \`${role.role}\` |
|
|
1581
|
+
| Telegram | \`${telegram}\` |
|
|
1582
|
+
| Purpose | ${role.purpose || `${role.role} agent for ${role.repo}`} |
|
|
1583
|
+
|
|
1584
|
+
## Scope
|
|
1585
|
+
|
|
1586
|
+
You operate only within the working directory of \`${role.repo}\`. Your HERMES_HOME resolves through the named profile \`${role.profileName || role.agentId}\`, which is symlinked to the runtime submodule at \`./runtime/\` (repo \`${runtimeOwner}/${role.runtimeRepo}\`). Your \`config.yaml\` inherits shared non-secret defaults from the fleet default profile; secrets, SOUL, memories, skills, sessions, gateway state, and runtime files remain local to this profile.
|
|
1587
|
+
|
|
1588
|
+
## Tone
|
|
1589
|
+
|
|
1590
|
+
${tone}
|
|
1591
|
+
|
|
1592
|
+
## Role-specific behavior
|
|
1593
|
+
|
|
1594
|
+
${roleSpecific}
|
|
1595
|
+
|
|
1596
|
+
## Memory hygiene
|
|
1597
|
+
|
|
1598
|
+
Your memory is the submodule at \`./runtime/memories/\`. Use durable memory deliberately and keep \`memories/MEMORY.md\` current.
|
|
1599
|
+
`;
|
|
1600
|
+
}
|
|
1601
|
+
function renderHermesWrapper(role) {
|
|
1602
|
+
return `#!/usr/bin/env bash
|
|
1603
|
+
# Launcher for ${role.agentId}. Resolves HERMES_HOME through the fleet profile
|
|
1604
|
+
# when available, then falls back to the local runtime submodule.
|
|
1605
|
+
|
|
1606
|
+
set -euo pipefail
|
|
1607
|
+
|
|
1608
|
+
ROLE_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
1609
|
+
RUNTIME_HOME="$ROLE_DIR/runtime"
|
|
1610
|
+
|
|
1611
|
+
FLEET_ENV="{HERMES_FLEET_ENV:-$HOME/.hermes/fleet.env}"
|
|
1612
|
+
if [[ -f "$FLEET_ENV" ]]; then
|
|
1613
|
+
# shellcheck disable=SC1090
|
|
1614
|
+
source "$FLEET_ENV"
|
|
1615
|
+
fi
|
|
1616
|
+
|
|
1617
|
+
HERMES_BIN="{HERMES_BIN:-{HERMES_FLEET_BIN:-/home/delorenj/code/hermes-agent/.venv/bin/hermes}}"
|
|
1618
|
+
HERMES_OAUTH_FILE="{HERMES_OAUTH_FILE:-{HERMES_FLEET_OAUTH_FILE:-$HOME/.hermes/auth.json}}"
|
|
1619
|
+
CODEX_HOME="{CODEX_HOME:-{HERMES_FLEET_CODEX_HOME:-$HOME/.codex}}"
|
|
1620
|
+
|
|
1621
|
+
FLEET_HOME="{HERMES_FLEET_HOME:-$HOME/.hermes}"
|
|
1622
|
+
PROFILE_NAME="{HERMES_PROFILE_NAME:-${role.profileName || role.agentId}}"
|
|
1623
|
+
PROFILE_HOME="$FLEET_HOME/profiles/$PROFILE_NAME"
|
|
1624
|
+
if [[ -d "$PROFILE_HOME" ]]; then
|
|
1625
|
+
HERMES_HOME="$PROFILE_HOME"
|
|
1626
|
+
else
|
|
1627
|
+
HERMES_HOME="$RUNTIME_HOME"
|
|
1628
|
+
fi
|
|
1629
|
+
|
|
1630
|
+
if [[ ! -d "$RUNTIME_HOME" ]]; then
|
|
1631
|
+
echo "hermes: runtime submodule not initialized at $RUNTIME_HOME" >&2
|
|
1632
|
+
echo " fix: git submodule update --init --recursive" >&2
|
|
1633
|
+
exit 1
|
|
1634
|
+
fi
|
|
1635
|
+
|
|
1636
|
+
exec env HERMES_HOME="$HERMES_HOME" HERMES_FLEET_ENV="$FLEET_ENV" HERMES_OAUTH_FILE="$HERMES_OAUTH_FILE" CODEX_HOME="$CODEX_HOME" "$HERMES_BIN" "$@"
|
|
1637
|
+
`.replace(/\u0010/g, "$");
|
|
1638
|
+
}
|
|
1639
|
+
function copyMissingRecursive(sourceDir, targetDir, changedFiles, dryRun, skip) {
|
|
1640
|
+
if (!existsSync5(sourceDir)) return;
|
|
1641
|
+
mkdirSync3(targetDir, { recursive: true });
|
|
1642
|
+
for (const entry of readdirSync(sourceDir, { withFileTypes: true })) {
|
|
1643
|
+
const sourcePath = join6(sourceDir, entry.name);
|
|
1644
|
+
if (skip?.(sourcePath)) continue;
|
|
1645
|
+
const targetPath = join6(targetDir, entry.name);
|
|
1646
|
+
if (entry.isDirectory()) {
|
|
1647
|
+
copyMissingRecursive(sourcePath, targetPath, changedFiles, dryRun, skip);
|
|
1648
|
+
continue;
|
|
1649
|
+
}
|
|
1650
|
+
if (existsSync5(targetPath)) continue;
|
|
1651
|
+
changedFiles.push(targetPath);
|
|
1652
|
+
if (!dryRun) {
|
|
1653
|
+
ensureParent(targetPath);
|
|
1654
|
+
copyFileSync(sourcePath, targetPath);
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
function upsertSubmodule(repoRoot, role, changedFiles, dryRun) {
|
|
1659
|
+
const gitmodulesPath = join6(repoRoot, ".gitmodules");
|
|
1660
|
+
const repoName = role.runtimeRepo || `agent-hm-${role.repo}-${role.role}`;
|
|
1661
|
+
const owner = role.runtimeOwner || "delorenj";
|
|
1662
|
+
const block = `[submodule "agents/hermes/${role.role}/runtime"]
|
|
1663
|
+
path = agents/hermes/${role.role}/runtime
|
|
1664
|
+
url = git@github.com:${owner}/${repoName}.git
|
|
1665
|
+
`;
|
|
1666
|
+
const current = safeReadText(gitmodulesPath) ?? "";
|
|
1667
|
+
const header = `[submodule "agents/hermes/${role.role}/runtime"]`;
|
|
1668
|
+
if (current.includes(header)) return [];
|
|
1669
|
+
changedFiles.push(gitmodulesPath);
|
|
1670
|
+
if (!dryRun) writeText(gitmodulesPath, `${current.replace(/\s*$/, "")}${current.trim() ? "\n" : ""}${block}`);
|
|
1671
|
+
return [gitmodulesPath];
|
|
1672
|
+
}
|
|
1673
|
+
function upsertRegistryEntry(role, homeDir, changedFiles, dryRun) {
|
|
1674
|
+
const path = registryPath(homeDir);
|
|
1675
|
+
const current = safeReadText(path) ?? "# Hermes agent fleet registry.\n# One entry per provisioned agent. Managed by hermes-agent-template/.scripts/80-registry.sh.\nschema_version: 1\nagents: {}\n";
|
|
1676
|
+
if (current.includes(`${role.agentId}:`)) return null;
|
|
1677
|
+
const block = ` ${role.agentId}:
|
|
1678
|
+
repo: ${role.repo}
|
|
1679
|
+
role: ${role.role}
|
|
1680
|
+
display_name: ${JSON.stringify(role.displayName || role.agentId)}
|
|
1681
|
+
project_path: ${ctxEscape(role.roleDir ? dirname3(dirname3(dirname3(role.roleDir))) : "")}
|
|
1682
|
+
role_dir: ${ctxEscape(role.roleDir)}
|
|
1683
|
+
profile_name: ${role.profileName || role.agentId}
|
|
1684
|
+
telegram:
|
|
1685
|
+
bot_username: ${ctxEscape(role.botHandle)}
|
|
1686
|
+
plane:
|
|
1687
|
+
workspace: ${ctxEscape(role.planeWorkspace)}
|
|
1688
|
+
project_id: ${ctxEscape(role.ticketProviderBoardId)}
|
|
1689
|
+
identifier: ${ctxEscape(role.ticketProviderIdentifier)}
|
|
1690
|
+
runtime_repo: ${ctxEscape(role.runtimeRepo)}
|
|
1691
|
+
systemd:
|
|
1692
|
+
gateway_unit: hermes-${role.agentId}-gateway.service
|
|
1693
|
+
consumer_unit: hermes-${role.agentId}-consumer.service
|
|
1694
|
+
checkpoint_timer: hermes-${role.agentId}-checkpoint.timer
|
|
1695
|
+
`;
|
|
1696
|
+
const next = current.includes("agents: {}") ? current.replace("agents: {}", `agents:
|
|
1697
|
+
${block}`) : `${current.replace(/\s*$/, "\n")}${block}`;
|
|
1698
|
+
changedFiles.push(path);
|
|
1699
|
+
if (!dryRun) writeText(path, next);
|
|
1700
|
+
return path;
|
|
1701
|
+
}
|
|
1702
|
+
function profileMetaInheritsDefault(path) {
|
|
1703
|
+
const text3 = safeReadText(path);
|
|
1704
|
+
return Boolean(
|
|
1705
|
+
text3 && /^config:\s*$/m.test(text3) && /^\s+inherit_from:\s*default\s*$/m.test(text3) && /^\s+save_mode:\s*delta\s*$/m.test(text3)
|
|
1706
|
+
);
|
|
1707
|
+
}
|
|
1708
|
+
function upsertInheritedProfileMeta(path, changedFiles, dryRun) {
|
|
1709
|
+
const current = safeReadText(path) ?? "";
|
|
1710
|
+
const lines = current.split("\n");
|
|
1711
|
+
let next;
|
|
1712
|
+
const start = lines.findIndex((line) => /^config:\s*$/.test(line));
|
|
1713
|
+
if (!current.trim()) {
|
|
1714
|
+
next = "config:\n inherit_from: default\n save_mode: delta\n";
|
|
1715
|
+
} else if (start === -1) {
|
|
1716
|
+
next = `${current.replace(/\s*$/, "\n")}config:
|
|
1717
|
+
inherit_from: default
|
|
1718
|
+
save_mode: delta
|
|
1719
|
+
`;
|
|
1720
|
+
} else {
|
|
1721
|
+
let end = start + 1;
|
|
1722
|
+
while (end < lines.length && !/^[^#\s][^:]*:\s*/.test(lines[end] ?? "")) end++;
|
|
1723
|
+
let hasInherit = false;
|
|
1724
|
+
let hasSave = false;
|
|
1725
|
+
for (let idx = start + 1; idx < end; idx++) {
|
|
1726
|
+
if (/^\s+inherit_from:\s*/.test(lines[idx] ?? "")) {
|
|
1727
|
+
lines[idx] = " inherit_from: default";
|
|
1728
|
+
hasInherit = true;
|
|
1729
|
+
} else if (/^\s+save_mode:\s*/.test(lines[idx] ?? "")) {
|
|
1730
|
+
lines[idx] = " save_mode: delta";
|
|
1731
|
+
hasSave = true;
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
const inserts = [];
|
|
1735
|
+
if (!hasInherit) inserts.push(" inherit_from: default");
|
|
1736
|
+
if (!hasSave) inserts.push(" save_mode: delta");
|
|
1737
|
+
if (inserts.length) lines.splice(end, 0, ...inserts);
|
|
1738
|
+
next = lines.join("\n");
|
|
1739
|
+
if (!next.endsWith("\n")) next += "\n";
|
|
1740
|
+
}
|
|
1741
|
+
if (next === current) return null;
|
|
1742
|
+
changedFiles.push(path);
|
|
1743
|
+
if (!dryRun) writeText(path, next);
|
|
1744
|
+
return path;
|
|
1745
|
+
}
|
|
1746
|
+
function ctxEscape(value) {
|
|
1747
|
+
return JSON.stringify(value || "");
|
|
1748
|
+
}
|
|
1749
|
+
function checkUnit(unit) {
|
|
1750
|
+
const enabled = systemctlUser(["is-enabled", unit]).ok;
|
|
1751
|
+
const active = systemctlUser(["is-active", unit]).ok;
|
|
1752
|
+
return { enabled, active };
|
|
1753
|
+
}
|
|
1754
|
+
var RULES = [
|
|
1755
|
+
{
|
|
1756
|
+
id: "mise.config-root",
|
|
1757
|
+
title: "mise config_root + AGENTS link hooks",
|
|
1758
|
+
audit: (ctx) => {
|
|
1759
|
+
const misePath = join6(ctx.repoRoot, "mise.toml");
|
|
1760
|
+
if (!existsSync5(misePath)) {
|
|
1761
|
+
return { id: "mise.config-root", title: "mise config_root + AGENTS link hooks", status: "fail", summary: "mise.toml missing", details: [], fixable: true };
|
|
1762
|
+
}
|
|
1763
|
+
const text3 = readText(misePath);
|
|
1764
|
+
const details = [];
|
|
1765
|
+
if (!text3.includes('_.path = [".mise/scripts", "agents/hermes/pm"]')) details.push("[env]._.path should include .mise/scripts and agents/hermes/pm");
|
|
1766
|
+
if (!text3.includes('"{{config_root}}/.mise/scripts/link-agentfiles.sh"')) details.push("link-agentfiles must use raw {{config_root}} guard");
|
|
1767
|
+
if (!text3.includes("op inject -i .env.op > .env")) details.push("[hooks].enter must materialize .env from .env.op");
|
|
1768
|
+
if (!text3.includes('patterns = ["AGENTS.md"]')) details.push("watch_files must monitor AGENTS.md");
|
|
1769
|
+
if (!text3.includes('task = "link-agentfiles"')) details.push("watch_files must dispatch link-agentfiles task");
|
|
1770
|
+
return {
|
|
1771
|
+
id: "mise.config-root",
|
|
1772
|
+
title: "mise config_root + AGENTS link hooks",
|
|
1773
|
+
status: details.length === 0 ? "pass" : "fail",
|
|
1774
|
+
summary: details.length === 0 ? "mise AGENTS-linking parity verified" : `${details.length} issue(s) detected in mise AGENTS-linking contract`,
|
|
1775
|
+
details,
|
|
1776
|
+
fixable: true
|
|
1777
|
+
};
|
|
1778
|
+
},
|
|
1779
|
+
migrate: (ctx, finding) => {
|
|
1780
|
+
const path = join6(ctx.repoRoot, "mise.toml");
|
|
1781
|
+
const changedFiles = [];
|
|
1782
|
+
if (!existsSync5(path)) {
|
|
1783
|
+
return { id: finding.id, title: finding.title, status: "blocked", summary: "mise.toml missing; initialize mise first", changedFiles, details: [] };
|
|
1784
|
+
}
|
|
1785
|
+
let text3 = readText(path);
|
|
1786
|
+
const next = upsertLinkAgentfilesBlock(text3);
|
|
1787
|
+
if (next !== text3) {
|
|
1788
|
+
changedFiles.push(path);
|
|
1789
|
+
if (!ctx.dryRun) writeText(path, next);
|
|
1790
|
+
text3 = next;
|
|
1791
|
+
}
|
|
1792
|
+
return {
|
|
1793
|
+
id: finding.id,
|
|
1794
|
+
title: finding.title,
|
|
1795
|
+
status: changedFiles.length ? "applied" : "noop",
|
|
1796
|
+
summary: changedFiles.length ? "Updated mise AGENTS-linking contract" : "No changes required",
|
|
1797
|
+
changedFiles,
|
|
1798
|
+
details: changedFiles.length ? ["Normalized hooks/watch_files/tasks.link-agentfiles block"] : []
|
|
1799
|
+
};
|
|
1800
|
+
}
|
|
1801
|
+
},
|
|
1802
|
+
{
|
|
1803
|
+
id: "mise.versioning",
|
|
1804
|
+
title: "managed mise versioning block",
|
|
1805
|
+
audit: (ctx) => {
|
|
1806
|
+
const details = [];
|
|
1807
|
+
const misePath = join6(ctx.repoRoot, "mise.toml");
|
|
1808
|
+
const versioningPath = join6(ctx.repoRoot, ".mise", "scripts", "versioning.sh");
|
|
1809
|
+
const manifestPath = join6(ctx.repoRoot, ".mise", "version-files.conf");
|
|
1810
|
+
const text3 = safeReadText(misePath);
|
|
1811
|
+
if (!text3?.includes("# >>> mise-versioning >>>")) details.push("mise versioning managed block missing");
|
|
1812
|
+
if (!existsSync5(versioningPath)) details.push(".mise/scripts/versioning.sh missing");
|
|
1813
|
+
if (!existsSync5(manifestPath)) details.push(".mise/version-files.conf missing");
|
|
1814
|
+
return {
|
|
1815
|
+
id: "mise.versioning",
|
|
1816
|
+
title: "managed mise versioning block",
|
|
1817
|
+
status: details.length === 0 ? "pass" : "fail",
|
|
1818
|
+
summary: details.length === 0 ? "mise versioning parity verified" : `${details.length} versioning issue(s) detected`,
|
|
1819
|
+
details,
|
|
1820
|
+
fixable: true
|
|
1821
|
+
};
|
|
1822
|
+
},
|
|
1823
|
+
migrate: (ctx, finding) => {
|
|
1824
|
+
const changedFiles = [];
|
|
1825
|
+
const misePath = join6(ctx.repoRoot, "mise.toml");
|
|
1826
|
+
if (!existsSync5(misePath)) {
|
|
1827
|
+
return { id: finding.id, title: finding.title, status: "blocked", summary: "mise.toml missing; cannot inject versioning block", changedFiles, details: [] };
|
|
1828
|
+
}
|
|
1829
|
+
const currentMise = readText(misePath);
|
|
1830
|
+
const nextMise = replaceOrAppendManagedBlock(currentMise, /# >>> mise-versioning >>>/, VERSIONING_BLOCK, /^\[tasks\.build\]/m);
|
|
1831
|
+
if (nextMise !== currentMise) {
|
|
1832
|
+
changedFiles.push(misePath);
|
|
1833
|
+
if (!ctx.dryRun) writeText(misePath, nextMise);
|
|
1834
|
+
}
|
|
1835
|
+
const versioningPath = join6(ctx.repoRoot, ".mise", "scripts", "versioning.sh");
|
|
1836
|
+
const expectedScript = templateVersioningScript(ctx);
|
|
1837
|
+
if (safeReadText(versioningPath) !== expectedScript) {
|
|
1838
|
+
changedFiles.push(versioningPath);
|
|
1839
|
+
if (!ctx.dryRun) {
|
|
1840
|
+
writeText(versioningPath, expectedScript);
|
|
1841
|
+
chmodSync(versioningPath, 493);
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
const manifestPath = join6(ctx.repoRoot, ".mise", "version-files.conf");
|
|
1845
|
+
const expectedManifest = templateVersionFilesConf(ctx, ctx.repoRoot);
|
|
1846
|
+
if (safeReadText(manifestPath) !== expectedManifest) {
|
|
1847
|
+
changedFiles.push(manifestPath);
|
|
1848
|
+
if (!ctx.dryRun) writeText(manifestPath, expectedManifest);
|
|
1849
|
+
}
|
|
1850
|
+
return {
|
|
1851
|
+
id: finding.id,
|
|
1852
|
+
title: finding.title,
|
|
1853
|
+
status: changedFiles.length ? "applied" : "noop",
|
|
1854
|
+
summary: changedFiles.length ? "Versioning block/script/manifest normalized" : "No changes required",
|
|
1855
|
+
changedFiles,
|
|
1856
|
+
details: []
|
|
1857
|
+
};
|
|
1858
|
+
}
|
|
1859
|
+
},
|
|
1860
|
+
{
|
|
1861
|
+
id: "sot.agent-symlinks",
|
|
1862
|
+
title: "AGENTS/CLAUDE/GEMINI symlink contract",
|
|
1863
|
+
audit: (ctx) => {
|
|
1864
|
+
const agentsPath = join6(ctx.repoRoot, "AGENTS.md");
|
|
1865
|
+
if (!existsSync5(agentsPath)) {
|
|
1866
|
+
return { id: "sot.agent-symlinks", title: "AGENTS/CLAUDE/GEMINI symlink contract", status: "skip", summary: "AGENTS.md missing; symlink contract not applicable", details: [], fixable: false };
|
|
1867
|
+
}
|
|
1868
|
+
const details = [];
|
|
1869
|
+
for (const file of ["CLAUDE.md", "GEMINI.md"]) {
|
|
1870
|
+
const full = join6(ctx.repoRoot, file);
|
|
1871
|
+
const target = readSymlinkTarget(full);
|
|
1872
|
+
if (target !== "AGENTS.md") details.push(`${file} should be a symlink to AGENTS.md`);
|
|
1873
|
+
}
|
|
1874
|
+
return {
|
|
1875
|
+
id: "sot.agent-symlinks",
|
|
1876
|
+
title: "AGENTS/CLAUDE/GEMINI symlink contract",
|
|
1877
|
+
status: details.length === 0 ? "pass" : "fail",
|
|
1878
|
+
summary: details.length === 0 ? "Agent documentation symlinks are in parity" : `${details.length} symlink issue(s) detected`,
|
|
1879
|
+
details,
|
|
1880
|
+
fixable: true
|
|
1881
|
+
};
|
|
1882
|
+
},
|
|
1883
|
+
migrate: (ctx, finding) => {
|
|
1884
|
+
const changedFiles = [];
|
|
1885
|
+
const details = [];
|
|
1886
|
+
if (!existsSync5(join6(ctx.repoRoot, "AGENTS.md"))) {
|
|
1887
|
+
return { id: finding.id, title: finding.title, status: "blocked", summary: "AGENTS.md missing; cannot wire symlinks", changedFiles, details: [] };
|
|
1888
|
+
}
|
|
1889
|
+
for (const file of ["CLAUDE.md", "GEMINI.md"]) {
|
|
1890
|
+
const full = join6(ctx.repoRoot, file);
|
|
1891
|
+
const result = ensureSymlink(full, "AGENTS.md", ctx.dryRun);
|
|
1892
|
+
if (result.blocked) details.push(result.blocked);
|
|
1893
|
+
if (result.changed) changedFiles.push(full);
|
|
1894
|
+
}
|
|
1895
|
+
return {
|
|
1896
|
+
id: finding.id,
|
|
1897
|
+
title: finding.title,
|
|
1898
|
+
status: details.length ? "blocked" : changedFiles.length ? "applied" : "noop",
|
|
1899
|
+
summary: details.length ? "One or more files could not be replaced safely" : changedFiles.length ? "Symlink contract repaired" : "No changes required",
|
|
1900
|
+
changedFiles,
|
|
1901
|
+
details
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1904
|
+
},
|
|
1905
|
+
{
|
|
1906
|
+
id: "sot.project-json",
|
|
1907
|
+
title: "Canonical .project.json",
|
|
1908
|
+
audit: projectJsonFinding,
|
|
1909
|
+
migrate: (ctx, finding) => {
|
|
1910
|
+
const changedFiles = [];
|
|
1911
|
+
const details = [];
|
|
1912
|
+
const path = join6(ctx.repoRoot, ".project.json");
|
|
1913
|
+
const existing = readProjectJson(ctx) ?? {};
|
|
1914
|
+
const canonical = canonicalProjectJson(ctx);
|
|
1915
|
+
const merged = { ...existing, ...canonical };
|
|
1916
|
+
const expected = `${JSON.stringify(merged, null, 2)}
|
|
1917
|
+
`;
|
|
1918
|
+
if (safeReadText(path) !== expected) {
|
|
1919
|
+
changedFiles.push(path);
|
|
1920
|
+
if (!ctx.dryRun) writeText(path, expected);
|
|
1921
|
+
}
|
|
1922
|
+
const planeJson = join6(ctx.repoRoot, ".plane.json");
|
|
1923
|
+
if (existsSync5(planeJson)) {
|
|
1924
|
+
const backup = `${planeJson}.migrated-backup`;
|
|
1925
|
+
if (existsSync5(backup)) {
|
|
1926
|
+
details.push(`cannot back up .plane.json because ${relative(ctx.repoRoot, backup)} already exists`);
|
|
1927
|
+
} else {
|
|
1928
|
+
changedFiles.push(backup);
|
|
1929
|
+
if (!ctx.dryRun) renameSync(planeJson, backup);
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
return {
|
|
1933
|
+
id: finding.id,
|
|
1934
|
+
title: finding.title,
|
|
1935
|
+
status: details.length ? "blocked" : changedFiles.length ? "applied" : "noop",
|
|
1936
|
+
summary: details.length ? "Project SOT partially blocked" : changedFiles.length ? "Canonical .project.json written" : "No changes required",
|
|
1937
|
+
changedFiles,
|
|
1938
|
+
details
|
|
1939
|
+
};
|
|
1940
|
+
}
|
|
1941
|
+
},
|
|
1942
|
+
{
|
|
1943
|
+
id: "secrets.env-op",
|
|
1944
|
+
title: ".env.op + gitignore secrets contract",
|
|
1945
|
+
audit: (ctx) => {
|
|
1946
|
+
const details = [];
|
|
1947
|
+
const envOp = safeReadText(join6(ctx.repoRoot, ".env.op"));
|
|
1948
|
+
const gitignore = safeReadText(join6(ctx.repoRoot, ".gitignore"));
|
|
1949
|
+
if (!envOp) {
|
|
1950
|
+
details.push(".env.op missing");
|
|
1951
|
+
} else {
|
|
1952
|
+
const invalidLines = envOp.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#") && line.includes("=")).filter((line) => {
|
|
1953
|
+
const value = line.slice(line.indexOf("=") + 1).trim();
|
|
1954
|
+
return !value.startsWith("op://") && !/^https?:\/\//.test(value) && !/^[A-Za-z0-9_.:-]+$/.test(value);
|
|
1955
|
+
});
|
|
1956
|
+
if (invalidLines.length) details.push(`.env.op has non-reference values that do not look like safe literals: ${invalidLines.join(", ")}`);
|
|
1957
|
+
}
|
|
1958
|
+
if (!gitignore?.includes(".env\n") && !gitignore?.includes(".env\r\n")) details.push(".gitignore should ignore .env");
|
|
1959
|
+
if (!gitignore?.includes(".env.*")) details.push(".gitignore should ignore .env.*");
|
|
1960
|
+
if (!gitignore?.includes("!.env.op")) details.push(".gitignore should unignore .env.op");
|
|
1961
|
+
return {
|
|
1962
|
+
id: "secrets.env-op",
|
|
1963
|
+
title: ".env.op + gitignore secrets contract",
|
|
1964
|
+
status: details.length === 0 ? "pass" : "fail",
|
|
1965
|
+
summary: details.length === 0 ? "Secret reference file and ignore rules are in parity" : `${details.length} env parity issue(s) detected`,
|
|
1966
|
+
details,
|
|
1967
|
+
fixable: true
|
|
1968
|
+
};
|
|
1969
|
+
},
|
|
1970
|
+
migrate: (ctx, finding) => {
|
|
1971
|
+
const changedFiles = [];
|
|
1972
|
+
const details = [];
|
|
1973
|
+
const envOpPath = join6(ctx.repoRoot, ".env.op");
|
|
1974
|
+
if (!existsSync5(envOpPath)) {
|
|
1975
|
+
changedFiles.push(envOpPath);
|
|
1976
|
+
if (!ctx.dryRun) writeText(envOpPath, readText(join6(ctx.pjanglerRoot, "templates", "commonproject", "template", ".env.op")));
|
|
1977
|
+
}
|
|
1978
|
+
const gitignorePath = join6(ctx.repoRoot, ".gitignore");
|
|
1979
|
+
const gitignore = safeReadText(gitignorePath) ?? "";
|
|
1980
|
+
const requiredBlock = `# Secrets \u2014 .env is materialized by \`op inject -i .env.op > .env\` on mise enter.
|
|
1981
|
+
# NEVER commit it. .env.op holds only 1Password references or safe literals and IS committed.
|
|
1982
|
+
.env
|
|
1983
|
+
.env.*
|
|
1984
|
+
!.env.op
|
|
1985
|
+
`;
|
|
1986
|
+
if (!gitignore.includes("!.env.op") || !gitignore.includes(".env.*")) {
|
|
1987
|
+
changedFiles.push(gitignorePath);
|
|
1988
|
+
if (!ctx.dryRun) writeText(gitignorePath, `${gitignore.replace(/\s*$/, "")}${gitignore.trim() ? "\n\n" : ""}${requiredBlock}`);
|
|
1989
|
+
}
|
|
1990
|
+
return {
|
|
1991
|
+
id: finding.id,
|
|
1992
|
+
title: finding.title,
|
|
1993
|
+
status: details.length ? "blocked" : changedFiles.length ? "applied" : "noop",
|
|
1994
|
+
summary: details.length ? "Manual cleanup still required" : changedFiles.length ? "Wrote .env.op/gitignore parity files" : "No changes required",
|
|
1995
|
+
changedFiles,
|
|
1996
|
+
details
|
|
1997
|
+
};
|
|
1998
|
+
}
|
|
1999
|
+
},
|
|
2000
|
+
{
|
|
2001
|
+
id: "provenance.copier",
|
|
2002
|
+
title: ".copier-answers.yml provenance + drift report",
|
|
2003
|
+
audit: (ctx) => {
|
|
2004
|
+
const details = [];
|
|
2005
|
+
const path = join6(ctx.repoRoot, ".copier-answers.yml");
|
|
2006
|
+
const text3 = safeReadText(path);
|
|
2007
|
+
const project = readProjectJson(ctx);
|
|
2008
|
+
if (!text3) {
|
|
2009
|
+
details.push(".copier-answers.yml missing");
|
|
2010
|
+
} else {
|
|
2011
|
+
if (!text3.startsWith("# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY")) details.push("missing Copier overwrite warning header");
|
|
2012
|
+
if (!text3.includes("_src_path:")) details.push("_src_path missing");
|
|
2013
|
+
if (project?.project_name) {
|
|
2014
|
+
const nameMatch = text3.match(/project_name:\s*(.+)/);
|
|
2015
|
+
if (!nameMatch || nameMatch[1]?.trim() !== String(project.project_name)) details.push("project_name drift between .copier-answers.yml and .project.json");
|
|
2016
|
+
}
|
|
2017
|
+
if (project?.project_description) {
|
|
2018
|
+
const descMatch = text3.match(/project_description:\s*([\s\S]*?)(?=\n\w|$)/);
|
|
2019
|
+
const yamlDesc = descMatch?.[1]?.replace(/\n\s+/g, " ").trim() ?? "";
|
|
2020
|
+
if (yamlDesc !== String(project.project_description)) details.push("project_description drift between .copier-answers.yml and .project.json");
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
return {
|
|
2024
|
+
id: "provenance.copier",
|
|
2025
|
+
title: ".copier-answers.yml provenance + drift report",
|
|
2026
|
+
status: details.length === 0 ? "pass" : "fail",
|
|
2027
|
+
summary: details.length === 0 ? "Copier provenance is in parity" : `${details.length} provenance issue(s) detected`,
|
|
2028
|
+
details,
|
|
2029
|
+
fixable: true
|
|
2030
|
+
};
|
|
2031
|
+
},
|
|
2032
|
+
migrate: (ctx, finding) => {
|
|
2033
|
+
const changedFiles = [];
|
|
2034
|
+
const project = canonicalProjectJson(ctx);
|
|
2035
|
+
const text3 = `# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY
|
|
2036
|
+
_src_path: ${join6(ctx.pjanglerRoot, "templates", "commonproject")}
|
|
2037
|
+
project_description: ${String(project.project_description)}
|
|
2038
|
+
project_name: ${String(project.project_name)}
|
|
2039
|
+
ticket_provider: ${String(project.ticket_provider?.type ?? "plane")}
|
|
2040
|
+
`;
|
|
2041
|
+
const path = join6(ctx.repoRoot, ".copier-answers.yml");
|
|
2042
|
+
if (safeReadText(path) !== text3) {
|
|
2043
|
+
changedFiles.push(path);
|
|
2044
|
+
if (!ctx.dryRun) writeText(path, text3);
|
|
2045
|
+
}
|
|
2046
|
+
return {
|
|
2047
|
+
id: finding.id,
|
|
2048
|
+
title: finding.title,
|
|
2049
|
+
status: changedFiles.length ? "applied" : "noop",
|
|
2050
|
+
summary: changedFiles.length ? "Copier provenance file refreshed" : "No changes required",
|
|
2051
|
+
changedFiles,
|
|
2052
|
+
details: []
|
|
2053
|
+
};
|
|
2054
|
+
}
|
|
2055
|
+
},
|
|
2056
|
+
{
|
|
2057
|
+
id: "bmad.scaffold",
|
|
2058
|
+
title: "BMAD modules/docs scaffold",
|
|
2059
|
+
audit: (ctx) => {
|
|
2060
|
+
const sourceRoot = join6(ctx.pjanglerRoot, "templates", "commonproject", "_bmad");
|
|
2061
|
+
const targetRoot = join6(ctx.repoRoot, "_bmad");
|
|
2062
|
+
const sentinels = [
|
|
2063
|
+
join6("core", "config.yaml"),
|
|
2064
|
+
join6("custom", "config.yaml"),
|
|
2065
|
+
join6("custom", "workflows", "ticket-lifecycle", "workflow.yaml"),
|
|
2066
|
+
join6("bmm", "workflows", "workflow-status", "workflow.yaml")
|
|
2067
|
+
];
|
|
2068
|
+
const missing = sentinels.filter((file) => existsSync5(join6(sourceRoot, file)) && !existsSync5(join6(targetRoot, file)));
|
|
2069
|
+
return {
|
|
2070
|
+
id: "bmad.scaffold",
|
|
2071
|
+
title: "BMAD modules/docs scaffold",
|
|
2072
|
+
status: missing.length === 0 ? "pass" : "fail",
|
|
2073
|
+
summary: missing.length === 0 ? "BMAD scaffold parity verified" : `${missing.length} BMAD sentinel file(s) missing`,
|
|
2074
|
+
details: missing.map((file) => `_bmad/${file}`),
|
|
2075
|
+
fixable: true
|
|
2076
|
+
};
|
|
2077
|
+
},
|
|
2078
|
+
migrate: (ctx, finding) => {
|
|
2079
|
+
const changedFiles = [];
|
|
2080
|
+
copyMissingRecursive(join6(ctx.pjanglerRoot, "templates", "commonproject", "_bmad"), join6(ctx.repoRoot, "_bmad"), changedFiles, ctx.dryRun);
|
|
2081
|
+
return {
|
|
2082
|
+
id: finding.id,
|
|
2083
|
+
title: finding.title,
|
|
2084
|
+
status: changedFiles.length ? "applied" : "noop",
|
|
2085
|
+
summary: changedFiles.length ? "Copied missing BMAD scaffold files" : "No changes required",
|
|
2086
|
+
changedFiles,
|
|
2087
|
+
details: []
|
|
2088
|
+
};
|
|
2089
|
+
}
|
|
2090
|
+
},
|
|
2091
|
+
{
|
|
2092
|
+
id: "hermes.pm-scaffold",
|
|
2093
|
+
title: "Hermes PM scaffold parity",
|
|
2094
|
+
audit: (ctx) => {
|
|
2095
|
+
const roles = discoverRoles(ctx.repoRoot);
|
|
2096
|
+
const role = roles.find((item) => item.role === "pm");
|
|
2097
|
+
if (!role) {
|
|
2098
|
+
return { id: "hermes.pm-scaffold", title: "Hermes PM scaffold parity", status: "skip", summary: "No pm role present", details: [], fixable: false };
|
|
2099
|
+
}
|
|
2100
|
+
const details = [];
|
|
2101
|
+
for (const rel of ["role.yaml", "SOUL.md", "hermes", ".gitignore", ".scripts/70-systemd.sh", ".runtime-scaffold/README.md", "runtime/memories/MEMORY.md", "runtime/bloodbank-consumer.py"]) {
|
|
2102
|
+
if (!existsSync5(join6(role.roleDir, rel))) details.push(`missing ${relative(ctx.repoRoot, join6(role.roleDir, rel))}`);
|
|
2103
|
+
}
|
|
2104
|
+
const gitmodules = safeReadText(join6(ctx.repoRoot, ".gitmodules")) ?? "";
|
|
2105
|
+
if (!gitmodules.includes(`agents/hermes/${role.role}/runtime`)) details.push(".gitmodules missing pm runtime submodule entry");
|
|
2106
|
+
if (!profileMetaInheritsDefault(join6(role.roleDir, "runtime", "profile.yaml"))) {
|
|
2107
|
+
details.push("runtime/profile.yaml missing inherited default config metadata");
|
|
2108
|
+
}
|
|
2109
|
+
const registry = safeReadText(registryPath(ctx.homeDir));
|
|
2110
|
+
if (!registry?.includes(`${role.agentId}:`)) details.push(`fleet registry missing ${role.agentId}`);
|
|
2111
|
+
return {
|
|
2112
|
+
id: "hermes.pm-scaffold",
|
|
2113
|
+
title: "Hermes PM scaffold parity",
|
|
2114
|
+
status: details.length === 0 ? "pass" : "fail",
|
|
2115
|
+
summary: details.length === 0 ? "PM scaffold parity verified" : `${details.length} PM scaffold issue(s) detected`,
|
|
2116
|
+
details,
|
|
2117
|
+
fixable: true
|
|
2118
|
+
};
|
|
2119
|
+
},
|
|
2120
|
+
migrate: (ctx, finding) => {
|
|
2121
|
+
const role = discoverRoles(ctx.repoRoot).find((item) => item.role === "pm");
|
|
2122
|
+
const changedFiles = [];
|
|
2123
|
+
const details = [];
|
|
2124
|
+
if (!role) {
|
|
2125
|
+
return { id: finding.id, title: finding.title, status: "blocked", summary: "No pm role present", changedFiles, details: [] };
|
|
2126
|
+
}
|
|
2127
|
+
const templateRoleDir = join6(ctx.pjanglerRoot, "templates", "hermes-agent", "template");
|
|
2128
|
+
writeIfDifferent(join6(role.roleDir, "SOUL.md"), renderSoul(role), ctx.dryRun, changedFiles);
|
|
2129
|
+
writeIfDifferent(join6(role.roleDir, "hermes"), renderHermesWrapper(role), ctx.dryRun, changedFiles, 493);
|
|
2130
|
+
writeIfDifferent(join6(role.roleDir, ".gitignore"), readText(join6(templateRoleDir, ".gitignore.jinja")).replace(/\{\{ role \}\}/g, role.role), ctx.dryRun, changedFiles);
|
|
2131
|
+
copyMissingRecursive(join6(templateRoleDir, ".runtime-scaffold"), join6(role.roleDir, ".runtime-scaffold"), changedFiles, ctx.dryRun);
|
|
2132
|
+
copyMissingRecursive(join6(templateRoleDir, ".runtime-scaffold"), join6(role.roleDir, "runtime"), changedFiles, ctx.dryRun);
|
|
2133
|
+
copyMissingRecursive(join6(templateRoleDir, ".scripts"), join6(role.roleDir, ".scripts"), changedFiles, ctx.dryRun, (source) => source.endsWith("continuous-ticket-sentinel.prompt.md.jinja"));
|
|
2134
|
+
upsertSubmodule(ctx.repoRoot, role, changedFiles, ctx.dryRun);
|
|
2135
|
+
const profileMetaUpdated = upsertInheritedProfileMeta(join6(role.roleDir, "runtime", "profile.yaml"), changedFiles, ctx.dryRun);
|
|
2136
|
+
if (profileMetaUpdated) details.push(`updated ${profileMetaUpdated}`);
|
|
2137
|
+
const registryUpdated = upsertRegistryEntry(role, ctx.homeDir, changedFiles, ctx.dryRun);
|
|
2138
|
+
if (registryUpdated) details.push(`updated ${registryUpdated}`);
|
|
2139
|
+
return {
|
|
2140
|
+
id: finding.id,
|
|
2141
|
+
title: finding.title,
|
|
2142
|
+
status: changedFiles.length ? "applied" : "noop",
|
|
2143
|
+
summary: changedFiles.length ? "PM scaffold normalized" : "No changes required",
|
|
2144
|
+
changedFiles,
|
|
2145
|
+
details
|
|
2146
|
+
};
|
|
2147
|
+
}
|
|
2148
|
+
},
|
|
2149
|
+
{
|
|
2150
|
+
id: "hermes.scrum-master-scaffold",
|
|
2151
|
+
title: "Hermes scrum-master scaffold parity",
|
|
2152
|
+
audit: (ctx) => {
|
|
2153
|
+
const roles = discoverRoles(ctx.repoRoot);
|
|
2154
|
+
const role = roles.find((item) => item.role === "scrum-master");
|
|
2155
|
+
if (!role) {
|
|
2156
|
+
return { id: "hermes.scrum-master-scaffold", title: "Hermes scrum-master scaffold parity", status: "skip", summary: "No scrum-master role present", details: [], fixable: false };
|
|
2157
|
+
}
|
|
2158
|
+
const details = [];
|
|
2159
|
+
for (const rel of ["role.yaml", "SOUL.md", "hermes", ".gitignore", ".scripts/75-scrum-master.sh", ".scripts/scrum-master/continuous-ticket-sentinel.sh", "runtime/memories/MEMORY.md", "runtime/bloodbank-consumer.py"]) {
|
|
2160
|
+
if (!existsSync5(join6(role.roleDir, rel))) details.push(`missing ${relative(ctx.repoRoot, join6(role.roleDir, rel))}`);
|
|
2161
|
+
}
|
|
2162
|
+
const gitmodules = safeReadText(join6(ctx.repoRoot, ".gitmodules")) ?? "";
|
|
2163
|
+
if (!gitmodules.includes(`agents/hermes/${role.role}/runtime`)) details.push(".gitmodules missing scrum-master runtime submodule entry");
|
|
2164
|
+
if (!profileMetaInheritsDefault(join6(role.roleDir, "runtime", "profile.yaml"))) {
|
|
2165
|
+
details.push("runtime/profile.yaml missing inherited default config metadata");
|
|
2166
|
+
}
|
|
2167
|
+
const registry = safeReadText(registryPath(ctx.homeDir));
|
|
2168
|
+
if (!registry?.includes(`${role.agentId}:`)) details.push(`fleet registry missing ${role.agentId}`);
|
|
2169
|
+
return {
|
|
2170
|
+
id: "hermes.scrum-master-scaffold",
|
|
2171
|
+
title: "Hermes scrum-master scaffold parity",
|
|
2172
|
+
status: details.length === 0 ? "pass" : "fail",
|
|
2173
|
+
summary: details.length === 0 ? "scrum-master scaffold parity verified" : `${details.length} scrum-master scaffold issue(s) detected`,
|
|
2174
|
+
details,
|
|
2175
|
+
fixable: true
|
|
2176
|
+
};
|
|
2177
|
+
},
|
|
2178
|
+
migrate: (ctx, finding) => {
|
|
2179
|
+
const role = discoverRoles(ctx.repoRoot).find((item) => item.role === "scrum-master");
|
|
2180
|
+
const changedFiles = [];
|
|
2181
|
+
const details = [];
|
|
2182
|
+
if (!role) {
|
|
2183
|
+
return { id: finding.id, title: finding.title, status: "blocked", summary: "No scrum-master role present", changedFiles, details: [] };
|
|
2184
|
+
}
|
|
2185
|
+
const templateRoleDir = join6(ctx.pjanglerRoot, "templates", "hermes-agent", "template");
|
|
2186
|
+
writeIfDifferent(join6(role.roleDir, "SOUL.md"), renderSoul(role), ctx.dryRun, changedFiles);
|
|
2187
|
+
writeIfDifferent(join6(role.roleDir, "hermes"), renderHermesWrapper(role), ctx.dryRun, changedFiles, 493);
|
|
2188
|
+
writeIfDifferent(join6(role.roleDir, ".gitignore"), readText(join6(templateRoleDir, ".gitignore.jinja")).replace(/\{\{ role \}\}/g, role.role), ctx.dryRun, changedFiles);
|
|
2189
|
+
copyMissingRecursive(join6(templateRoleDir, ".runtime-scaffold"), join6(role.roleDir, ".runtime-scaffold"), changedFiles, ctx.dryRun);
|
|
2190
|
+
copyMissingRecursive(join6(templateRoleDir, ".runtime-scaffold"), join6(role.roleDir, "runtime"), changedFiles, ctx.dryRun);
|
|
2191
|
+
copyMissingRecursive(join6(templateRoleDir, ".scripts"), join6(role.roleDir, ".scripts"), changedFiles, ctx.dryRun, (source) => source.endsWith("continuous-ticket-sentinel.prompt.md.jinja"));
|
|
2192
|
+
const promptSource = join6(templateRoleDir, ".scripts", "scrum-master", "continuous-ticket-sentinel.prompt.md.jinja");
|
|
2193
|
+
const promptTarget = join6(role.roleDir, ".scripts", "scrum-master", "continuous-ticket-sentinel.prompt.md");
|
|
2194
|
+
if (!existsSync5(promptTarget)) {
|
|
2195
|
+
const prompt = readText(promptSource).replace(/\{\{ agent_id \}\}/g, role.agentId).replace(/\{\{ role \}\}/g, role.role).replace(/\{\{ target_repo \}\}/g, role.repo);
|
|
2196
|
+
writeIfDifferent(promptTarget, prompt, ctx.dryRun, changedFiles);
|
|
2197
|
+
}
|
|
2198
|
+
upsertSubmodule(ctx.repoRoot, role, changedFiles, ctx.dryRun);
|
|
2199
|
+
const profileMetaUpdated = upsertInheritedProfileMeta(join6(role.roleDir, "runtime", "profile.yaml"), changedFiles, ctx.dryRun);
|
|
2200
|
+
if (profileMetaUpdated) details.push(`updated ${profileMetaUpdated}`);
|
|
2201
|
+
const registryUpdated = upsertRegistryEntry(role, ctx.homeDir, changedFiles, ctx.dryRun);
|
|
2202
|
+
if (registryUpdated) details.push(`updated ${registryUpdated}`);
|
|
2203
|
+
return {
|
|
2204
|
+
id: finding.id,
|
|
2205
|
+
title: finding.title,
|
|
2206
|
+
status: changedFiles.length ? "applied" : "noop",
|
|
2207
|
+
summary: changedFiles.length ? "scrum-master scaffold normalized" : "No changes required",
|
|
2208
|
+
changedFiles,
|
|
2209
|
+
details
|
|
2210
|
+
};
|
|
2211
|
+
}
|
|
2212
|
+
},
|
|
2213
|
+
{
|
|
2214
|
+
id: "systemd.sentinel",
|
|
2215
|
+
title: "Hermes systemd/sentinel units enabled + active",
|
|
2216
|
+
audit: (ctx) => {
|
|
2217
|
+
const roles = discoverRoles(ctx.repoRoot);
|
|
2218
|
+
if (!roles.length) {
|
|
2219
|
+
return { id: "systemd.sentinel", title: "Hermes systemd/sentinel units enabled + active", status: "skip", summary: "No Hermes roles present", details: [], fixable: false };
|
|
2220
|
+
}
|
|
2221
|
+
const probe = systemctlUser(["is-system-running"]);
|
|
2222
|
+
if (!probe.ok && !/running|degraded|starting|maintenance/.test(`${probe.stdout} ${probe.stderr}`)) {
|
|
2223
|
+
return { id: "systemd.sentinel", title: "Hermes systemd/sentinel units enabled + active", status: "warn", summary: "systemd --user unavailable; unit state not auditable here", details: [], fixable: false };
|
|
2224
|
+
}
|
|
2225
|
+
const details = [];
|
|
2226
|
+
for (const role of roles) {
|
|
2227
|
+
for (const unit of [`hermes-${role.agentId}-gateway.service`, `hermes-${role.agentId}-consumer.service`, `hermes-${role.agentId}-checkpoint.timer`]) {
|
|
2228
|
+
const state = checkUnit(unit);
|
|
2229
|
+
if (!state.enabled || !state.active) details.push(`${unit} should be enabled+active`);
|
|
2230
|
+
}
|
|
2231
|
+
if (role.role === "scrum-master") {
|
|
2232
|
+
const state = checkUnit(`hermes-${role.agentId}-continuous-ticket-sentinel.timer`);
|
|
2233
|
+
if (!state.enabled || !state.active) details.push(`hermes-${role.agentId}-continuous-ticket-sentinel.timer should be enabled+active`);
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
return {
|
|
2237
|
+
id: "systemd.sentinel",
|
|
2238
|
+
title: "Hermes systemd/sentinel units enabled + active",
|
|
2239
|
+
status: details.length === 0 ? "pass" : "fail",
|
|
2240
|
+
summary: details.length === 0 ? "Hermes user units are enabled and active" : `${details.length} systemd parity issue(s) detected`,
|
|
2241
|
+
details,
|
|
2242
|
+
fixable: true
|
|
2243
|
+
};
|
|
2244
|
+
},
|
|
2245
|
+
migrate: (ctx, finding) => {
|
|
2246
|
+
const roles = discoverRoles(ctx.repoRoot);
|
|
2247
|
+
const changedFiles = [];
|
|
2248
|
+
const details = [];
|
|
2249
|
+
if (!roles.length) {
|
|
2250
|
+
return { id: finding.id, title: finding.title, status: "blocked", summary: "No Hermes roles present", changedFiles, details };
|
|
2251
|
+
}
|
|
2252
|
+
const probe = systemctlUser(["is-system-running"]);
|
|
2253
|
+
if (!probe.ok && !/running|degraded|starting|maintenance/.test(`${probe.stdout} ${probe.stderr}`)) {
|
|
2254
|
+
return { id: finding.id, title: finding.title, status: "blocked", summary: "systemd --user unavailable on this host", changedFiles, details };
|
|
2255
|
+
}
|
|
2256
|
+
for (const role of roles) {
|
|
2257
|
+
const sysDir = join6(ctx.homeDir, ".config", "systemd", "user");
|
|
2258
|
+
const units = [`hermes-${role.agentId}-gateway.service`, `hermes-${role.agentId}-consumer.service`, `hermes-${role.agentId}-checkpoint.timer`];
|
|
2259
|
+
if (role.role === "scrum-master") units.push(`hermes-${role.agentId}-continuous-ticket-sentinel.timer`);
|
|
2260
|
+
const allUnitsPresent = units.every((unit) => existsSync5(join6(sysDir, unit)));
|
|
2261
|
+
if (allUnitsPresent) {
|
|
2262
|
+
if (ctx.dryRun) {
|
|
2263
|
+
details.push(`would run: systemctl --user enable --now ${units.join(" ")}`);
|
|
2264
|
+
} else {
|
|
2265
|
+
systemctlUser(["daemon-reload"]);
|
|
2266
|
+
for (const unit of units) {
|
|
2267
|
+
systemctlUser(["enable", "--now", unit]);
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
continue;
|
|
2271
|
+
}
|
|
2272
|
+
for (const script of [join6(role.roleDir, ".scripts", "70-systemd.sh"), role.role === "scrum-master" ? join6(role.roleDir, ".scripts", "75-scrum-master.sh") : ""]) {
|
|
2273
|
+
if (!script || !existsSync5(script)) continue;
|
|
2274
|
+
if (ctx.dryRun) {
|
|
2275
|
+
details.push(`would run: bash ${script}`);
|
|
2276
|
+
} else {
|
|
2277
|
+
const result = spawnSync4("bash", [script], { cwd: role.roleDir, encoding: "utf8" });
|
|
2278
|
+
if (result.status !== 0) details.push(`script failed: ${script}: ${result.stderr.trim() || result.stdout.trim()}`);
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
return {
|
|
2283
|
+
id: finding.id,
|
|
2284
|
+
title: finding.title,
|
|
2285
|
+
status: details.some((detail) => detail.includes("failed:")) ? "blocked" : details.length ? ctx.dryRun ? "skipped" : "applied" : "noop",
|
|
2286
|
+
summary: details.length ? ctx.dryRun ? "Planned systemd remediation commands" : "Attempted systemd remediation" : "No changes required",
|
|
2287
|
+
changedFiles,
|
|
2288
|
+
details
|
|
2289
|
+
};
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
];
|
|
2293
|
+
function writeIfDifferent(path, content, dryRun, changedFiles, mode) {
|
|
2294
|
+
const normalized = content.endsWith("\n") ? content : `${content}
|
|
2295
|
+
`;
|
|
2296
|
+
if (safeReadText(path) === normalized) return;
|
|
2297
|
+
changedFiles.push(path);
|
|
2298
|
+
if (!dryRun) {
|
|
2299
|
+
writeText(path, normalized);
|
|
2300
|
+
if (mode) chmodSync(path, mode);
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
function runAudit(repoArg) {
|
|
2304
|
+
const pjanglerRoot = resolvePjanglerRoot();
|
|
2305
|
+
const ctx = {
|
|
2306
|
+
repoRoot: resolve(repoArg ?? process.cwd()),
|
|
2307
|
+
dryRun: true,
|
|
2308
|
+
pjanglerRoot,
|
|
2309
|
+
homeDir: homedir3()
|
|
2310
|
+
};
|
|
2311
|
+
const rules = RULES.map((rule) => rule.audit(ctx));
|
|
2312
|
+
return {
|
|
2313
|
+
repo: ctx.repoRoot,
|
|
2314
|
+
ok: rules.every((rule) => rule.status === "pass" || rule.status === "skip"),
|
|
2315
|
+
auditedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2316
|
+
rules
|
|
2317
|
+
};
|
|
2318
|
+
}
|
|
2319
|
+
function runMigration(selector, repoArg, dryRun, all) {
|
|
2320
|
+
const pjanglerRoot = resolvePjanglerRoot();
|
|
2321
|
+
const ctx = {
|
|
2322
|
+
repoRoot: resolve(repoArg ?? process.cwd()),
|
|
2323
|
+
dryRun,
|
|
2324
|
+
pjanglerRoot,
|
|
2325
|
+
homeDir: homedir3()
|
|
2326
|
+
};
|
|
2327
|
+
const selected = all ? RULES : RULES.filter((rule) => rule.id === selector);
|
|
2328
|
+
if (!selected.length) {
|
|
2329
|
+
throw new Error(`Unknown parity rule: ${selector}`);
|
|
2330
|
+
}
|
|
2331
|
+
const results = selected.map((rule) => rule.migrate(ctx, rule.audit(ctx)));
|
|
2332
|
+
const changedFiles = Array.from(new Set(results.flatMap((result) => result.changedFiles))).sort();
|
|
2333
|
+
return {
|
|
2334
|
+
repo: ctx.repoRoot,
|
|
2335
|
+
dryRun,
|
|
2336
|
+
ok: results.every((result) => result.status !== "blocked"),
|
|
2337
|
+
selectedRules: selected.map((rule) => rule.id),
|
|
2338
|
+
results,
|
|
2339
|
+
changedFiles
|
|
2340
|
+
};
|
|
2341
|
+
}
|
|
2342
|
+
function formatAuditReport(report) {
|
|
2343
|
+
const lines = [`repo: ${report.repo}`, `ok: ${report.ok}`, `audited_at: ${report.auditedAt}`, "rules:"];
|
|
2344
|
+
for (const rule of report.rules) {
|
|
2345
|
+
lines.push(`- ${rule.id} [${rule.status}] ${rule.summary}`);
|
|
2346
|
+
for (const detail of rule.details) lines.push(` - ${detail}`);
|
|
2347
|
+
}
|
|
2348
|
+
return `${lines.join("\n")}
|
|
2349
|
+
`;
|
|
2350
|
+
}
|
|
2351
|
+
function formatMigrationReport(report) {
|
|
2352
|
+
const lines = [`repo: ${report.repo}`, `dry_run: ${report.dryRun}`, `ok: ${report.ok}`, `selected_rules: ${report.selectedRules.join(", ")}`, "results:"];
|
|
2353
|
+
for (const result of report.results) {
|
|
2354
|
+
lines.push(`- ${result.id} [${result.status}] ${result.summary}`);
|
|
2355
|
+
for (const detail of result.details) lines.push(` - ${detail}`);
|
|
2356
|
+
for (const file of result.changedFiles) lines.push(` - changed: ${file}`);
|
|
2357
|
+
}
|
|
2358
|
+
if (report.changedFiles.length) {
|
|
2359
|
+
lines.push("changed_files:");
|
|
2360
|
+
for (const file of report.changedFiles) lines.push(`- ${file}`);
|
|
2361
|
+
}
|
|
2362
|
+
return `${lines.join("\n")}
|
|
2363
|
+
`;
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
// src/index.ts
|
|
2367
|
+
var program = new Command3();
|
|
2368
|
+
program.name("pjangler").description("Project subsystem bootstrapper CLI").version("1.0.0");
|
|
2369
|
+
program.command("init").argument("<subsystem>", "Subsystem to initialize").description("Initialize a project subsystem").option("--dry-run", "Preview changes without writing files").option("-f, --force", "Overwrite existing files").action(async (subsystem, options) => {
|
|
2370
|
+
const context = {
|
|
2371
|
+
targetDir: process.cwd(),
|
|
2372
|
+
force: options.force || false,
|
|
2373
|
+
dryRun: options.dryRun || false
|
|
2374
|
+
};
|
|
2375
|
+
try {
|
|
2376
|
+
const recipe = createRecipe(subsystem, context);
|
|
2377
|
+
if (!recipe) {
|
|
2378
|
+
console.error(`\u274C Unknown subsystem: ${subsystem}`);
|
|
2379
|
+
console.log(`Available subsystems: ${getRecipeNames().join(", ")}`);
|
|
2380
|
+
process.exit(1);
|
|
2381
|
+
}
|
|
2382
|
+
await recipe.execute();
|
|
2383
|
+
} catch (error) {
|
|
2384
|
+
console.error(`\u274C Error initializing ${subsystem}:`, error);
|
|
2385
|
+
process.exit(1);
|
|
2386
|
+
}
|
|
2387
|
+
});
|
|
2388
|
+
program.command("list").description("List available subsystems").action(() => {
|
|
2389
|
+
console.log("Available subsystems:");
|
|
2390
|
+
console.log("");
|
|
2391
|
+
for (const [name, info] of Object.entries(RECIPE_REGISTRY)) {
|
|
2392
|
+
console.log(` ${name.padEnd(10)} - ${info.description}`);
|
|
2393
|
+
}
|
|
2394
|
+
console.log("");
|
|
2395
|
+
console.log("Usage examples:");
|
|
2396
|
+
console.log(" pjangler init mise");
|
|
2397
|
+
console.log(" pjangler init docker");
|
|
2398
|
+
console.log(" pjangler init node");
|
|
2399
|
+
});
|
|
2400
|
+
var recipeCmd = program.command("recipe").description("Manage pjangler recipes");
|
|
2401
|
+
recipeCmd.command("list").description("List all available recipes").action(() => {
|
|
2402
|
+
console.log("\u{1F4E6} Available Recipes:");
|
|
2403
|
+
console.log("");
|
|
2404
|
+
for (const [name, info] of Object.entries(RECIPE_REGISTRY)) {
|
|
2405
|
+
console.log(` ${name}`);
|
|
2406
|
+
console.log(` ${info.description}`);
|
|
2407
|
+
console.log(` Commands: ${info.commands.join(", ")}`);
|
|
2408
|
+
console.log("");
|
|
2409
|
+
}
|
|
2410
|
+
console.log("Usage:");
|
|
2411
|
+
console.log(" pjangler recipe run <name>");
|
|
2412
|
+
console.log(" pjangler recipe describe <name>");
|
|
2413
|
+
});
|
|
2414
|
+
recipeCmd.command("describe").argument("<name>", "Recipe name").description("Show detailed information about a recipe").action((name) => {
|
|
2415
|
+
const info = getRecipeInfo(name);
|
|
2416
|
+
if (!info) {
|
|
2417
|
+
console.error(`\u274C Recipe not found: ${name}`);
|
|
2418
|
+
console.log(`Available recipes: ${getRecipeNames().join(", ")}`);
|
|
2419
|
+
process.exit(1);
|
|
2420
|
+
}
|
|
2421
|
+
console.log(`\u{1F4E6} Recipe: ${info.name}`);
|
|
2422
|
+
console.log("");
|
|
2423
|
+
console.log(`Description: ${info.description}`);
|
|
2424
|
+
console.log("");
|
|
2425
|
+
console.log("Commands:");
|
|
2426
|
+
for (const cmd of info.commands) {
|
|
2427
|
+
console.log(` - ${cmd}`);
|
|
2428
|
+
}
|
|
2429
|
+
console.log("");
|
|
2430
|
+
console.log("Usage:");
|
|
2431
|
+
console.log(` pjangler recipe run ${name}`);
|
|
2432
|
+
console.log(` pjangler init ${name}`);
|
|
2433
|
+
});
|
|
2434
|
+
recipeCmd.command("run").argument("<name>", "Recipe name").description("Execute a specific recipe").option("--dry-run", "Preview changes without writing files").option("-f, --force", "Overwrite existing files").action(async (name, options) => {
|
|
2435
|
+
const context = {
|
|
2436
|
+
targetDir: process.cwd(),
|
|
2437
|
+
force: options.force || false,
|
|
2438
|
+
dryRun: options.dryRun || false
|
|
2439
|
+
};
|
|
2440
|
+
try {
|
|
2441
|
+
const recipe = createRecipe(name, context);
|
|
2442
|
+
if (!recipe) {
|
|
2443
|
+
console.error(`\u274C Recipe not found: ${name}`);
|
|
2444
|
+
console.log(`Available recipes: ${getRecipeNames().join(", ")}`);
|
|
2445
|
+
process.exit(1);
|
|
2446
|
+
}
|
|
2447
|
+
const dryRunPrefix = context.dryRun ? "[DRY RUN] " : "";
|
|
2448
|
+
console.log(`${dryRunPrefix}\u{1F680} Running recipe: ${name}`);
|
|
2449
|
+
console.log("");
|
|
2450
|
+
await recipe.execute();
|
|
2451
|
+
} catch (error) {
|
|
2452
|
+
console.error(`\u274C Error running recipe ${name}:`, error);
|
|
2453
|
+
process.exit(1);
|
|
2454
|
+
}
|
|
2455
|
+
});
|
|
2456
|
+
var commandCmd = program.command("command").alias("cmd").description("Manage pjangler commands");
|
|
2457
|
+
commandCmd.command("list").description("List all available commands").option("-g, --group", "Group commands by category").action((options) => {
|
|
2458
|
+
if (options.group) {
|
|
2459
|
+
console.log("\u2699\uFE0F Available Commands (Grouped):");
|
|
2460
|
+
console.log("");
|
|
2461
|
+
const grouped = getCommandsByGroup();
|
|
2462
|
+
for (const [group, commands] of Object.entries(grouped)) {
|
|
2463
|
+
console.log(` ${group.toUpperCase()}:`);
|
|
2464
|
+
for (const cmd of commands) {
|
|
2465
|
+
console.log(` ${cmd.name.padEnd(30)} - ${cmd.description}`);
|
|
2466
|
+
}
|
|
2467
|
+
console.log("");
|
|
2468
|
+
}
|
|
2469
|
+
} else {
|
|
2470
|
+
console.log("\u2699\uFE0F Available Commands:");
|
|
2471
|
+
console.log("");
|
|
2472
|
+
for (const [name, info] of Object.entries(COMMAND_REGISTRY)) {
|
|
2473
|
+
console.log(` ${name.padEnd(30)} - ${info.description}`);
|
|
2474
|
+
}
|
|
2475
|
+
console.log("");
|
|
2476
|
+
}
|
|
2477
|
+
console.log("Usage:");
|
|
2478
|
+
console.log(" pj command list --group # Group by category");
|
|
2479
|
+
console.log(" pj command describe <name> # Show command details");
|
|
2480
|
+
});
|
|
2481
|
+
commandCmd.command("describe").argument("<name>", "Command name").description("Show detailed information about a command").action((name) => {
|
|
2482
|
+
const info = getCommandInfo(name);
|
|
2483
|
+
if (!info) {
|
|
2484
|
+
console.error(`\u274C Command not found: ${name}`);
|
|
2485
|
+
console.log(`Available commands: ${getCommandNames().join(", ")}`);
|
|
2486
|
+
process.exit(1);
|
|
2487
|
+
}
|
|
2488
|
+
console.log(`\u2699\uFE0F Command: ${info.name}`);
|
|
2489
|
+
console.log("");
|
|
2490
|
+
console.log(`Description: ${info.description}`);
|
|
2491
|
+
console.log(`Group: ${info.group}`);
|
|
2492
|
+
console.log("");
|
|
2493
|
+
console.log("This command is used in recipes:");
|
|
2494
|
+
for (const [recipeName, recipeInfo] of Object.entries(RECIPE_REGISTRY)) {
|
|
2495
|
+
if (recipeInfo.commands.includes(name)) {
|
|
2496
|
+
console.log(` - ${recipeName}`);
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
console.log("");
|
|
2500
|
+
console.log("Usage:");
|
|
2501
|
+
console.log(` Part of recipe execution (not run directly)`);
|
|
2502
|
+
});
|
|
2503
|
+
commandCmd.command("create").argument("<name>", "Command name").argument("<prompt>", "Description of what the command should do").description("Create a new command from template (placeholder for STORY-005)").option("-t, --template <type>", "Template type (toml, json, yaml, dockerfile)").option("-m, --model <model>", "LLM model to use (OpenRouter)").action((name, prompt, options) => {
|
|
2504
|
+
console.log("\u{1F6A7} Command generation coming in STORY-005!");
|
|
2505
|
+
console.log("");
|
|
2506
|
+
console.log("Planned features:");
|
|
2507
|
+
console.log(` - Generate ${name} from prompt: "${prompt}"`);
|
|
2508
|
+
if (options.template) {
|
|
2509
|
+
console.log(` - Template type: ${options.template}`);
|
|
2510
|
+
}
|
|
2511
|
+
if (options.model) {
|
|
2512
|
+
console.log(` - LLM model: ${options.model}`);
|
|
2513
|
+
}
|
|
2514
|
+
console.log("");
|
|
2515
|
+
console.log("This feature will be implemented in the Template Generation System story.");
|
|
2516
|
+
console.log("For now, manually create commands in src/commands/");
|
|
2517
|
+
});
|
|
2518
|
+
program.command("audit").argument("[repo]", "Path to repo to audit (default: cwd)").description("Deterministic parity audit against 33god project standard").option("--json", "Output machine-parseable JSON").action((repo, options) => {
|
|
2519
|
+
try {
|
|
2520
|
+
const report = runAudit(repo);
|
|
2521
|
+
if (options.json) {
|
|
2522
|
+
console.log(JSON.stringify(report, null, 2));
|
|
2523
|
+
} else {
|
|
2524
|
+
console.log(formatAuditReport(report));
|
|
2525
|
+
}
|
|
2526
|
+
process.exit(report.ok ? 0 : 1);
|
|
2527
|
+
} catch (err) {
|
|
2528
|
+
console.error("\u274C audit failed:", err);
|
|
2529
|
+
process.exit(1);
|
|
2530
|
+
}
|
|
2531
|
+
});
|
|
2532
|
+
program.command("migrate").argument("[rule-id]", "Rule ID to migrate (omit with --all to apply all)").argument("[repo]", "Path to repo (default: cwd)").description("Idempotent migration recipe for a parity rule (or --all)").option("--all", "Apply every migration recipe in order").option("--dry-run", "Preview changes without writing files").option("--json", "Output machine-parseable JSON").action((ruleId, repo, options) => {
|
|
2533
|
+
try {
|
|
2534
|
+
const all = options.all ?? false;
|
|
2535
|
+
if (!all && !ruleId) {
|
|
2536
|
+
console.error("\u274C Provide a rule-id or use --all");
|
|
2537
|
+
process.exit(1);
|
|
2538
|
+
}
|
|
2539
|
+
let actualRuleId = all ? void 0 : ruleId;
|
|
2540
|
+
let actualRepo = repo;
|
|
2541
|
+
if (all && ruleId && !actualRepo) {
|
|
2542
|
+
actualRepo = ruleId;
|
|
2543
|
+
}
|
|
2544
|
+
const report = runMigration(actualRuleId, actualRepo, options.dryRun ?? false, all);
|
|
2545
|
+
if (options.json) {
|
|
2546
|
+
console.log(JSON.stringify(report, null, 2));
|
|
2547
|
+
} else {
|
|
2548
|
+
console.log(formatMigrationReport(report));
|
|
2549
|
+
}
|
|
2550
|
+
process.exit(report.ok ? 0 : 1);
|
|
2551
|
+
} catch (err) {
|
|
2552
|
+
console.error("\u274C migrate failed:", err);
|
|
2553
|
+
process.exit(1);
|
|
2554
|
+
}
|
|
2555
|
+
});
|
|
2556
|
+
program.command("hermes-agent").alias("hermes").description("Provision a Hermes agent role into the current repo (TUI; --yes for non-interactive)").option("-y, --yes", "Non-interactive: accept all defaults (skips Telegram + email)").option("--target-repo <name>", "Target repo name (default: basename of cwd)").option("--role <role>", "Agent role (pm | dev | review | ops | qa | ci | ...)").option("--purpose <text>", "One-line agent purpose").option(`--tone <tone>`, `Personality tone (${SOUL_TONES.join(" | ")})`).option("--model-provider <name>", 'Inference provider override ("" = inherit shared default profile)').option("--model-name <name>", 'Model name override ("" = inherit shared default profile)').option("--skip-telegram", "Skip BotFather token capture step").option("--skip-email", "Skip Cloudflare Email Routing step").option("--skip-runtime-repo", "Skip creating the per-agent runtime GH repo").option("--skip-plane", "Skip creating the Plane project").option("--skip-bloodbank", "Skip installing the Bloodbank NATS consumer").option("--skip-systemd", "Skip installing systemd --user units").option("--local", "Local-only: skip runtime repo, Plane, Bloodbank, and systemd (safe for laptops/macOS/non-technical operators)").option("--force-config", "Regenerate ~/.config/hermes-agent-template/config.toml even if it exists").option("--dry-run", "Preview what would run; don't execute copier").option("-f, --force", "Re-render even if agents/hermes/<role>/role.yaml already exists").action(async (options) => {
|
|
2557
|
+
const isDarwin = process.platform === "darwin";
|
|
2558
|
+
const local = options.local ?? false;
|
|
2559
|
+
const context = {
|
|
2560
|
+
targetDir: process.cwd(),
|
|
2561
|
+
force: options.force ?? false,
|
|
2562
|
+
dryRun: options.dryRun ?? false,
|
|
2563
|
+
yes: options.yes ?? false,
|
|
2564
|
+
local,
|
|
2565
|
+
forceConfig: options.forceConfig ?? false,
|
|
2566
|
+
targetRepo: options.targetRepo,
|
|
2567
|
+
role: options.role,
|
|
2568
|
+
agentPurpose: options.purpose,
|
|
2569
|
+
soulTone: options.tone,
|
|
2570
|
+
modelProvider: options.modelProvider,
|
|
2571
|
+
modelName: options.modelName,
|
|
2572
|
+
skipTelegram: options.skipTelegram,
|
|
2573
|
+
skipEmail: options.skipEmail,
|
|
2574
|
+
// --local (and macOS, for systemd) flip the heavy/irreversible steps off
|
|
2575
|
+
// by default so a non-technical operator can't accidentally create cloud
|
|
2576
|
+
// resources under the wrong account or hit systemd on a Mac. An explicit
|
|
2577
|
+
// --skip-* still forces the skip; passing neither + not --local keeps the
|
|
2578
|
+
// full provisioning behavior for the authoring machine.
|
|
2579
|
+
skipRuntimeRepo: options.skipRuntimeRepo ?? local,
|
|
2580
|
+
skipPlane: options.skipPlane ?? local,
|
|
2581
|
+
skipBloodbank: options.skipBloodbank ?? local,
|
|
2582
|
+
skipSystemd: options.skipSystemd ?? (local || isDarwin)
|
|
2583
|
+
};
|
|
2584
|
+
try {
|
|
2585
|
+
const recipe = createRecipe("hermes-agent", context);
|
|
2586
|
+
if (!recipe) {
|
|
2587
|
+
console.error("\u274C hermes-agent recipe not registered");
|
|
2588
|
+
process.exit(1);
|
|
2589
|
+
}
|
|
2590
|
+
await recipe.execute();
|
|
2591
|
+
} catch (err) {
|
|
2592
|
+
console.error("\u274C hermes-agent failed:", err);
|
|
2593
|
+
process.exit(1);
|
|
2594
|
+
}
|
|
2595
|
+
});
|
|
2596
|
+
var configCmd = program.command("config").description("Manage host/provisioner configuration");
|
|
2597
|
+
configCmd.command("bootstrap").description("Create ~/.config/hermes-agent-template/config.toml with host-correct defaults if missing").option("--force", "Overwrite an existing config file").option("--dry-run", "Show what would be written without writing").action(async (options) => {
|
|
2598
|
+
const ctx = {
|
|
2599
|
+
targetDir: process.cwd(),
|
|
2600
|
+
dryRun: options.dryRun ?? false,
|
|
2601
|
+
forceConfig: options.force ?? false
|
|
2602
|
+
};
|
|
2603
|
+
const result = await new EnsureTemplateConfig(ctx).invoke();
|
|
2604
|
+
if (!result.success) {
|
|
2605
|
+
if (result.message) console.error(result.message);
|
|
2606
|
+
process.exit(1);
|
|
2607
|
+
}
|
|
2608
|
+
});
|
|
2609
|
+
program.command("describe").description("Describe the current project (for AI context)").action(() => {
|
|
2610
|
+
console.log("\u{1F50D} Project Description (placeholder for future enhancement)");
|
|
2611
|
+
console.log("");
|
|
2612
|
+
console.log("This command will analyze the project and provide:");
|
|
2613
|
+
console.log(" - Detected project type");
|
|
2614
|
+
console.log(" - Installed subsystems");
|
|
2615
|
+
console.log(" - Configuration files present");
|
|
2616
|
+
console.log(" - Suggested next steps");
|
|
2617
|
+
console.log("");
|
|
2618
|
+
console.log("Coming soon!");
|
|
2619
|
+
});
|
|
2620
|
+
program.parse();
|