@ncoderz/awa 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +234 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1353 -0
- package/dist/index.js.map +1 -0
- package/package.json +75 -0
- package/templates/awa/.agent/skills/awa-architecture/SKILL.md +3 -0
- package/templates/awa/.agent/skills/awa-brainstorm/SKILL.md +3 -0
- package/templates/awa/.agent/skills/awa-code/SKILL.md +3 -0
- package/templates/awa/.agent/skills/awa-design/SKILL.md +3 -0
- package/templates/awa/.agent/skills/awa-documentation/SKILL.md +3 -0
- package/templates/awa/.agent/skills/awa-examples/SKILL.md +3 -0
- package/templates/awa/.agent/skills/awa-feature/SKILL.md +3 -0
- package/templates/awa/.agent/skills/awa-plan/SKILL.md +3 -0
- package/templates/awa/.agent/skills/awa-refactor/SKILL.md +3 -0
- package/templates/awa/.agent/skills/awa-requirements/SKILL.md +3 -0
- package/templates/awa/.agent/skills/awa-tasks/SKILL.md +3 -0
- package/templates/awa/.agent/skills/awa-upgrade/SKILL.md +3 -0
- package/templates/awa/.agent/skills/awa-validate-alignment/SKILL.md +3 -0
- package/templates/awa/.agent/skills/awa-vibe/SKILL.md +3 -0
- package/templates/awa/.agent/workflows/awa-architecture.md +3 -0
- package/templates/awa/.agent/workflows/awa-brainstorm.md +3 -0
- package/templates/awa/.agent/workflows/awa-code.md +3 -0
- package/templates/awa/.agent/workflows/awa-design.md +3 -0
- package/templates/awa/.agent/workflows/awa-documentation.md +3 -0
- package/templates/awa/.agent/workflows/awa-examples.md +3 -0
- package/templates/awa/.agent/workflows/awa-feature.md +3 -0
- package/templates/awa/.agent/workflows/awa-plan.md +3 -0
- package/templates/awa/.agent/workflows/awa-refactor.md +3 -0
- package/templates/awa/.agent/workflows/awa-requirements.md +3 -0
- package/templates/awa/.agent/workflows/awa-tasks.md +3 -0
- package/templates/awa/.agent/workflows/awa-upgrade.md +3 -0
- package/templates/awa/.agent/workflows/awa-validate-alignment.md +3 -0
- package/templates/awa/.agent/workflows/awa-vibe.md +3 -0
- package/templates/awa/.agents/skills/awa-architecture/SKILL.md +3 -0
- package/templates/awa/.agents/skills/awa-brainstorm/SKILL.md +3 -0
- package/templates/awa/.agents/skills/awa-code/SKILL.md +3 -0
- package/templates/awa/.agents/skills/awa-design/SKILL.md +3 -0
- package/templates/awa/.agents/skills/awa-documentation/SKILL.md +3 -0
- package/templates/awa/.agents/skills/awa-examples/SKILL.md +3 -0
- package/templates/awa/.agents/skills/awa-feature/SKILL.md +3 -0
- package/templates/awa/.agents/skills/awa-plan/SKILL.md +3 -0
- package/templates/awa/.agents/skills/awa-refactor/SKILL.md +3 -0
- package/templates/awa/.agents/skills/awa-requirements/SKILL.md +3 -0
- package/templates/awa/.agents/skills/awa-tasks/SKILL.md +3 -0
- package/templates/awa/.agents/skills/awa-upgrade/SKILL.md +3 -0
- package/templates/awa/.agents/skills/awa-validate-alignment/SKILL.md +3 -0
- package/templates/awa/.agents/skills/awa-vibe/SKILL.md +3 -0
- package/templates/awa/.awa/.agent/awa.core.md +1 -0
- package/templates/awa/.awa/.agent/schemas/ALIGN_REPORT.schema.md +156 -0
- package/templates/awa/.awa/.agent/schemas/API.schema.md +4 -0
- package/templates/awa/.awa/.agent/schemas/ARCHITECTURE.schema.md +176 -0
- package/templates/awa/.awa/.agent/schemas/DESIGN.schema.md +253 -0
- package/templates/awa/.awa/.agent/schemas/EXAMPLES.schema.md +51 -0
- package/templates/awa/.awa/.agent/schemas/FEAT.schema.md +61 -0
- package/templates/awa/.awa/.agent/schemas/PLAN.schema.md +8 -0
- package/templates/awa/.awa/.agent/schemas/README.schema.md +133 -0
- package/templates/awa/.awa/.agent/schemas/REQ.schema.md +125 -0
- package/templates/awa/.awa/.agent/schemas/TASK.schema.md +137 -0
- package/templates/awa/.claude/agents/awa.md +14 -0
- package/templates/awa/.claude/skills/awa-architecture/SKILL.md +3 -0
- package/templates/awa/.claude/skills/awa-brainstorm/SKILL.md +3 -0
- package/templates/awa/.claude/skills/awa-code/SKILL.md +3 -0
- package/templates/awa/.claude/skills/awa-design/SKILL.md +3 -0
- package/templates/awa/.claude/skills/awa-documentation/SKILL.md +3 -0
- package/templates/awa/.claude/skills/awa-examples/SKILL.md +3 -0
- package/templates/awa/.claude/skills/awa-feature/SKILL.md +3 -0
- package/templates/awa/.claude/skills/awa-plan/SKILL.md +3 -0
- package/templates/awa/.claude/skills/awa-refactor/SKILL.md +3 -0
- package/templates/awa/.claude/skills/awa-requirements/SKILL.md +3 -0
- package/templates/awa/.claude/skills/awa-tasks/SKILL.md +3 -0
- package/templates/awa/.claude/skills/awa-upgrade/SKILL.md +3 -0
- package/templates/awa/.claude/skills/awa-validate-alignment/SKILL.md +3 -0
- package/templates/awa/.claude/skills/awa-vibe/SKILL.md +3 -0
- package/templates/awa/.codex/prompts/awa-architecture.md +3 -0
- package/templates/awa/.codex/prompts/awa-brainstorm.md +3 -0
- package/templates/awa/.codex/prompts/awa-code.md +3 -0
- package/templates/awa/.codex/prompts/awa-design.md +3 -0
- package/templates/awa/.codex/prompts/awa-documentation.md +3 -0
- package/templates/awa/.codex/prompts/awa-examples.md +3 -0
- package/templates/awa/.codex/prompts/awa-feature.md +3 -0
- package/templates/awa/.codex/prompts/awa-plan.md +3 -0
- package/templates/awa/.codex/prompts/awa-refactor.md +3 -0
- package/templates/awa/.codex/prompts/awa-requirements.md +3 -0
- package/templates/awa/.codex/prompts/awa-tasks.md +3 -0
- package/templates/awa/.codex/prompts/awa-upgrade.md +3 -0
- package/templates/awa/.codex/prompts/awa-validate-alignment.md +3 -0
- package/templates/awa/.codex/prompts/awa-vibe.md +3 -0
- package/templates/awa/.cursor/rules/awa-agent.md +14 -0
- package/templates/awa/.cursor/rules/awa-architecture.md +8 -0
- package/templates/awa/.cursor/rules/awa-brainstorm.md +8 -0
- package/templates/awa/.cursor/rules/awa-code.md +8 -0
- package/templates/awa/.cursor/rules/awa-design.md +8 -0
- package/templates/awa/.cursor/rules/awa-documentation.md +8 -0
- package/templates/awa/.cursor/rules/awa-examples.md +8 -0
- package/templates/awa/.cursor/rules/awa-feature.md +8 -0
- package/templates/awa/.cursor/rules/awa-plan.md +8 -0
- package/templates/awa/.cursor/rules/awa-refactor.md +8 -0
- package/templates/awa/.cursor/rules/awa-requirements.md +8 -0
- package/templates/awa/.cursor/rules/awa-tasks.md +8 -0
- package/templates/awa/.cursor/rules/awa-upgrade.md +8 -0
- package/templates/awa/.cursor/rules/awa-validate-alignment.md +8 -0
- package/templates/awa/.cursor/rules/awa-vibe.md +8 -0
- package/templates/awa/.gemini/commands/awa-architecture.md +3 -0
- package/templates/awa/.gemini/commands/awa-brainstorm.md +3 -0
- package/templates/awa/.gemini/commands/awa-code.md +3 -0
- package/templates/awa/.gemini/commands/awa-design.md +3 -0
- package/templates/awa/.gemini/commands/awa-documentation.md +3 -0
- package/templates/awa/.gemini/commands/awa-examples.md +3 -0
- package/templates/awa/.gemini/commands/awa-feature.md +3 -0
- package/templates/awa/.gemini/commands/awa-plan.md +3 -0
- package/templates/awa/.gemini/commands/awa-refactor.md +3 -0
- package/templates/awa/.gemini/commands/awa-requirements.md +3 -0
- package/templates/awa/.gemini/commands/awa-tasks.md +3 -0
- package/templates/awa/.gemini/commands/awa-upgrade.md +3 -0
- package/templates/awa/.gemini/commands/awa-validate-alignment.md +3 -0
- package/templates/awa/.gemini/commands/awa-vibe.md +3 -0
- package/templates/awa/.gemini/skills/awa-architecture/SKILL.md +3 -0
- package/templates/awa/.gemini/skills/awa-brainstorm/SKILL.md +3 -0
- package/templates/awa/.gemini/skills/awa-code/SKILL.md +3 -0
- package/templates/awa/.gemini/skills/awa-design/SKILL.md +3 -0
- package/templates/awa/.gemini/skills/awa-documentation/SKILL.md +3 -0
- package/templates/awa/.gemini/skills/awa-examples/SKILL.md +3 -0
- package/templates/awa/.gemini/skills/awa-feature/SKILL.md +3 -0
- package/templates/awa/.gemini/skills/awa-plan/SKILL.md +3 -0
- package/templates/awa/.gemini/skills/awa-refactor/SKILL.md +3 -0
- package/templates/awa/.gemini/skills/awa-requirements/SKILL.md +3 -0
- package/templates/awa/.gemini/skills/awa-tasks/SKILL.md +3 -0
- package/templates/awa/.gemini/skills/awa-upgrade/SKILL.md +3 -0
- package/templates/awa/.gemini/skills/awa-validate-alignment/SKILL.md +3 -0
- package/templates/awa/.gemini/skills/awa-vibe/SKILL.md +3 -0
- package/templates/awa/.github/agents/awa.agent.md +14 -0
- package/templates/awa/.github/prompts/awa.architecture.prompt.md +8 -0
- package/templates/awa/.github/prompts/awa.brainstorm.prompt.md +8 -0
- package/templates/awa/.github/prompts/awa.code.prompt.md +8 -0
- package/templates/awa/.github/prompts/awa.design.prompt.md +8 -0
- package/templates/awa/.github/prompts/awa.documentation.prompt.md +8 -0
- package/templates/awa/.github/prompts/awa.examples.prompt.md +8 -0
- package/templates/awa/.github/prompts/awa.feature.prompt.md +8 -0
- package/templates/awa/.github/prompts/awa.plan.prompt.md +8 -0
- package/templates/awa/.github/prompts/awa.refactor.prompt.md +8 -0
- package/templates/awa/.github/prompts/awa.requirements.prompt.md +8 -0
- package/templates/awa/.github/prompts/awa.tasks.prompt.md +8 -0
- package/templates/awa/.github/prompts/awa.upgrade.prompt.md +8 -0
- package/templates/awa/.github/prompts/awa.validate-alignment.prompt.md +8 -0
- package/templates/awa/.github/prompts/awa.vibe.prompt.md +8 -0
- package/templates/awa/.github/skills/awa-architecture/SKILL.md +8 -0
- package/templates/awa/.github/skills/awa-brainstorm/SKILL.md +8 -0
- package/templates/awa/.github/skills/awa-code/SKILL.md +8 -0
- package/templates/awa/.github/skills/awa-design/SKILL.md +8 -0
- package/templates/awa/.github/skills/awa-documentation/SKILL.md +8 -0
- package/templates/awa/.github/skills/awa-examples/SKILL.md +8 -0
- package/templates/awa/.github/skills/awa-feature/SKILL.md +8 -0
- package/templates/awa/.github/skills/awa-plan/SKILL.md +8 -0
- package/templates/awa/.github/skills/awa-refactor/SKILL.md +8 -0
- package/templates/awa/.github/skills/awa-requirements/SKILL.md +8 -0
- package/templates/awa/.github/skills/awa-tasks/SKILL.md +8 -0
- package/templates/awa/.github/skills/awa-upgrade/SKILL.md +8 -0
- package/templates/awa/.github/skills/awa-validate-alignment/SKILL.md +8 -0
- package/templates/awa/.github/skills/awa-vibe/SKILL.md +8 -0
- package/templates/awa/.kilocode/rules/awa-agent.md +13 -0
- package/templates/awa/.kilocode/skills/awa-architecture/SKILL.md +3 -0
- package/templates/awa/.kilocode/skills/awa-brainstorm/SKILL.md +3 -0
- package/templates/awa/.kilocode/skills/awa-code/SKILL.md +3 -0
- package/templates/awa/.kilocode/skills/awa-design/SKILL.md +3 -0
- package/templates/awa/.kilocode/skills/awa-documentation/SKILL.md +3 -0
- package/templates/awa/.kilocode/skills/awa-examples/SKILL.md +3 -0
- package/templates/awa/.kilocode/skills/awa-feature/SKILL.md +3 -0
- package/templates/awa/.kilocode/skills/awa-plan/SKILL.md +3 -0
- package/templates/awa/.kilocode/skills/awa-refactor/SKILL.md +3 -0
- package/templates/awa/.kilocode/skills/awa-requirements/SKILL.md +3 -0
- package/templates/awa/.kilocode/skills/awa-tasks/SKILL.md +3 -0
- package/templates/awa/.kilocode/skills/awa-upgrade/SKILL.md +3 -0
- package/templates/awa/.kilocode/skills/awa-validate-alignment/SKILL.md +3 -0
- package/templates/awa/.kilocode/skills/awa-vibe/SKILL.md +3 -0
- package/templates/awa/.kilocode/workflows/awa-architecture.md +3 -0
- package/templates/awa/.kilocode/workflows/awa-brainstorm.md +3 -0
- package/templates/awa/.kilocode/workflows/awa-code.md +3 -0
- package/templates/awa/.kilocode/workflows/awa-design.md +3 -0
- package/templates/awa/.kilocode/workflows/awa-documentation.md +3 -0
- package/templates/awa/.kilocode/workflows/awa-examples.md +3 -0
- package/templates/awa/.kilocode/workflows/awa-feature.md +3 -0
- package/templates/awa/.kilocode/workflows/awa-plan.md +3 -0
- package/templates/awa/.kilocode/workflows/awa-refactor.md +3 -0
- package/templates/awa/.kilocode/workflows/awa-requirements.md +3 -0
- package/templates/awa/.kilocode/workflows/awa-tasks.md +3 -0
- package/templates/awa/.kilocode/workflows/awa-upgrade.md +3 -0
- package/templates/awa/.kilocode/workflows/awa-validate-alignment.md +3 -0
- package/templates/awa/.kilocode/workflows/awa-vibe.md +3 -0
- package/templates/awa/.opencode/agents/awa.md +14 -0
- package/templates/awa/.opencode/commands/awa-architecture.md +3 -0
- package/templates/awa/.opencode/commands/awa-brainstorm.md +3 -0
- package/templates/awa/.opencode/commands/awa-code.md +3 -0
- package/templates/awa/.opencode/commands/awa-design.md +3 -0
- package/templates/awa/.opencode/commands/awa-documentation.md +3 -0
- package/templates/awa/.opencode/commands/awa-examples.md +3 -0
- package/templates/awa/.opencode/commands/awa-feature.md +3 -0
- package/templates/awa/.opencode/commands/awa-plan.md +3 -0
- package/templates/awa/.opencode/commands/awa-refactor.md +3 -0
- package/templates/awa/.opencode/commands/awa-requirements.md +3 -0
- package/templates/awa/.opencode/commands/awa-tasks.md +3 -0
- package/templates/awa/.opencode/commands/awa-upgrade.md +3 -0
- package/templates/awa/.opencode/commands/awa-validate-alignment.md +3 -0
- package/templates/awa/.opencode/commands/awa-vibe.md +3 -0
- package/templates/awa/.opencode/skills/awa-architecture/SKILL.md +3 -0
- package/templates/awa/.opencode/skills/awa-brainstorm/SKILL.md +3 -0
- package/templates/awa/.opencode/skills/awa-code/SKILL.md +3 -0
- package/templates/awa/.opencode/skills/awa-design/SKILL.md +3 -0
- package/templates/awa/.opencode/skills/awa-documentation/SKILL.md +3 -0
- package/templates/awa/.opencode/skills/awa-examples/SKILL.md +3 -0
- package/templates/awa/.opencode/skills/awa-feature/SKILL.md +3 -0
- package/templates/awa/.opencode/skills/awa-plan/SKILL.md +3 -0
- package/templates/awa/.opencode/skills/awa-refactor/SKILL.md +3 -0
- package/templates/awa/.opencode/skills/awa-requirements/SKILL.md +3 -0
- package/templates/awa/.opencode/skills/awa-tasks/SKILL.md +3 -0
- package/templates/awa/.opencode/skills/awa-upgrade/SKILL.md +3 -0
- package/templates/awa/.opencode/skills/awa-validate-alignment/SKILL.md +3 -0
- package/templates/awa/.opencode/skills/awa-vibe/SKILL.md +3 -0
- package/templates/awa/.qwen/commands/awa-architecture.md +3 -0
- package/templates/awa/.qwen/commands/awa-brainstorm.md +3 -0
- package/templates/awa/.qwen/commands/awa-code.md +3 -0
- package/templates/awa/.qwen/commands/awa-design.md +3 -0
- package/templates/awa/.qwen/commands/awa-documentation.md +3 -0
- package/templates/awa/.qwen/commands/awa-examples.md +3 -0
- package/templates/awa/.qwen/commands/awa-feature.md +3 -0
- package/templates/awa/.qwen/commands/awa-plan.md +3 -0
- package/templates/awa/.qwen/commands/awa-refactor.md +3 -0
- package/templates/awa/.qwen/commands/awa-requirements.md +3 -0
- package/templates/awa/.qwen/commands/awa-tasks.md +3 -0
- package/templates/awa/.qwen/commands/awa-upgrade.md +3 -0
- package/templates/awa/.qwen/commands/awa-validate-alignment.md +3 -0
- package/templates/awa/.qwen/commands/awa-vibe.md +3 -0
- package/templates/awa/.qwen/skills/awa-architecture/SKILL.md +3 -0
- package/templates/awa/.qwen/skills/awa-brainstorm/SKILL.md +3 -0
- package/templates/awa/.qwen/skills/awa-code/SKILL.md +3 -0
- package/templates/awa/.qwen/skills/awa-design/SKILL.md +3 -0
- package/templates/awa/.qwen/skills/awa-documentation/SKILL.md +3 -0
- package/templates/awa/.qwen/skills/awa-examples/SKILL.md +3 -0
- package/templates/awa/.qwen/skills/awa-feature/SKILL.md +3 -0
- package/templates/awa/.qwen/skills/awa-plan/SKILL.md +3 -0
- package/templates/awa/.qwen/skills/awa-refactor/SKILL.md +3 -0
- package/templates/awa/.qwen/skills/awa-requirements/SKILL.md +3 -0
- package/templates/awa/.qwen/skills/awa-tasks/SKILL.md +3 -0
- package/templates/awa/.qwen/skills/awa-upgrade/SKILL.md +3 -0
- package/templates/awa/.qwen/skills/awa-validate-alignment/SKILL.md +3 -0
- package/templates/awa/.qwen/skills/awa-vibe/SKILL.md +3 -0
- package/templates/awa/.roo/rules/awa-agent.md +13 -0
- package/templates/awa/.roo/skills/awa-architecture/SKILL.md +3 -0
- package/templates/awa/.roo/skills/awa-brainstorm/SKILL.md +3 -0
- package/templates/awa/.roo/skills/awa-code/SKILL.md +3 -0
- package/templates/awa/.roo/skills/awa-design/SKILL.md +3 -0
- package/templates/awa/.roo/skills/awa-documentation/SKILL.md +3 -0
- package/templates/awa/.roo/skills/awa-examples/SKILL.md +3 -0
- package/templates/awa/.roo/skills/awa-feature/SKILL.md +3 -0
- package/templates/awa/.roo/skills/awa-plan/SKILL.md +3 -0
- package/templates/awa/.roo/skills/awa-refactor/SKILL.md +3 -0
- package/templates/awa/.roo/skills/awa-requirements/SKILL.md +3 -0
- package/templates/awa/.roo/skills/awa-tasks/SKILL.md +3 -0
- package/templates/awa/.roo/skills/awa-upgrade/SKILL.md +3 -0
- package/templates/awa/.roo/skills/awa-validate-alignment/SKILL.md +3 -0
- package/templates/awa/.roo/skills/awa-vibe/SKILL.md +3 -0
- package/templates/awa/.windsurf/rules/awa-agent.md +14 -0
- package/templates/awa/.windsurf/skills/awa-architecture/SKILL.md +3 -0
- package/templates/awa/.windsurf/skills/awa-brainstorm/SKILL.md +3 -0
- package/templates/awa/.windsurf/skills/awa-code/SKILL.md +3 -0
- package/templates/awa/.windsurf/skills/awa-design/SKILL.md +3 -0
- package/templates/awa/.windsurf/skills/awa-documentation/SKILL.md +3 -0
- package/templates/awa/.windsurf/skills/awa-examples/SKILL.md +3 -0
- package/templates/awa/.windsurf/skills/awa-feature/SKILL.md +3 -0
- package/templates/awa/.windsurf/skills/awa-plan/SKILL.md +3 -0
- package/templates/awa/.windsurf/skills/awa-refactor/SKILL.md +3 -0
- package/templates/awa/.windsurf/skills/awa-requirements/SKILL.md +3 -0
- package/templates/awa/.windsurf/skills/awa-tasks/SKILL.md +3 -0
- package/templates/awa/.windsurf/skills/awa-upgrade/SKILL.md +3 -0
- package/templates/awa/.windsurf/skills/awa-validate-alignment/SKILL.md +3 -0
- package/templates/awa/.windsurf/skills/awa-vibe/SKILL.md +3 -0
- package/templates/awa/AGENTS.md +9 -0
- package/templates/awa/CLAUDE.md +9 -0
- package/templates/awa/GEMINI.md +9 -0
- package/templates/awa/QWEN.md +9 -0
- package/templates/awa/_README.md +86 -0
- package/templates/awa/_delete.txt +300 -0
- package/templates/awa/_partials/_cmd.awa-architecture.md +6 -0
- package/templates/awa/_partials/_cmd.awa-brainstorm.md +6 -0
- package/templates/awa/_partials/_cmd.awa-code.md +6 -0
- package/templates/awa/_partials/_cmd.awa-design.md +6 -0
- package/templates/awa/_partials/_cmd.awa-documentation.md +6 -0
- package/templates/awa/_partials/_cmd.awa-examples.md +6 -0
- package/templates/awa/_partials/_cmd.awa-feature.md +6 -0
- package/templates/awa/_partials/_cmd.awa-plan.md +6 -0
- package/templates/awa/_partials/_cmd.awa-refactor.md +6 -0
- package/templates/awa/_partials/_cmd.awa-requirements.md +6 -0
- package/templates/awa/_partials/_cmd.awa-tasks.md +6 -0
- package/templates/awa/_partials/_cmd.awa-upgrade.md +6 -0
- package/templates/awa/_partials/_cmd.awa-validate-alignment.md +6 -0
- package/templates/awa/_partials/_cmd.awa-vibe.md +6 -0
- package/templates/awa/_partials/_skill.awa-architecture.md +6 -0
- package/templates/awa/_partials/_skill.awa-brainstorm.md +6 -0
- package/templates/awa/_partials/_skill.awa-code.md +6 -0
- package/templates/awa/_partials/_skill.awa-design.md +6 -0
- package/templates/awa/_partials/_skill.awa-documentation.md +6 -0
- package/templates/awa/_partials/_skill.awa-examples.md +6 -0
- package/templates/awa/_partials/_skill.awa-feature.md +6 -0
- package/templates/awa/_partials/_skill.awa-plan.md +6 -0
- package/templates/awa/_partials/_skill.awa-refactor.md +6 -0
- package/templates/awa/_partials/_skill.awa-requirements.md +6 -0
- package/templates/awa/_partials/_skill.awa-tasks.md +6 -0
- package/templates/awa/_partials/_skill.awa-upgrade.md +6 -0
- package/templates/awa/_partials/_skill.awa-validate-alignment.md +6 -0
- package/templates/awa/_partials/_skill.awa-vibe.md +6 -0
- package/templates/awa/_partials/awa.architecture.md +45 -0
- package/templates/awa/_partials/awa.brainstorm.md +52 -0
- package/templates/awa/_partials/awa.code.md +167 -0
- package/templates/awa/_partials/awa.core.md +100 -0
- package/templates/awa/_partials/awa.design.md +55 -0
- package/templates/awa/_partials/awa.documentation.md +86 -0
- package/templates/awa/_partials/awa.examples.md +53 -0
- package/templates/awa/_partials/awa.feature.md +50 -0
- package/templates/awa/_partials/awa.plan.md +48 -0
- package/templates/awa/_partials/awa.refactor.md +46 -0
- package/templates/awa/_partials/awa.requirements.md +50 -0
- package/templates/awa/_partials/awa.tasks.md +147 -0
- package/templates/awa/_partials/awa.upgrade.md +63 -0
- package/templates/awa/_partials/awa.validate-alignment.md +62 -0
- package/templates/awa/_partials/awa.vibe.md +64 -0
- package/templates/example/.github/agents/example.agent.md +14 -0
- package/templates/example/_README.md +43 -0
- package/templates/example/_partials/planning.md +5 -0
- package/templates/example/agent.md +72 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1353 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli/index.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/_generated/package_info.ts
|
|
7
|
+
var PACKAGE_INFO = {
|
|
8
|
+
"name": "@ncoderz/awa",
|
|
9
|
+
"version": "0.2.0",
|
|
10
|
+
"author": "Richard Sewell <richard.sewell@ncoderz.com>",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"description": "awa is an Agent Workflow for AIs. It is also a CLI tool to powerfully manage agent workflow files using templates."
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// src/commands/diff.ts
|
|
16
|
+
import { intro, outro } from "@clack/prompts";
|
|
17
|
+
|
|
18
|
+
// src/core/config.ts
|
|
19
|
+
import { parse } from "smol-toml";
|
|
20
|
+
|
|
21
|
+
// src/types/index.ts
|
|
22
|
+
var DiffError = class extends Error {
|
|
23
|
+
constructor(message) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.name = "DiffError";
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
var ConfigError = class extends Error {
|
|
29
|
+
constructor(message, code, filePath) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.code = code;
|
|
32
|
+
this.filePath = filePath;
|
|
33
|
+
this.name = "ConfigError";
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
var TemplateError = class extends Error {
|
|
37
|
+
constructor(message, code, source) {
|
|
38
|
+
super(message);
|
|
39
|
+
this.code = code;
|
|
40
|
+
this.source = source;
|
|
41
|
+
this.name = "TemplateError";
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var GenerationError = class extends Error {
|
|
45
|
+
constructor(message, code) {
|
|
46
|
+
super(message);
|
|
47
|
+
this.code = code;
|
|
48
|
+
this.name = "GenerationError";
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// src/utils/fs.ts
|
|
53
|
+
import { mkdir, readdir, readFile, rm, stat, writeFile } from "fs/promises";
|
|
54
|
+
import { homedir } from "os";
|
|
55
|
+
import { dirname, join } from "path";
|
|
56
|
+
import { fileURLToPath } from "url";
|
|
57
|
+
async function ensureDir(dirPath) {
|
|
58
|
+
await mkdir(dirPath, { recursive: true });
|
|
59
|
+
}
|
|
60
|
+
async function pathExists(path) {
|
|
61
|
+
try {
|
|
62
|
+
await stat(path);
|
|
63
|
+
return true;
|
|
64
|
+
} catch {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function readTextFile(path) {
|
|
69
|
+
return readFile(path, "utf-8");
|
|
70
|
+
}
|
|
71
|
+
async function readBinaryFile(path) {
|
|
72
|
+
return readFile(path);
|
|
73
|
+
}
|
|
74
|
+
async function writeTextFile(path, content) {
|
|
75
|
+
await ensureDir(dirname(path));
|
|
76
|
+
await writeFile(path, content, "utf-8");
|
|
77
|
+
}
|
|
78
|
+
async function* walkDirectory(dir) {
|
|
79
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
80
|
+
for (const entry of entries) {
|
|
81
|
+
const fullPath = join(dir, entry.name);
|
|
82
|
+
if (entry.isDirectory()) {
|
|
83
|
+
if (entry.name.startsWith("_")) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
yield* walkDirectory(fullPath);
|
|
87
|
+
} else if (entry.isFile()) {
|
|
88
|
+
if (entry.name.startsWith("_")) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
yield fullPath;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function getCacheDir() {
|
|
96
|
+
return join(homedir(), ".cache", "awa", "templates");
|
|
97
|
+
}
|
|
98
|
+
function getTemplateDir() {
|
|
99
|
+
const currentFile = fileURLToPath(import.meta.url);
|
|
100
|
+
const currentDir = dirname(currentFile);
|
|
101
|
+
if (currentDir.includes("/dist")) {
|
|
102
|
+
return join(dirname(currentDir), "templates");
|
|
103
|
+
}
|
|
104
|
+
return join(currentDir, "..", "..", "templates");
|
|
105
|
+
}
|
|
106
|
+
async function rmDir(dirPath) {
|
|
107
|
+
await rm(dirPath, { recursive: true, force: true });
|
|
108
|
+
}
|
|
109
|
+
async function deleteFile(filePath) {
|
|
110
|
+
await rm(filePath, { force: true });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/utils/logger.ts
|
|
114
|
+
import chalk from "chalk";
|
|
115
|
+
var Logger = class {
|
|
116
|
+
info(message) {
|
|
117
|
+
console.log(chalk.blue("\u2139"), message);
|
|
118
|
+
}
|
|
119
|
+
success(message) {
|
|
120
|
+
console.log(chalk.green("\u2714"), message);
|
|
121
|
+
}
|
|
122
|
+
warn(message) {
|
|
123
|
+
console.warn(chalk.yellow("\u26A0"), message);
|
|
124
|
+
}
|
|
125
|
+
error(message) {
|
|
126
|
+
console.error(chalk.red("\u2716"), message);
|
|
127
|
+
}
|
|
128
|
+
fileAction(action) {
|
|
129
|
+
const { type, outputPath } = action;
|
|
130
|
+
switch (type) {
|
|
131
|
+
case "create":
|
|
132
|
+
console.log(chalk.green(" + "), chalk.dim(outputPath));
|
|
133
|
+
break;
|
|
134
|
+
case "overwrite":
|
|
135
|
+
console.log(chalk.yellow(" ~ "), chalk.dim(outputPath));
|
|
136
|
+
break;
|
|
137
|
+
case "skip-user":
|
|
138
|
+
console.log(chalk.blue(" - "), chalk.dim(outputPath), chalk.dim("(skipped)"));
|
|
139
|
+
break;
|
|
140
|
+
case "skip-empty":
|
|
141
|
+
console.log(chalk.dim(" \xB7 "), chalk.dim(outputPath), chalk.dim("(empty)"));
|
|
142
|
+
break;
|
|
143
|
+
case "skip-equal":
|
|
144
|
+
console.log(chalk.dim(" = "), chalk.dim(outputPath), chalk.dim("(unchanged)"));
|
|
145
|
+
break;
|
|
146
|
+
case "delete":
|
|
147
|
+
console.log(chalk.red(" \u2716 "), chalk.dim(outputPath), chalk.red("(deleted)"));
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// @awa-impl: GEN-9_AC-1, GEN-9_AC-2, GEN-9_AC-3, GEN-9_AC-4, GEN-9_AC-5, GEN-9_AC-6
|
|
152
|
+
summary(result) {
|
|
153
|
+
console.log("");
|
|
154
|
+
console.log(chalk.bold("Summary:"));
|
|
155
|
+
if (result.created === 0 && result.overwritten === 0 && result.deleted === 0) {
|
|
156
|
+
console.log(chalk.yellow(" \u26A0 No files were created, overwritten, or deleted"));
|
|
157
|
+
}
|
|
158
|
+
if (result.created > 0) {
|
|
159
|
+
console.log(chalk.green(` Created: ${result.created}`));
|
|
160
|
+
}
|
|
161
|
+
if (result.overwritten > 0) {
|
|
162
|
+
console.log(chalk.yellow(` Overwritten: ${result.overwritten}`));
|
|
163
|
+
}
|
|
164
|
+
if (result.deleted > 0) {
|
|
165
|
+
console.log(chalk.red(` Deleted: ${result.deleted}`));
|
|
166
|
+
}
|
|
167
|
+
if (result.skippedEqual > 0) {
|
|
168
|
+
console.log(chalk.dim(` Skipped (equal): ${result.skippedEqual}`));
|
|
169
|
+
}
|
|
170
|
+
if (result.skippedUser > 0) {
|
|
171
|
+
console.log(chalk.blue(` Skipped (user): ${result.skippedUser}`));
|
|
172
|
+
}
|
|
173
|
+
if (result.skippedEmpty > 0) {
|
|
174
|
+
console.log(chalk.dim(` Skipped (empty): ${result.skippedEmpty}`));
|
|
175
|
+
}
|
|
176
|
+
console.log("");
|
|
177
|
+
}
|
|
178
|
+
// @awa-impl: DIFF-4_AC-3
|
|
179
|
+
diffLine(line, type) {
|
|
180
|
+
switch (type) {
|
|
181
|
+
case "add":
|
|
182
|
+
console.log(chalk.green(line));
|
|
183
|
+
break;
|
|
184
|
+
case "remove":
|
|
185
|
+
console.log(chalk.red(line));
|
|
186
|
+
break;
|
|
187
|
+
case "context":
|
|
188
|
+
console.log(chalk.dim(line));
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// @awa-impl: DIFF-4_AC-4, DIFF-4_AC-5
|
|
193
|
+
diffSummary(result) {
|
|
194
|
+
console.log("");
|
|
195
|
+
const filesCompared = result.identical + result.modified + result.newFiles + result.extraFiles + result.binaryDiffers + result.deleteListed;
|
|
196
|
+
const differences = result.modified + result.newFiles + result.extraFiles + result.binaryDiffers + result.deleteListed;
|
|
197
|
+
console.log(chalk.bold(`${filesCompared} files compared, ${differences} differences`));
|
|
198
|
+
if (!result.hasDifferences) {
|
|
199
|
+
console.log(chalk.green("\u2714 No differences found"));
|
|
200
|
+
}
|
|
201
|
+
console.log(chalk.bold("Summary:"));
|
|
202
|
+
console.log(chalk.dim(` Identical: ${result.identical}`));
|
|
203
|
+
if (result.modified > 0) {
|
|
204
|
+
console.log(chalk.yellow(` Modified: ${result.modified}`));
|
|
205
|
+
}
|
|
206
|
+
if (result.newFiles > 0) {
|
|
207
|
+
console.log(chalk.green(` New: ${result.newFiles}`));
|
|
208
|
+
}
|
|
209
|
+
if (result.extraFiles > 0) {
|
|
210
|
+
console.log(chalk.red(` Extra: ${result.extraFiles}`));
|
|
211
|
+
}
|
|
212
|
+
if (result.binaryDiffers > 0) {
|
|
213
|
+
console.log(chalk.red(` Binary differs: ${result.binaryDiffers}`));
|
|
214
|
+
}
|
|
215
|
+
if (result.deleteListed > 0) {
|
|
216
|
+
console.log(chalk.red(` Delete listed: ${result.deleteListed}`));
|
|
217
|
+
}
|
|
218
|
+
console.log("");
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
var logger = new Logger();
|
|
222
|
+
|
|
223
|
+
// src/core/config.ts
|
|
224
|
+
var DEFAULT_CONFIG_PATH = ".awa.toml";
|
|
225
|
+
var ConfigLoader = class {
|
|
226
|
+
// @awa-impl: CFG-1_AC-1, CFG-1_AC-2, CFG-1_AC-3, CFG-1_AC-4
|
|
227
|
+
async load(configPath) {
|
|
228
|
+
const pathToLoad = configPath ?? DEFAULT_CONFIG_PATH;
|
|
229
|
+
const exists = await pathExists(pathToLoad);
|
|
230
|
+
if (configPath && !exists) {
|
|
231
|
+
throw new ConfigError(
|
|
232
|
+
`Configuration file not found: ${configPath}`,
|
|
233
|
+
"FILE_NOT_FOUND",
|
|
234
|
+
configPath
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
if (!configPath && !exists) {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
try {
|
|
241
|
+
const content = await readTextFile(pathToLoad);
|
|
242
|
+
const parsed = parse(content);
|
|
243
|
+
const config = {};
|
|
244
|
+
if (parsed.output !== void 0) {
|
|
245
|
+
if (typeof parsed.output !== "string") {
|
|
246
|
+
throw new ConfigError(
|
|
247
|
+
`Invalid type for 'output': expected string, got ${typeof parsed.output}`,
|
|
248
|
+
"INVALID_TYPE",
|
|
249
|
+
pathToLoad
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
config.output = parsed.output;
|
|
253
|
+
}
|
|
254
|
+
if (parsed.template !== void 0) {
|
|
255
|
+
if (typeof parsed.template !== "string") {
|
|
256
|
+
throw new ConfigError(
|
|
257
|
+
`Invalid type for 'template': expected string, got ${typeof parsed.template}`,
|
|
258
|
+
"INVALID_TYPE",
|
|
259
|
+
pathToLoad
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
config.template = parsed.template;
|
|
263
|
+
}
|
|
264
|
+
if (parsed.features !== void 0) {
|
|
265
|
+
if (!Array.isArray(parsed.features) || !parsed.features.every((f) => typeof f === "string")) {
|
|
266
|
+
throw new ConfigError(
|
|
267
|
+
`Invalid type for 'features': expected array of strings`,
|
|
268
|
+
"INVALID_TYPE",
|
|
269
|
+
pathToLoad
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
config.features = parsed.features;
|
|
273
|
+
}
|
|
274
|
+
if (parsed.preset !== void 0) {
|
|
275
|
+
if (!Array.isArray(parsed.preset) || !parsed.preset.every((p) => typeof p === "string")) {
|
|
276
|
+
throw new ConfigError(
|
|
277
|
+
`Invalid type for 'preset': expected array of strings`,
|
|
278
|
+
"INVALID_TYPE",
|
|
279
|
+
pathToLoad
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
config.preset = parsed.preset;
|
|
283
|
+
}
|
|
284
|
+
if (parsed["remove-features"] !== void 0) {
|
|
285
|
+
if (!Array.isArray(parsed["remove-features"]) || !parsed["remove-features"].every((f) => typeof f === "string")) {
|
|
286
|
+
throw new ConfigError(
|
|
287
|
+
`Invalid type for 'remove-features': expected array of strings`,
|
|
288
|
+
"INVALID_TYPE",
|
|
289
|
+
pathToLoad
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
config["remove-features"] = parsed["remove-features"];
|
|
293
|
+
}
|
|
294
|
+
if (parsed.force !== void 0) {
|
|
295
|
+
if (typeof parsed.force !== "boolean") {
|
|
296
|
+
throw new ConfigError(
|
|
297
|
+
`Invalid type for 'force': expected boolean, got ${typeof parsed.force}`,
|
|
298
|
+
"INVALID_TYPE",
|
|
299
|
+
pathToLoad
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
config.force = parsed.force;
|
|
303
|
+
}
|
|
304
|
+
if (parsed["dry-run"] !== void 0) {
|
|
305
|
+
if (typeof parsed["dry-run"] !== "boolean") {
|
|
306
|
+
throw new ConfigError(
|
|
307
|
+
`Invalid type for 'dry-run': expected boolean, got ${typeof parsed["dry-run"]}`,
|
|
308
|
+
"INVALID_TYPE",
|
|
309
|
+
pathToLoad
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
config["dry-run"] = parsed["dry-run"];
|
|
313
|
+
}
|
|
314
|
+
if (parsed.delete !== void 0) {
|
|
315
|
+
if (typeof parsed.delete !== "boolean") {
|
|
316
|
+
throw new ConfigError(
|
|
317
|
+
`Invalid type for 'delete': expected boolean, got ${typeof parsed.delete}`,
|
|
318
|
+
"INVALID_TYPE",
|
|
319
|
+
pathToLoad
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
config.delete = parsed.delete;
|
|
323
|
+
}
|
|
324
|
+
if (parsed.refresh !== void 0) {
|
|
325
|
+
if (typeof parsed.refresh !== "boolean") {
|
|
326
|
+
throw new ConfigError(
|
|
327
|
+
`Invalid type for 'refresh': expected boolean, got ${typeof parsed.refresh}`,
|
|
328
|
+
"INVALID_TYPE",
|
|
329
|
+
pathToLoad
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
config.refresh = parsed.refresh;
|
|
333
|
+
}
|
|
334
|
+
if (parsed.presets !== void 0) {
|
|
335
|
+
if (parsed.presets === null || typeof parsed.presets !== "object" || Array.isArray(parsed.presets)) {
|
|
336
|
+
throw new ConfigError(
|
|
337
|
+
`Invalid type for 'presets': expected table of string arrays`,
|
|
338
|
+
"INVALID_PRESET",
|
|
339
|
+
pathToLoad
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
const defs = {};
|
|
343
|
+
for (const [presetName, value] of Object.entries(
|
|
344
|
+
parsed.presets
|
|
345
|
+
)) {
|
|
346
|
+
if (!Array.isArray(value) || !value.every((v) => typeof v === "string")) {
|
|
347
|
+
throw new ConfigError(
|
|
348
|
+
`Invalid preset '${presetName}': expected array of strings`,
|
|
349
|
+
"INVALID_PRESET",
|
|
350
|
+
pathToLoad
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
defs[presetName] = value;
|
|
354
|
+
}
|
|
355
|
+
config.presets = defs;
|
|
356
|
+
}
|
|
357
|
+
if (parsed["list-unknown"] !== void 0) {
|
|
358
|
+
if (typeof parsed["list-unknown"] !== "boolean") {
|
|
359
|
+
throw new ConfigError(
|
|
360
|
+
`Invalid type for 'list-unknown': expected boolean, got ${typeof parsed["list-unknown"]}`,
|
|
361
|
+
"INVALID_TYPE",
|
|
362
|
+
pathToLoad
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
config["list-unknown"] = parsed["list-unknown"];
|
|
366
|
+
}
|
|
367
|
+
const knownKeys = /* @__PURE__ */ new Set([
|
|
368
|
+
"output",
|
|
369
|
+
"template",
|
|
370
|
+
"features",
|
|
371
|
+
"preset",
|
|
372
|
+
"remove-features",
|
|
373
|
+
"presets",
|
|
374
|
+
"force",
|
|
375
|
+
"dry-run",
|
|
376
|
+
"delete",
|
|
377
|
+
"refresh",
|
|
378
|
+
"list-unknown"
|
|
379
|
+
]);
|
|
380
|
+
for (const key of Object.keys(parsed)) {
|
|
381
|
+
if (!knownKeys.has(key)) {
|
|
382
|
+
logger.warn(`Unknown configuration option: '${key}'`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return config;
|
|
386
|
+
} catch (error) {
|
|
387
|
+
if (error instanceof ConfigError) {
|
|
388
|
+
throw error;
|
|
389
|
+
}
|
|
390
|
+
throw new ConfigError(
|
|
391
|
+
`Failed to parse TOML configuration: ${error instanceof Error ? error.message : String(error)}`,
|
|
392
|
+
"PARSE_ERROR",
|
|
393
|
+
pathToLoad
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
// @awa-impl: CFG-4_AC-1, CFG-4_AC-2, CFG-4_AC-3, CFG-4_AC-4
|
|
398
|
+
// @awa-impl: CLI-2_AC-2, CLI-2_AC-3, CLI-2_AC-4
|
|
399
|
+
merge(cli, file) {
|
|
400
|
+
const output = cli.output ?? file?.output;
|
|
401
|
+
if (!output) {
|
|
402
|
+
throw new ConfigError(
|
|
403
|
+
"Output directory is required. Provide it as a positional argument or in the config file.",
|
|
404
|
+
"MISSING_OUTPUT",
|
|
405
|
+
null
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
const template = cli.template ?? file?.template ?? null;
|
|
409
|
+
const features = cli.features ?? file?.features ?? [];
|
|
410
|
+
const preset = cli.preset ?? file?.preset ?? [];
|
|
411
|
+
const removeFeatures = cli.removeFeatures ?? file?.["remove-features"] ?? [];
|
|
412
|
+
const presets = file?.presets ?? {};
|
|
413
|
+
const force = cli.force ?? file?.force ?? false;
|
|
414
|
+
const dryRun = cli.dryRun ?? file?.["dry-run"] ?? false;
|
|
415
|
+
const enableDelete = cli.delete ?? file?.delete ?? false;
|
|
416
|
+
const refresh = cli.refresh ?? file?.refresh ?? false;
|
|
417
|
+
const listUnknown = cli.listUnknown ?? file?.["list-unknown"] ?? false;
|
|
418
|
+
return {
|
|
419
|
+
output,
|
|
420
|
+
template,
|
|
421
|
+
features,
|
|
422
|
+
preset,
|
|
423
|
+
removeFeatures,
|
|
424
|
+
force,
|
|
425
|
+
dryRun,
|
|
426
|
+
delete: enableDelete,
|
|
427
|
+
refresh,
|
|
428
|
+
presets,
|
|
429
|
+
listUnknown
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
var configLoader = new ConfigLoader();
|
|
434
|
+
|
|
435
|
+
// src/core/differ.ts
|
|
436
|
+
import { tmpdir } from "os";
|
|
437
|
+
import { join as join4, relative as relative2 } from "path";
|
|
438
|
+
import { structuredPatch } from "diff";
|
|
439
|
+
import { isBinaryFile as detectBinaryFile } from "isbinaryfile";
|
|
440
|
+
|
|
441
|
+
// src/core/delete-list.ts
|
|
442
|
+
import { join as join2 } from "path";
|
|
443
|
+
var DELETE_LIST_FILENAME = "_delete.txt";
|
|
444
|
+
function parseDeleteList(content) {
|
|
445
|
+
const entries = [];
|
|
446
|
+
let currentFeatures;
|
|
447
|
+
for (const raw of content.split("\n")) {
|
|
448
|
+
const line = raw.trim();
|
|
449
|
+
if (line.length === 0) continue;
|
|
450
|
+
if (line.startsWith("#")) {
|
|
451
|
+
const featureMatch = line.match(/^#\s*@feature\s+(.+)$/);
|
|
452
|
+
if (featureMatch) {
|
|
453
|
+
const featureSection = featureMatch[1];
|
|
454
|
+
currentFeatures = featureSection ? featureSection.trim().split(/\s+/) : void 0;
|
|
455
|
+
} else {
|
|
456
|
+
currentFeatures = void 0;
|
|
457
|
+
}
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
entries.push({ path: line, features: currentFeatures });
|
|
461
|
+
}
|
|
462
|
+
return entries;
|
|
463
|
+
}
|
|
464
|
+
function resolveDeleteList(entries, activeFeatures) {
|
|
465
|
+
const activeSet = new Set(activeFeatures);
|
|
466
|
+
return entries.filter((e) => e.features === void 0 || !e.features.some((f) => activeSet.has(f))).map((e) => e.path);
|
|
467
|
+
}
|
|
468
|
+
async function loadDeleteList(templatePath) {
|
|
469
|
+
const deleteListPath = join2(templatePath, DELETE_LIST_FILENAME);
|
|
470
|
+
if (!await pathExists(deleteListPath)) {
|
|
471
|
+
return [];
|
|
472
|
+
}
|
|
473
|
+
const content = await readTextFile(deleteListPath);
|
|
474
|
+
return parseDeleteList(content);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// src/core/generator.ts
|
|
478
|
+
import { join as join3, relative } from "path";
|
|
479
|
+
|
|
480
|
+
// src/core/resolver.ts
|
|
481
|
+
import { MultiSelectPrompt } from "@clack/core";
|
|
482
|
+
import { isCancel, multiselect } from "@clack/prompts";
|
|
483
|
+
import chalk2 from "chalk";
|
|
484
|
+
var _unicode = process.platform !== "win32";
|
|
485
|
+
var _s = (c, fb) => _unicode ? c : fb;
|
|
486
|
+
var _CHECKED = _s("\u25FC", "[+]");
|
|
487
|
+
var _UNCHECKED_A = _s("\u25FB", "[\xB7]");
|
|
488
|
+
var _UNCHECKED = _s("\u25FB", "[ ]");
|
|
489
|
+
var _BAR = _s("\u2502", "|");
|
|
490
|
+
var _BAR_END = _s("\u2514", "-");
|
|
491
|
+
function _renderDeleteItem(opt, state) {
|
|
492
|
+
const label = opt.label ?? opt.value;
|
|
493
|
+
const hint = opt.hint ? ` ${chalk2.dim(`(${opt.hint})`)}` : "";
|
|
494
|
+
switch (state) {
|
|
495
|
+
case "active":
|
|
496
|
+
return `${chalk2.cyan(_UNCHECKED_A)} ${label}${hint}`;
|
|
497
|
+
case "selected":
|
|
498
|
+
return `${chalk2.red(_CHECKED)} ${chalk2.dim(label)}${hint}`;
|
|
499
|
+
case "active-selected":
|
|
500
|
+
return `${chalk2.red(_CHECKED)} ${label}${hint}`;
|
|
501
|
+
case "cancelled":
|
|
502
|
+
return chalk2.strikethrough(chalk2.dim(label));
|
|
503
|
+
case "submitted":
|
|
504
|
+
return chalk2.dim(label);
|
|
505
|
+
default:
|
|
506
|
+
return `${chalk2.dim(_UNCHECKED)} ${chalk2.dim(label)}`;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
async function deleteMultiselect(opts) {
|
|
510
|
+
const { message, options, initialValues, required = false } = opts;
|
|
511
|
+
return new MultiSelectPrompt({
|
|
512
|
+
options,
|
|
513
|
+
initialValues,
|
|
514
|
+
required,
|
|
515
|
+
render() {
|
|
516
|
+
const self = this;
|
|
517
|
+
const header = `${chalk2.gray(_BAR)}
|
|
518
|
+
${chalk2.cyan(_BAR)} ${message}
|
|
519
|
+
`;
|
|
520
|
+
const getState = (opt, idx) => {
|
|
521
|
+
const active = idx === self.cursor;
|
|
522
|
+
const sel = self.value.includes(opt.value);
|
|
523
|
+
if (active && sel) return "active-selected";
|
|
524
|
+
if (sel) return "selected";
|
|
525
|
+
if (active) return "active";
|
|
526
|
+
return "inactive";
|
|
527
|
+
};
|
|
528
|
+
switch (self.state) {
|
|
529
|
+
case "submit":
|
|
530
|
+
return `${header}${chalk2.gray(_BAR)} ` + (self.options.filter((o) => self.value.includes(o.value)).map((o) => _renderDeleteItem(o, "submitted")).join(chalk2.dim(", ")) || chalk2.dim("none"));
|
|
531
|
+
case "cancel": {
|
|
532
|
+
const cancelled = self.options.filter((o) => self.value.includes(o.value)).map((o) => _renderDeleteItem(o, "cancelled")).join(chalk2.dim(", "));
|
|
533
|
+
return `${header}${chalk2.gray(_BAR)} ${cancelled.trim() ? `${cancelled}
|
|
534
|
+
${chalk2.gray(_BAR)}` : chalk2.dim("none")}`;
|
|
535
|
+
}
|
|
536
|
+
default:
|
|
537
|
+
return `${header}${chalk2.cyan(_BAR)} ` + self.options.map((o, i) => _renderDeleteItem(o, getState(o, i))).join(`
|
|
538
|
+
${chalk2.cyan(_BAR)} `) + `
|
|
539
|
+
${chalk2.cyan(_BAR_END)}
|
|
540
|
+
`;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}).prompt();
|
|
544
|
+
}
|
|
545
|
+
var ConflictResolver = class {
|
|
546
|
+
// @awa-impl: GEN-4_AC-1, GEN-4_AC-2, GEN-4_AC-3
|
|
547
|
+
// @awa-impl: GEN-5_AC-1, GEN-5_AC-2, GEN-5_AC-3, GEN-5_AC-4, GEN-5_AC-5, GEN-5_AC-6, GEN-5_AC-7
|
|
548
|
+
// @awa-impl: CLI-5_AC-2, CLI-5_AC-3
|
|
549
|
+
// @awa-impl: GEN-6_AC-3
|
|
550
|
+
async resolveBatch(conflicts, force, dryRun) {
|
|
551
|
+
const identicalPaths = conflicts.filter((c) => c.newContent === c.existingContent).map((c) => c.outputPath);
|
|
552
|
+
const differentFiles = conflicts.filter((c) => c.newContent !== c.existingContent);
|
|
553
|
+
if (dryRun) {
|
|
554
|
+
return {
|
|
555
|
+
overwrite: [],
|
|
556
|
+
skip: differentFiles.map((c) => c.outputPath),
|
|
557
|
+
equal: identicalPaths
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
if (force) {
|
|
561
|
+
return {
|
|
562
|
+
overwrite: differentFiles.map((c) => c.outputPath),
|
|
563
|
+
skip: [],
|
|
564
|
+
equal: identicalPaths
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
if (differentFiles.length === 0) {
|
|
568
|
+
return {
|
|
569
|
+
overwrite: [],
|
|
570
|
+
skip: [],
|
|
571
|
+
equal: identicalPaths
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
const selected = await multiselect({
|
|
575
|
+
message: "The following files already exist. Select files to overwrite:",
|
|
576
|
+
options: differentFiles.map((c) => ({
|
|
577
|
+
value: c.outputPath,
|
|
578
|
+
label: c.outputPath
|
|
579
|
+
})),
|
|
580
|
+
initialValues: differentFiles.map((c) => c.outputPath),
|
|
581
|
+
// All selected by default (AC-5.6)
|
|
582
|
+
required: false
|
|
583
|
+
});
|
|
584
|
+
if (isCancel(selected)) {
|
|
585
|
+
process.exit(1);
|
|
586
|
+
}
|
|
587
|
+
const selectedPaths = selected;
|
|
588
|
+
const allPaths = differentFiles.map((c) => c.outputPath);
|
|
589
|
+
return {
|
|
590
|
+
overwrite: selectedPaths,
|
|
591
|
+
skip: allPaths.filter((p) => !selectedPaths.includes(p)),
|
|
592
|
+
equal: identicalPaths
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
var conflictResolver = new ConflictResolver();
|
|
597
|
+
var DeleteResolver = class {
|
|
598
|
+
/**
|
|
599
|
+
* Prompt user to confirm which files to delete.
|
|
600
|
+
* Returns the list of absolute paths confirmed for deletion.
|
|
601
|
+
*/
|
|
602
|
+
async resolveDeletes(candidates, force, dryRun) {
|
|
603
|
+
if (candidates.length === 0) {
|
|
604
|
+
return [];
|
|
605
|
+
}
|
|
606
|
+
if (dryRun) {
|
|
607
|
+
return candidates;
|
|
608
|
+
}
|
|
609
|
+
if (force) {
|
|
610
|
+
return candidates;
|
|
611
|
+
}
|
|
612
|
+
const selected = await deleteMultiselect({
|
|
613
|
+
message: "\u26A0 WARNING: The selected files will be PERMANENTLY DELETED from disk.\n Deselect any files you want to keep. Press Enter to confirm deletion:",
|
|
614
|
+
options: candidates.map((p) => ({
|
|
615
|
+
value: p,
|
|
616
|
+
label: p
|
|
617
|
+
})),
|
|
618
|
+
initialValues: candidates,
|
|
619
|
+
required: false
|
|
620
|
+
});
|
|
621
|
+
if (isCancel(selected)) {
|
|
622
|
+
process.exit(1);
|
|
623
|
+
}
|
|
624
|
+
return selected;
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
var deleteResolver = new DeleteResolver();
|
|
628
|
+
|
|
629
|
+
// src/core/template.ts
|
|
630
|
+
import { Eta } from "eta";
|
|
631
|
+
var EMPTY_FILE_MARKER = "<!-- AWA:EMPTY_FILE -->";
|
|
632
|
+
var TemplateEngine = class {
|
|
633
|
+
eta = null;
|
|
634
|
+
templateDir = null;
|
|
635
|
+
compiledCache = /* @__PURE__ */ new Map();
|
|
636
|
+
// @awa-impl: TPL-8_AC-1, TPL-8_AC-2, TPL-8_AC-3, TPL-8_AC-4
|
|
637
|
+
configure(templateDir) {
|
|
638
|
+
this.templateDir = templateDir;
|
|
639
|
+
this.compiledCache.clear();
|
|
640
|
+
this.eta = new Eta({
|
|
641
|
+
views: templateDir,
|
|
642
|
+
cache: true,
|
|
643
|
+
// Enable compilation caching
|
|
644
|
+
autoEscape: false,
|
|
645
|
+
// Don't escape HTML by default
|
|
646
|
+
defaultExtension: ""
|
|
647
|
+
// No automatic extension - use exact path as specified (AC-8.2)
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
// @awa-impl: TPL-5_AC-1, TPL-5_AC-2, TPL-5_AC-3
|
|
651
|
+
// @awa-impl: TPL-6_AC-1, TPL-6_AC-2
|
|
652
|
+
// @awa-impl: TPL-7_AC-1, TPL-7_AC-2
|
|
653
|
+
// @awa-impl: TPL-11_AC-1, TPL-11_AC-2
|
|
654
|
+
async render(templatePath, context) {
|
|
655
|
+
if (!this.eta || !this.templateDir) {
|
|
656
|
+
throw new TemplateError(
|
|
657
|
+
"Template engine not configured. Call configure() first.",
|
|
658
|
+
"RENDER_ERROR"
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
try {
|
|
662
|
+
const templateContent = await readTextFile(templatePath);
|
|
663
|
+
const rendered = await this.eta.renderStringAsync(templateContent, {
|
|
664
|
+
features: context.features
|
|
665
|
+
});
|
|
666
|
+
const trimmed = rendered.trim();
|
|
667
|
+
const isEmpty = trimmed.length === 0;
|
|
668
|
+
const isEmptyFileMarker = trimmed === EMPTY_FILE_MARKER;
|
|
669
|
+
return {
|
|
670
|
+
content: rendered,
|
|
671
|
+
isEmpty,
|
|
672
|
+
isEmptyFileMarker
|
|
673
|
+
};
|
|
674
|
+
} catch (error) {
|
|
675
|
+
throw new TemplateError(
|
|
676
|
+
`Failed to render template ${templatePath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
677
|
+
"RENDER_ERROR",
|
|
678
|
+
templatePath
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
};
|
|
683
|
+
var templateEngine = new TemplateEngine();
|
|
684
|
+
|
|
685
|
+
// src/core/generator.ts
|
|
686
|
+
var FileGenerator = class {
|
|
687
|
+
// @awa-impl: GEN-1_AC-1, GEN-1_AC-2, GEN-1_AC-3
|
|
688
|
+
// @awa-impl: GEN-2_AC-1, GEN-2_AC-2, GEN-2_AC-3
|
|
689
|
+
// @awa-impl: GEN-3_AC-1, GEN-3_AC-2, GEN-3_AC-3
|
|
690
|
+
async generate(options) {
|
|
691
|
+
const { templatePath, outputPath, features, force, dryRun } = options;
|
|
692
|
+
const enableDelete = options.delete;
|
|
693
|
+
templateEngine.configure(templatePath);
|
|
694
|
+
const actions = [];
|
|
695
|
+
let created = 0;
|
|
696
|
+
let overwritten = 0;
|
|
697
|
+
let deleted = 0;
|
|
698
|
+
let skippedEmpty = 0;
|
|
699
|
+
let skippedUser = 0;
|
|
700
|
+
let skippedEqual = 0;
|
|
701
|
+
const filesToProcess = [];
|
|
702
|
+
const conflicts = [];
|
|
703
|
+
try {
|
|
704
|
+
for await (const templateFile of this.walkTemplates(templatePath)) {
|
|
705
|
+
const outputFile = this.computeOutputPath(templateFile, templatePath, outputPath);
|
|
706
|
+
const result = await templateEngine.render(templateFile, { features });
|
|
707
|
+
if (result.isEmpty && !result.isEmptyFileMarker) {
|
|
708
|
+
actions.push({
|
|
709
|
+
type: "skip-empty",
|
|
710
|
+
sourcePath: templateFile,
|
|
711
|
+
outputPath: outputFile
|
|
712
|
+
});
|
|
713
|
+
skippedEmpty++;
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
716
|
+
const content = result.isEmptyFileMarker ? "" : result.content;
|
|
717
|
+
const fileExists = await pathExists(outputFile);
|
|
718
|
+
if (fileExists) {
|
|
719
|
+
const existingContent = await readTextFile(outputFile);
|
|
720
|
+
conflicts.push({
|
|
721
|
+
outputPath: outputFile,
|
|
722
|
+
sourcePath: templateFile,
|
|
723
|
+
newContent: content,
|
|
724
|
+
existingContent
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
filesToProcess.push({
|
|
728
|
+
templateFile,
|
|
729
|
+
outputFile,
|
|
730
|
+
content,
|
|
731
|
+
isNew: !fileExists
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
let resolution = {
|
|
735
|
+
overwrite: [],
|
|
736
|
+
skip: [],
|
|
737
|
+
equal: []
|
|
738
|
+
};
|
|
739
|
+
if (conflicts.length > 0) {
|
|
740
|
+
resolution = await conflictResolver.resolveBatch(conflicts, force, dryRun);
|
|
741
|
+
}
|
|
742
|
+
for (const file of filesToProcess) {
|
|
743
|
+
if (file.isNew) {
|
|
744
|
+
if (!dryRun) {
|
|
745
|
+
await writeTextFile(file.outputFile, file.content);
|
|
746
|
+
}
|
|
747
|
+
actions.push({
|
|
748
|
+
type: "create",
|
|
749
|
+
sourcePath: file.templateFile,
|
|
750
|
+
outputPath: file.outputFile
|
|
751
|
+
});
|
|
752
|
+
created++;
|
|
753
|
+
logger.fileAction({
|
|
754
|
+
type: "create",
|
|
755
|
+
sourcePath: file.templateFile,
|
|
756
|
+
outputPath: file.outputFile
|
|
757
|
+
});
|
|
758
|
+
} else if (resolution.overwrite.includes(file.outputFile)) {
|
|
759
|
+
if (!dryRun) {
|
|
760
|
+
await writeTextFile(file.outputFile, file.content);
|
|
761
|
+
}
|
|
762
|
+
actions.push({
|
|
763
|
+
type: "overwrite",
|
|
764
|
+
sourcePath: file.templateFile,
|
|
765
|
+
outputPath: file.outputFile
|
|
766
|
+
});
|
|
767
|
+
overwritten++;
|
|
768
|
+
logger.fileAction({
|
|
769
|
+
type: "overwrite",
|
|
770
|
+
sourcePath: file.templateFile,
|
|
771
|
+
outputPath: file.outputFile
|
|
772
|
+
});
|
|
773
|
+
} else if (resolution.equal.includes(file.outputFile)) {
|
|
774
|
+
actions.push({
|
|
775
|
+
type: "skip-equal",
|
|
776
|
+
sourcePath: file.templateFile,
|
|
777
|
+
outputPath: file.outputFile
|
|
778
|
+
});
|
|
779
|
+
skippedEqual++;
|
|
780
|
+
logger.fileAction({
|
|
781
|
+
type: "skip-equal",
|
|
782
|
+
sourcePath: file.templateFile,
|
|
783
|
+
outputPath: file.outputFile
|
|
784
|
+
});
|
|
785
|
+
} else if (resolution.skip.includes(file.outputFile)) {
|
|
786
|
+
actions.push({
|
|
787
|
+
type: "skip-user",
|
|
788
|
+
sourcePath: file.templateFile,
|
|
789
|
+
outputPath: file.outputFile
|
|
790
|
+
});
|
|
791
|
+
skippedUser++;
|
|
792
|
+
logger.fileAction({
|
|
793
|
+
type: "skip-user",
|
|
794
|
+
sourcePath: file.templateFile,
|
|
795
|
+
outputPath: file.outputFile
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
const deleteEntries = await loadDeleteList(templatePath);
|
|
800
|
+
if (deleteEntries.length > 0) {
|
|
801
|
+
const deleteList = resolveDeleteList(deleteEntries, features);
|
|
802
|
+
const generatedOutputPaths = new Set(filesToProcess.map((f) => f.outputFile));
|
|
803
|
+
const deleteCandidates = [];
|
|
804
|
+
for (const relPath of deleteList) {
|
|
805
|
+
const absPath = join3(outputPath, relPath);
|
|
806
|
+
if (generatedOutputPaths.has(absPath)) {
|
|
807
|
+
logger.warn(
|
|
808
|
+
`Delete list entry '${relPath}' conflicts with generated file \u2014 skipping deletion`
|
|
809
|
+
);
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
812
|
+
if (await pathExists(absPath)) {
|
|
813
|
+
deleteCandidates.push(absPath);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
if (deleteCandidates.length > 0) {
|
|
817
|
+
if (!enableDelete) {
|
|
818
|
+
for (const absPath of deleteCandidates) {
|
|
819
|
+
logger.warn(
|
|
820
|
+
`Would delete (pass --delete to enable): ${relative(outputPath, absPath)}`
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
} else {
|
|
824
|
+
const confirmed = await deleteResolver.resolveDeletes(deleteCandidates, force, dryRun);
|
|
825
|
+
for (const absPath of confirmed) {
|
|
826
|
+
if (!dryRun) {
|
|
827
|
+
await deleteFile(absPath);
|
|
828
|
+
}
|
|
829
|
+
actions.push({ type: "delete", outputPath: absPath });
|
|
830
|
+
deleted++;
|
|
831
|
+
logger.fileAction({ type: "delete", outputPath: absPath });
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
return {
|
|
837
|
+
actions,
|
|
838
|
+
created,
|
|
839
|
+
overwritten,
|
|
840
|
+
deleted,
|
|
841
|
+
skipped: skippedEmpty + skippedUser + skippedEqual,
|
|
842
|
+
skippedEmpty,
|
|
843
|
+
skippedUser,
|
|
844
|
+
skippedEqual
|
|
845
|
+
};
|
|
846
|
+
} catch (error) {
|
|
847
|
+
if (error instanceof Error && "code" in error) {
|
|
848
|
+
const code = error.code;
|
|
849
|
+
if (code === "EACCES" || code === "EPERM") {
|
|
850
|
+
throw new GenerationError(`Permission denied: ${error.message}`, "PERMISSION_DENIED");
|
|
851
|
+
}
|
|
852
|
+
if (code === "ENOSPC") {
|
|
853
|
+
throw new GenerationError(`Disk full: ${error.message}`, "DISK_FULL");
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
throw error;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
// @awa-impl: GEN-8_AC-1, GEN-8_AC-2, GEN-8_AC-3
|
|
860
|
+
// @awa-impl: TPL-9_AC-1, TPL-9_AC-2
|
|
861
|
+
async *walkTemplates(dir) {
|
|
862
|
+
yield* walkDirectory(dir);
|
|
863
|
+
}
|
|
864
|
+
// @awa-impl: GEN-1_AC-1, GEN-1_AC-2, GEN-1_AC-3
|
|
865
|
+
computeOutputPath(templatePath, templateRoot, outputRoot) {
|
|
866
|
+
const relativePath = relative(templateRoot, templatePath);
|
|
867
|
+
return join3(outputRoot, relativePath);
|
|
868
|
+
}
|
|
869
|
+
};
|
|
870
|
+
var fileGenerator = new FileGenerator();
|
|
871
|
+
|
|
872
|
+
// src/core/differ.ts
|
|
873
|
+
var DiffEngine = class {
|
|
874
|
+
// @awa-impl: DIFF-1_AC-1, DIFF-1_AC-2, DIFF-1_AC-3
|
|
875
|
+
// @awa-impl: DIFF-2_AC-1, DIFF-2_AC-2, DIFF-2_AC-3, DIFF-2_AC-4, DIFF-2_AC-5
|
|
876
|
+
// @awa-impl: DIFF-3_AC-1, DIFF-3_AC-2, DIFF-3_AC-3
|
|
877
|
+
// @awa-impl: DIFF-4_AC-1, DIFF-4_AC-2
|
|
878
|
+
// @awa-impl: DIFF-5_AC-1, DIFF-5_AC-2, DIFF-5_AC-3
|
|
879
|
+
// @awa-impl: DIFF-6_AC-1, DIFF-6_AC-2, DIFF-6_AC-3
|
|
880
|
+
async diff(options) {
|
|
881
|
+
const { templatePath, targetPath, features, listUnknown } = options;
|
|
882
|
+
const tempPath = await this.createTempDir();
|
|
883
|
+
try {
|
|
884
|
+
const generateOptions = {
|
|
885
|
+
templatePath,
|
|
886
|
+
outputPath: tempPath,
|
|
887
|
+
features,
|
|
888
|
+
force: true,
|
|
889
|
+
dryRun: false,
|
|
890
|
+
delete: false
|
|
891
|
+
};
|
|
892
|
+
await fileGenerator.generate(generateOptions);
|
|
893
|
+
const generatedFiles = /* @__PURE__ */ new Set();
|
|
894
|
+
const targetFiles = /* @__PURE__ */ new Set();
|
|
895
|
+
if (await pathExists(tempPath)) {
|
|
896
|
+
for await (const file of walkDirectory(tempPath)) {
|
|
897
|
+
const relPath = relative2(tempPath, file);
|
|
898
|
+
generatedFiles.add(relPath);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
if (await pathExists(targetPath)) {
|
|
902
|
+
for await (const file of walkDirectory(targetPath)) {
|
|
903
|
+
const relPath = relative2(targetPath, file);
|
|
904
|
+
targetFiles.add(relPath);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
const files = [];
|
|
908
|
+
for (const relPath of generatedFiles) {
|
|
909
|
+
const generatedFilePath = join4(tempPath, relPath);
|
|
910
|
+
const targetFilePath = join4(targetPath, relPath);
|
|
911
|
+
if (targetFiles.has(relPath)) {
|
|
912
|
+
const fileDiff = await this.compareFiles(generatedFilePath, targetFilePath, relPath);
|
|
913
|
+
files.push(fileDiff);
|
|
914
|
+
} else {
|
|
915
|
+
files.push({
|
|
916
|
+
relativePath: relPath,
|
|
917
|
+
status: "new"
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
if (listUnknown) {
|
|
922
|
+
for (const relPath of targetFiles) {
|
|
923
|
+
if (generatedFiles.has(relPath)) {
|
|
924
|
+
continue;
|
|
925
|
+
}
|
|
926
|
+
files.push({
|
|
927
|
+
relativePath: relPath,
|
|
928
|
+
status: "extra"
|
|
929
|
+
});
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
const deleteEntries = await loadDeleteList(templatePath);
|
|
933
|
+
const deleteList = resolveDeleteList(deleteEntries, features ?? []);
|
|
934
|
+
for (const relPath of deleteList) {
|
|
935
|
+
if (targetFiles.has(relPath) && !generatedFiles.has(relPath)) {
|
|
936
|
+
const existingIdx = files.findIndex(
|
|
937
|
+
(f) => f.relativePath === relPath && f.status === "extra"
|
|
938
|
+
);
|
|
939
|
+
if (existingIdx !== -1) {
|
|
940
|
+
files.splice(existingIdx, 1);
|
|
941
|
+
}
|
|
942
|
+
files.push({
|
|
943
|
+
relativePath: relPath,
|
|
944
|
+
status: "delete-listed"
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
const identical = files.filter((f) => f.status === "identical").length;
|
|
949
|
+
const modified = files.filter((f) => f.status === "modified").length;
|
|
950
|
+
const newFiles = files.filter((f) => f.status === "new").length;
|
|
951
|
+
const extraFiles = files.filter((f) => f.status === "extra").length;
|
|
952
|
+
const binaryDiffers = files.filter((f) => f.status === "binary-differs").length;
|
|
953
|
+
const deleteListed = files.filter((f) => f.status === "delete-listed").length;
|
|
954
|
+
const hasDifferences = modified > 0 || newFiles > 0 || extraFiles > 0 || binaryDiffers > 0 || deleteListed > 0;
|
|
955
|
+
return {
|
|
956
|
+
files,
|
|
957
|
+
identical,
|
|
958
|
+
modified,
|
|
959
|
+
newFiles,
|
|
960
|
+
extraFiles,
|
|
961
|
+
binaryDiffers,
|
|
962
|
+
deleteListed,
|
|
963
|
+
hasDifferences
|
|
964
|
+
};
|
|
965
|
+
} finally {
|
|
966
|
+
await this.cleanupTempDir(tempPath);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
// @awa-impl: DIFF-1_AC-1, DIFF-1_AC-2
|
|
970
|
+
async createTempDir() {
|
|
971
|
+
const systemTemp = tmpdir();
|
|
972
|
+
const timestamp = Date.now();
|
|
973
|
+
const random = Math.random().toString(36).substring(2, 8);
|
|
974
|
+
const tempPath = join4(systemTemp, `awa-diff-${timestamp}-${random}`);
|
|
975
|
+
await ensureDir(tempPath);
|
|
976
|
+
return tempPath;
|
|
977
|
+
}
|
|
978
|
+
// @awa-impl: DIFF-6_AC-1, DIFF-6_AC-2, DIFF-6_AC-3
|
|
979
|
+
async cleanupTempDir(tempPath) {
|
|
980
|
+
try {
|
|
981
|
+
if (await pathExists(tempPath)) {
|
|
982
|
+
await rmDir(tempPath);
|
|
983
|
+
}
|
|
984
|
+
} catch (_error) {
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
// @awa-impl: DIFF-2_AC-1, DIFF-2_AC-2, DIFF-2_AC-3, DIFF-2_AC-4, DIFF-2_AC-5
|
|
988
|
+
async compareFiles(generatedPath, targetPath, relativePath) {
|
|
989
|
+
const generatedBytes = await readBinaryFile(generatedPath);
|
|
990
|
+
const targetBytes = await readBinaryFile(targetPath);
|
|
991
|
+
if (generatedBytes.equals(targetBytes)) {
|
|
992
|
+
return {
|
|
993
|
+
relativePath,
|
|
994
|
+
status: "identical"
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
const isBinaryGenerated = await this.isBinaryFile(generatedPath);
|
|
998
|
+
const isBinaryTarget = await this.isBinaryFile(targetPath);
|
|
999
|
+
if (isBinaryGenerated || isBinaryTarget) {
|
|
1000
|
+
return {
|
|
1001
|
+
relativePath,
|
|
1002
|
+
status: "binary-differs"
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
const generatedContent = generatedBytes.toString("utf-8");
|
|
1006
|
+
const targetContent = targetBytes.toString("utf-8");
|
|
1007
|
+
const patch = structuredPatch(
|
|
1008
|
+
`a/${relativePath}`,
|
|
1009
|
+
`b/${relativePath}`,
|
|
1010
|
+
targetContent,
|
|
1011
|
+
generatedContent,
|
|
1012
|
+
"target",
|
|
1013
|
+
"generated",
|
|
1014
|
+
{
|
|
1015
|
+
context: 3
|
|
1016
|
+
}
|
|
1017
|
+
);
|
|
1018
|
+
const headerLines = [
|
|
1019
|
+
`diff --git a/${relativePath} b/${relativePath}`,
|
|
1020
|
+
`--- a/${relativePath}`,
|
|
1021
|
+
`+++ b/${relativePath}`
|
|
1022
|
+
];
|
|
1023
|
+
const hunkLines = patch.hunks.flatMap((hunk) => {
|
|
1024
|
+
const lines = [`@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`];
|
|
1025
|
+
lines.push(...hunk.lines);
|
|
1026
|
+
return lines;
|
|
1027
|
+
});
|
|
1028
|
+
const unifiedDiff = [...headerLines, ...hunkLines].join("\n");
|
|
1029
|
+
return {
|
|
1030
|
+
relativePath,
|
|
1031
|
+
status: "modified",
|
|
1032
|
+
unifiedDiff
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
// @awa-impl: DIFF-2_AC-5
|
|
1036
|
+
async isBinaryFile(filePath) {
|
|
1037
|
+
try {
|
|
1038
|
+
return await detectBinaryFile(filePath);
|
|
1039
|
+
} catch {
|
|
1040
|
+
return false;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
};
|
|
1044
|
+
var diffEngine = new DiffEngine();
|
|
1045
|
+
|
|
1046
|
+
// src/core/feature-resolver.ts
|
|
1047
|
+
var FeatureResolver = class {
|
|
1048
|
+
validatePresets(presetNames, definitions) {
|
|
1049
|
+
for (const name of presetNames) {
|
|
1050
|
+
if (!definitions[name]) {
|
|
1051
|
+
throw new ConfigError(`Unknown preset: ${name}`, "UNKNOWN_PRESET");
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
resolve(input) {
|
|
1056
|
+
const { baseFeatures, presetNames, removeFeatures, presetDefinitions } = input;
|
|
1057
|
+
this.validatePresets(presetNames, presetDefinitions);
|
|
1058
|
+
const finalFeatures = [];
|
|
1059
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1060
|
+
const add = (feature) => {
|
|
1061
|
+
if (seen.has(feature)) return;
|
|
1062
|
+
seen.add(feature);
|
|
1063
|
+
finalFeatures.push(feature);
|
|
1064
|
+
};
|
|
1065
|
+
for (const feature of baseFeatures) add(feature);
|
|
1066
|
+
for (const presetName of presetNames) {
|
|
1067
|
+
for (const feature of presetDefinitions[presetName] ?? []) add(feature);
|
|
1068
|
+
}
|
|
1069
|
+
if (removeFeatures.length === 0) return finalFeatures;
|
|
1070
|
+
const removeSet = new Set(removeFeatures);
|
|
1071
|
+
return finalFeatures.filter((f) => !removeSet.has(f));
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
1074
|
+
var featureResolver = new FeatureResolver();
|
|
1075
|
+
|
|
1076
|
+
// src/core/template-resolver.ts
|
|
1077
|
+
import { createHash } from "crypto";
|
|
1078
|
+
import { rm as rm2 } from "fs/promises";
|
|
1079
|
+
import { isAbsolute, join as join5, resolve } from "path";
|
|
1080
|
+
import degit from "degit";
|
|
1081
|
+
var TemplateResolver = class {
|
|
1082
|
+
// @awa-impl: CLI-3_AC-2, TPL-10_AC-1
|
|
1083
|
+
async resolve(source, refresh) {
|
|
1084
|
+
if (!source) {
|
|
1085
|
+
const bundledPath = join5(getTemplateDir(), "awa");
|
|
1086
|
+
return {
|
|
1087
|
+
type: "bundled",
|
|
1088
|
+
localPath: bundledPath,
|
|
1089
|
+
source: "bundled"
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
const type = this.detectType(source);
|
|
1093
|
+
if (type === "local") {
|
|
1094
|
+
const localPath = isAbsolute(source) ? source : resolve(process.cwd(), source);
|
|
1095
|
+
if (!await pathExists(localPath)) {
|
|
1096
|
+
throw new TemplateError(
|
|
1097
|
+
`Template source not found: ${localPath}`,
|
|
1098
|
+
"SOURCE_NOT_FOUND",
|
|
1099
|
+
source
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
return {
|
|
1103
|
+
type: "local",
|
|
1104
|
+
localPath,
|
|
1105
|
+
source
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
if (type === "git") {
|
|
1109
|
+
const cachePath = this.getCachePath(source);
|
|
1110
|
+
const cacheExists = await pathExists(cachePath);
|
|
1111
|
+
if (cacheExists && !refresh) {
|
|
1112
|
+
logger.info(`Using cached template: ${source}`);
|
|
1113
|
+
return {
|
|
1114
|
+
type: "git",
|
|
1115
|
+
localPath: cachePath,
|
|
1116
|
+
source
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1119
|
+
try {
|
|
1120
|
+
if (cacheExists && refresh) {
|
|
1121
|
+
logger.info(`Refreshing template: ${source}`);
|
|
1122
|
+
await rm2(cachePath, { recursive: true, force: true });
|
|
1123
|
+
} else {
|
|
1124
|
+
logger.info(`Fetching template: ${source}`);
|
|
1125
|
+
}
|
|
1126
|
+
await ensureDir(cachePath);
|
|
1127
|
+
const emitter = degit(source, { cache: false, force: true });
|
|
1128
|
+
await emitter.clone(cachePath);
|
|
1129
|
+
return {
|
|
1130
|
+
type: "git",
|
|
1131
|
+
localPath: cachePath,
|
|
1132
|
+
source
|
|
1133
|
+
};
|
|
1134
|
+
} catch (error) {
|
|
1135
|
+
throw new TemplateError(
|
|
1136
|
+
`Failed to fetch Git template: ${error instanceof Error ? error.message : String(error)}`,
|
|
1137
|
+
"FETCH_FAILED",
|
|
1138
|
+
source
|
|
1139
|
+
);
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
throw new TemplateError(
|
|
1143
|
+
`Unable to resolve template source: ${source}`,
|
|
1144
|
+
"SOURCE_NOT_FOUND",
|
|
1145
|
+
source
|
|
1146
|
+
);
|
|
1147
|
+
}
|
|
1148
|
+
// @awa-impl: TPL-2_AC-1 through TPL-2_AC-6
|
|
1149
|
+
detectType(source) {
|
|
1150
|
+
if (source.startsWith(".") || source.startsWith("/") || source.startsWith("~")) {
|
|
1151
|
+
return "local";
|
|
1152
|
+
}
|
|
1153
|
+
if (/^[a-zA-Z]:/.test(source)) {
|
|
1154
|
+
return "local";
|
|
1155
|
+
}
|
|
1156
|
+
return "git";
|
|
1157
|
+
}
|
|
1158
|
+
// @awa-impl: TPL-3_AC-1
|
|
1159
|
+
getCachePath(source) {
|
|
1160
|
+
const hash = createHash("sha256").update(source).digest("hex").substring(0, 16);
|
|
1161
|
+
const cacheDir = getCacheDir();
|
|
1162
|
+
return join5(cacheDir, hash);
|
|
1163
|
+
}
|
|
1164
|
+
};
|
|
1165
|
+
var templateResolver = new TemplateResolver();
|
|
1166
|
+
|
|
1167
|
+
// src/commands/diff.ts
|
|
1168
|
+
async function diffCommand(cliOptions) {
|
|
1169
|
+
try {
|
|
1170
|
+
intro("awa CLI - Template Diff");
|
|
1171
|
+
const fileConfig = await configLoader.load(cliOptions.config ?? null);
|
|
1172
|
+
const options = configLoader.merge(cliOptions, fileConfig);
|
|
1173
|
+
if (!await pathExists(options.output)) {
|
|
1174
|
+
throw new DiffError(`Target directory does not exist: ${options.output}`);
|
|
1175
|
+
}
|
|
1176
|
+
const targetPath = options.output;
|
|
1177
|
+
const template = await templateResolver.resolve(options.template, options.refresh);
|
|
1178
|
+
const features = featureResolver.resolve({
|
|
1179
|
+
baseFeatures: [...options.features],
|
|
1180
|
+
presetNames: [...options.preset],
|
|
1181
|
+
removeFeatures: [...options.removeFeatures],
|
|
1182
|
+
presetDefinitions: options.presets
|
|
1183
|
+
});
|
|
1184
|
+
const result = await diffEngine.diff({
|
|
1185
|
+
templatePath: template.localPath,
|
|
1186
|
+
targetPath,
|
|
1187
|
+
features,
|
|
1188
|
+
listUnknown: options.listUnknown
|
|
1189
|
+
});
|
|
1190
|
+
for (const file of result.files) {
|
|
1191
|
+
switch (file.status) {
|
|
1192
|
+
case "modified":
|
|
1193
|
+
logger.info(`Modified: ${file.relativePath}`);
|
|
1194
|
+
if (file.unifiedDiff) {
|
|
1195
|
+
const lines = file.unifiedDiff.split("\n");
|
|
1196
|
+
for (const line of lines) {
|
|
1197
|
+
if (line.startsWith("diff --git") || line.startsWith("index ") || line.startsWith("--- ") || line.startsWith("+++ ")) {
|
|
1198
|
+
logger.diffLine(line, "context");
|
|
1199
|
+
} else if (line.startsWith("+")) {
|
|
1200
|
+
logger.diffLine(line, "add");
|
|
1201
|
+
} else if (line.startsWith("-")) {
|
|
1202
|
+
logger.diffLine(line, "remove");
|
|
1203
|
+
} else if (line.startsWith("@@")) {
|
|
1204
|
+
logger.diffLine(line, "context");
|
|
1205
|
+
} else {
|
|
1206
|
+
logger.diffLine(line, "context");
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
break;
|
|
1211
|
+
case "new":
|
|
1212
|
+
logger.info(`New file: ${file.relativePath}`);
|
|
1213
|
+
break;
|
|
1214
|
+
case "extra":
|
|
1215
|
+
logger.warn(`Extra file (not in template): ${file.relativePath}`);
|
|
1216
|
+
break;
|
|
1217
|
+
case "binary-differs":
|
|
1218
|
+
logger.warn(`binary files differ: ${file.relativePath}`);
|
|
1219
|
+
break;
|
|
1220
|
+
case "delete-listed":
|
|
1221
|
+
logger.warn(`Delete listed: ${file.relativePath}`);
|
|
1222
|
+
break;
|
|
1223
|
+
case "identical":
|
|
1224
|
+
break;
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
logger.diffSummary(result);
|
|
1228
|
+
outro("Diff complete!");
|
|
1229
|
+
return result.hasDifferences ? 1 : 0;
|
|
1230
|
+
} catch (error) {
|
|
1231
|
+
if (error instanceof Error) {
|
|
1232
|
+
logger.error(error.message);
|
|
1233
|
+
} else {
|
|
1234
|
+
logger.error(String(error));
|
|
1235
|
+
}
|
|
1236
|
+
return 2;
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
// src/commands/generate.ts
|
|
1241
|
+
import { intro as intro2, isCancel as isCancel2, multiselect as multiselect2, outro as outro2 } from "@clack/prompts";
|
|
1242
|
+
var TOOL_FEATURES = [
|
|
1243
|
+
{ value: "copilot", label: "GitHub Copilot" },
|
|
1244
|
+
{ value: "claude", label: "Claude Code" },
|
|
1245
|
+
{ value: "cursor", label: "Cursor" },
|
|
1246
|
+
{ value: "windsurf", label: "Windsurf" },
|
|
1247
|
+
{ value: "kilocode", label: "Kilocode" },
|
|
1248
|
+
{ value: "opencode", label: "OpenCode" },
|
|
1249
|
+
{ value: "gemini", label: "Gemini CLI" },
|
|
1250
|
+
{ value: "roo", label: "Roo Code" },
|
|
1251
|
+
{ value: "qwen", label: "Qwen Code" },
|
|
1252
|
+
{ value: "codex", label: "Codex CLI" },
|
|
1253
|
+
{ value: "agy", label: "Antigravity (agy)" },
|
|
1254
|
+
{ value: "agents-md", label: "AGENTS.md (cross-tool)" }
|
|
1255
|
+
];
|
|
1256
|
+
var TOOL_FEATURE_VALUES = new Set(TOOL_FEATURES.map((t) => t.value));
|
|
1257
|
+
async function generateCommand(cliOptions) {
|
|
1258
|
+
try {
|
|
1259
|
+
intro2("awa CLI - Template Generator");
|
|
1260
|
+
const fileConfig = await configLoader.load(cliOptions.config ?? null);
|
|
1261
|
+
const options = configLoader.merge(cliOptions, fileConfig);
|
|
1262
|
+
const template = await templateResolver.resolve(options.template, options.refresh);
|
|
1263
|
+
const features = featureResolver.resolve({
|
|
1264
|
+
baseFeatures: [...options.features],
|
|
1265
|
+
presetNames: [...options.preset],
|
|
1266
|
+
removeFeatures: [...options.removeFeatures],
|
|
1267
|
+
presetDefinitions: options.presets
|
|
1268
|
+
});
|
|
1269
|
+
const hasToolFlag = features.some((f) => TOOL_FEATURE_VALUES.has(f));
|
|
1270
|
+
if (!hasToolFlag) {
|
|
1271
|
+
const selected = await multiselect2({
|
|
1272
|
+
message: "Select AI tools to generate for (space to toggle, enter to confirm):",
|
|
1273
|
+
options: TOOL_FEATURES.map((t) => ({ value: t.value, label: t.label })),
|
|
1274
|
+
required: true
|
|
1275
|
+
});
|
|
1276
|
+
if (isCancel2(selected)) {
|
|
1277
|
+
logger.info("Generation cancelled.");
|
|
1278
|
+
process.exit(0);
|
|
1279
|
+
}
|
|
1280
|
+
features.push(...selected);
|
|
1281
|
+
}
|
|
1282
|
+
if (options.dryRun) {
|
|
1283
|
+
logger.info("Running in dry-run mode (no files will be modified)");
|
|
1284
|
+
}
|
|
1285
|
+
if (options.force) {
|
|
1286
|
+
logger.info("Force mode enabled (existing files will be overwritten)");
|
|
1287
|
+
}
|
|
1288
|
+
const result = await fileGenerator.generate({
|
|
1289
|
+
templatePath: template.localPath,
|
|
1290
|
+
outputPath: options.output,
|
|
1291
|
+
features,
|
|
1292
|
+
force: options.force,
|
|
1293
|
+
dryRun: options.dryRun,
|
|
1294
|
+
delete: options.delete
|
|
1295
|
+
});
|
|
1296
|
+
logger.summary(result);
|
|
1297
|
+
outro2("Generation complete!");
|
|
1298
|
+
} catch (error) {
|
|
1299
|
+
if (error instanceof Error) {
|
|
1300
|
+
logger.error(error.message);
|
|
1301
|
+
} else {
|
|
1302
|
+
logger.error(String(error));
|
|
1303
|
+
}
|
|
1304
|
+
process.exit(1);
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
// src/cli/index.ts
|
|
1309
|
+
var version = PACKAGE_INFO.version;
|
|
1310
|
+
var program = new Command();
|
|
1311
|
+
program.name("awa").description("awa - tool for generating AI coding agent configuration files").version(version, "-v, --version", "Display version number");
|
|
1312
|
+
program.command("generate").description("Generate AI agent configuration files from templates").argument("[output]", "Output directory (optional if specified in config)").option("-t, --template <source>", "Template source (local path or Git repository)").option("-f, --features <flag...>", "Feature flags (can be specified multiple times)").option("--preset <name...>", "Preset names to enable (can be specified multiple times)").option(
|
|
1313
|
+
"--remove-features <flag...>",
|
|
1314
|
+
"Feature flags to remove (can be specified multiple times)"
|
|
1315
|
+
).option("--force", "Force overwrite existing files without prompting", false).option("--dry-run", "Preview changes without modifying files", false).option(
|
|
1316
|
+
"--delete",
|
|
1317
|
+
"Enable deletion of files listed in the delete list (default: warn only)",
|
|
1318
|
+
false
|
|
1319
|
+
).option("-c, --config <path>", "Path to configuration file").option("--refresh", "Force refresh of cached Git templates", false).action(async (output, options) => {
|
|
1320
|
+
const cliOptions = {
|
|
1321
|
+
output,
|
|
1322
|
+
template: options.template,
|
|
1323
|
+
features: options.features || [],
|
|
1324
|
+
preset: options.preset || [],
|
|
1325
|
+
removeFeatures: options.removeFeatures || [],
|
|
1326
|
+
force: options.force,
|
|
1327
|
+
dryRun: options.dryRun,
|
|
1328
|
+
delete: options.delete,
|
|
1329
|
+
config: options.config,
|
|
1330
|
+
refresh: options.refresh
|
|
1331
|
+
};
|
|
1332
|
+
await generateCommand(cliOptions);
|
|
1333
|
+
});
|
|
1334
|
+
program.command("diff").description("Compare template output with existing target directory").argument("[target]", "Target directory to compare against (optional if specified in config)").option("-t, --template <source>", "Template source (local path or Git repository)").option("-f, --features <flag...>", "Feature flags (can be specified multiple times)").option("--preset <name...>", "Preset names to enable (can be specified multiple times)").option(
|
|
1335
|
+
"--remove-features <flag...>",
|
|
1336
|
+
"Feature flags to remove (can be specified multiple times)"
|
|
1337
|
+
).option("-c, --config <path>", "Path to configuration file").option("--refresh", "Force refresh of cached Git templates", false).option("--list-unknown", "Include target-only files in diff results", false).action(async (target, options) => {
|
|
1338
|
+
const cliOptions = {
|
|
1339
|
+
output: target,
|
|
1340
|
+
// Use target as output for consistency
|
|
1341
|
+
template: options.template,
|
|
1342
|
+
features: options.features || [],
|
|
1343
|
+
preset: options.preset || [],
|
|
1344
|
+
removeFeatures: options.removeFeatures || [],
|
|
1345
|
+
config: options.config,
|
|
1346
|
+
refresh: options.refresh,
|
|
1347
|
+
listUnknown: options.listUnknown
|
|
1348
|
+
};
|
|
1349
|
+
const exitCode = await diffCommand(cliOptions);
|
|
1350
|
+
process.exit(exitCode);
|
|
1351
|
+
});
|
|
1352
|
+
program.parse();
|
|
1353
|
+
//# sourceMappingURL=index.js.map
|