javi-forge 0.1.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/.gitignore.template +105 -0
- package/.releaserc +44 -0
- package/README.md +45 -0
- package/ai-config/.skillignore +15 -0
- package/ai-config/AUTO_INVOKE.md +300 -0
- package/ai-config/agents/_TEMPLATE.md +93 -0
- package/ai-config/agents/business/api-designer.md +1657 -0
- package/ai-config/agents/business/business-analyst.md +1331 -0
- package/ai-config/agents/business/product-strategist.md +206 -0
- package/ai-config/agents/business/project-manager.md +178 -0
- package/ai-config/agents/business/requirements-analyst.md +1277 -0
- package/ai-config/agents/business/technical-writer.md +1679 -0
- package/ai-config/agents/creative/ux-designer.md +205 -0
- package/ai-config/agents/data-ai/ai-engineer.md +487 -0
- package/ai-config/agents/data-ai/analytics-engineer.md +953 -0
- package/ai-config/agents/data-ai/data-engineer.md +173 -0
- package/ai-config/agents/data-ai/data-scientist.md +672 -0
- package/ai-config/agents/data-ai/mlops-engineer.md +814 -0
- package/ai-config/agents/data-ai/prompt-engineer.md +772 -0
- package/ai-config/agents/development/angular-expert.md +620 -0
- package/ai-config/agents/development/backend-architect.md +795 -0
- package/ai-config/agents/development/database-specialist.md +212 -0
- package/ai-config/agents/development/frontend-specialist.md +686 -0
- package/ai-config/agents/development/fullstack-engineer.md +668 -0
- package/ai-config/agents/development/golang-pro.md +338 -0
- package/ai-config/agents/development/java-enterprise.md +400 -0
- package/ai-config/agents/development/javascript-pro.md +422 -0
- package/ai-config/agents/development/nextjs-pro.md +474 -0
- package/ai-config/agents/development/python-pro.md +570 -0
- package/ai-config/agents/development/react-pro.md +487 -0
- package/ai-config/agents/development/rust-pro.md +246 -0
- package/ai-config/agents/development/spring-boot-4-expert.md +326 -0
- package/ai-config/agents/development/typescript-pro.md +336 -0
- package/ai-config/agents/development/vue-specialist.md +605 -0
- package/ai-config/agents/infrastructure/cloud-architect.md +472 -0
- package/ai-config/agents/infrastructure/deployment-manager.md +358 -0
- package/ai-config/agents/infrastructure/devops-engineer.md +455 -0
- package/ai-config/agents/infrastructure/incident-responder.md +519 -0
- package/ai-config/agents/infrastructure/kubernetes-expert.md +705 -0
- package/ai-config/agents/infrastructure/monitoring-specialist.md +674 -0
- package/ai-config/agents/infrastructure/performance-engineer.md +658 -0
- package/ai-config/agents/orchestrator.md +241 -0
- package/ai-config/agents/quality/accessibility-auditor.md +1204 -0
- package/ai-config/agents/quality/code-reviewer-compact.md +123 -0
- package/ai-config/agents/quality/code-reviewer.md +363 -0
- package/ai-config/agents/quality/dependency-manager.md +743 -0
- package/ai-config/agents/quality/e2e-test-specialist.md +1005 -0
- package/ai-config/agents/quality/performance-tester.md +1086 -0
- package/ai-config/agents/quality/security-auditor.md +133 -0
- package/ai-config/agents/quality/test-engineer.md +453 -0
- package/ai-config/agents/specialists/api-designer.md +87 -0
- package/ai-config/agents/specialists/backend-architect.md +73 -0
- package/ai-config/agents/specialists/code-reviewer.md +77 -0
- package/ai-config/agents/specialists/db-optimizer.md +75 -0
- package/ai-config/agents/specialists/devops-engineer.md +83 -0
- package/ai-config/agents/specialists/documentation-writer.md +78 -0
- package/ai-config/agents/specialists/frontend-developer.md +75 -0
- package/ai-config/agents/specialists/performance-analyst.md +82 -0
- package/ai-config/agents/specialists/refactor-specialist.md +74 -0
- package/ai-config/agents/specialists/security-auditor.md +74 -0
- package/ai-config/agents/specialists/test-engineer.md +81 -0
- package/ai-config/agents/specialists/ux-consultant.md +76 -0
- package/ai-config/agents/specialized/agent-generator.md +1190 -0
- package/ai-config/agents/specialized/blockchain-developer.md +149 -0
- package/ai-config/agents/specialized/code-migrator.md +892 -0
- package/ai-config/agents/specialized/context-manager.md +978 -0
- package/ai-config/agents/specialized/documentation-writer.md +1078 -0
- package/ai-config/agents/specialized/ecommerce-expert.md +1756 -0
- package/ai-config/agents/specialized/embedded-engineer.md +1714 -0
- package/ai-config/agents/specialized/error-detective.md +1034 -0
- package/ai-config/agents/specialized/fintech-specialist.md +1659 -0
- package/ai-config/agents/specialized/freelance-project-planner-v2.md +1988 -0
- package/ai-config/agents/specialized/freelance-project-planner-v3.md +2136 -0
- package/ai-config/agents/specialized/freelance-project-planner-v4.md +4503 -0
- package/ai-config/agents/specialized/freelance-project-planner.md +722 -0
- package/ai-config/agents/specialized/game-developer.md +1963 -0
- package/ai-config/agents/specialized/healthcare-dev.md +1620 -0
- package/ai-config/agents/specialized/mobile-developer.md +188 -0
- package/ai-config/agents/specialized/parallel-plan-executor.md +506 -0
- package/ai-config/agents/specialized/plan-executor.md +485 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/00-INDEX.md +485 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/01-CORE.md +3493 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/02-SELF-CORRECTION.md +778 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/03-PROGRESSIVE-SETUP.md +918 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/04-DEPLOYMENT.md +1537 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/05-TESTING.md +2633 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/06-OPERATIONS.md +5610 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/INSTALL.md +335 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/QUICK-REFERENCE.txt +215 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/README.md +260 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/START-HERE.md +379 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/WORKFLOW-DIAGRAM.md +355 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/solo-dev-planner.md +279 -0
- package/ai-config/agents/specialized/template-writer.md +347 -0
- package/ai-config/agents/specialized/test-runner.md +99 -0
- package/ai-config/agents/specialized/vibekanban-smart-worker.md +244 -0
- package/ai-config/agents/specialized/wave-executor.md +138 -0
- package/ai-config/agents/specialized/workflow-optimizer.md +1114 -0
- package/ai-config/commands/git/changelog.md +32 -0
- package/ai-config/commands/git/ci-local.md +70 -0
- package/ai-config/commands/git/commit.md +35 -0
- package/ai-config/commands/git/fix-issue.md +23 -0
- package/ai-config/commands/git/pr-create.md +42 -0
- package/ai-config/commands/git/pr-review.md +50 -0
- package/ai-config/commands/git/worktree.md +39 -0
- package/ai-config/commands/refactoring/cleanup.md +24 -0
- package/ai-config/commands/refactoring/dead-code.md +40 -0
- package/ai-config/commands/refactoring/extract.md +31 -0
- package/ai-config/commands/testing/e2e.md +30 -0
- package/ai-config/commands/testing/tdd.md +36 -0
- package/ai-config/commands/testing/test-coverage.md +30 -0
- package/ai-config/commands/testing/test-fix.md +24 -0
- package/ai-config/commands/workflow/generate-agents-md.md +85 -0
- package/ai-config/commands/workflow/planning.md +47 -0
- package/ai-config/commands/workflows/compound.md +89 -0
- package/ai-config/commands/workflows/plan.md +77 -0
- package/ai-config/commands/workflows/review.md +78 -0
- package/ai-config/commands/workflows/work.md +75 -0
- package/ai-config/config.yaml +18 -0
- package/ai-config/hooks/_TEMPLATE.md +96 -0
- package/ai-config/hooks/block-dangerous-commands.md +75 -0
- package/ai-config/hooks/commit-guard.md +90 -0
- package/ai-config/hooks/context-loader.md +73 -0
- package/ai-config/hooks/improve-prompt.md +91 -0
- package/ai-config/hooks/learning-log.md +72 -0
- package/ai-config/hooks/model-router.md +86 -0
- package/ai-config/hooks/secret-scanner.md +64 -0
- package/ai-config/hooks/skill-validator.md +102 -0
- package/ai-config/hooks/task-artifact.md +114 -0
- package/ai-config/hooks/validate-workflow.md +100 -0
- package/ai-config/prompts/base.md +71 -0
- package/ai-config/prompts/modes/debug.md +34 -0
- package/ai-config/prompts/modes/deploy.md +40 -0
- package/ai-config/prompts/modes/research.md +32 -0
- package/ai-config/prompts/modes/review.md +33 -0
- package/ai-config/prompts/review-policy.md +79 -0
- package/ai-config/skills/_TEMPLATE.md +157 -0
- package/ai-config/skills/backend/api-gateway/SKILL.md +254 -0
- package/ai-config/skills/backend/bff-concepts/SKILL.md +239 -0
- package/ai-config/skills/backend/bff-spring/SKILL.md +364 -0
- package/ai-config/skills/backend/chi-router/SKILL.md +396 -0
- package/ai-config/skills/backend/error-handling/SKILL.md +255 -0
- package/ai-config/skills/backend/exceptions-spring/SKILL.md +323 -0
- package/ai-config/skills/backend/fastapi/SKILL.md +302 -0
- package/ai-config/skills/backend/gateway-spring/SKILL.md +390 -0
- package/ai-config/skills/backend/go-backend/SKILL.md +457 -0
- package/ai-config/skills/backend/gradle-multimodule/SKILL.md +274 -0
- package/ai-config/skills/backend/graphql-concepts/SKILL.md +352 -0
- package/ai-config/skills/backend/graphql-spring/SKILL.md +398 -0
- package/ai-config/skills/backend/grpc-concepts/SKILL.md +283 -0
- package/ai-config/skills/backend/grpc-spring/SKILL.md +445 -0
- package/ai-config/skills/backend/jwt-auth/SKILL.md +412 -0
- package/ai-config/skills/backend/notifications-concepts/SKILL.md +259 -0
- package/ai-config/skills/backend/recommendations-concepts/SKILL.md +261 -0
- package/ai-config/skills/backend/search-concepts/SKILL.md +263 -0
- package/ai-config/skills/backend/search-spring/SKILL.md +375 -0
- package/ai-config/skills/backend/spring-boot-4/SKILL.md +172 -0
- package/ai-config/skills/backend/websockets/SKILL.md +532 -0
- package/ai-config/skills/data-ai/ai-ml/SKILL.md +423 -0
- package/ai-config/skills/data-ai/analytics-concepts/SKILL.md +195 -0
- package/ai-config/skills/data-ai/analytics-spring/SKILL.md +340 -0
- package/ai-config/skills/data-ai/duckdb-analytics/SKILL.md +440 -0
- package/ai-config/skills/data-ai/langchain/SKILL.md +238 -0
- package/ai-config/skills/data-ai/mlflow/SKILL.md +302 -0
- package/ai-config/skills/data-ai/onnx-inference/SKILL.md +290 -0
- package/ai-config/skills/data-ai/powerbi/SKILL.md +352 -0
- package/ai-config/skills/data-ai/pytorch/SKILL.md +274 -0
- package/ai-config/skills/data-ai/scikit-learn/SKILL.md +321 -0
- package/ai-config/skills/data-ai/vector-db/SKILL.md +301 -0
- package/ai-config/skills/database/graph-databases/SKILL.md +218 -0
- package/ai-config/skills/database/graph-spring/SKILL.md +361 -0
- package/ai-config/skills/database/pgx-postgres/SKILL.md +512 -0
- package/ai-config/skills/database/redis-cache/SKILL.md +343 -0
- package/ai-config/skills/database/sqlite-embedded/SKILL.md +388 -0
- package/ai-config/skills/database/timescaledb/SKILL.md +320 -0
- package/ai-config/skills/docs/api-documentation/SKILL.md +293 -0
- package/ai-config/skills/docs/docs-spring/SKILL.md +377 -0
- package/ai-config/skills/docs/mustache-templates/SKILL.md +190 -0
- package/ai-config/skills/docs/technical-docs/SKILL.md +447 -0
- package/ai-config/skills/frontend/astro-ssr/SKILL.md +441 -0
- package/ai-config/skills/frontend/frontend-design/SKILL.md +54 -0
- package/ai-config/skills/frontend/frontend-web/SKILL.md +368 -0
- package/ai-config/skills/frontend/mantine-ui/SKILL.md +396 -0
- package/ai-config/skills/frontend/tanstack-query/SKILL.md +439 -0
- package/ai-config/skills/frontend/zod-validation/SKILL.md +417 -0
- package/ai-config/skills/frontend/zustand-state/SKILL.md +350 -0
- package/ai-config/skills/infrastructure/chaos-engineering/SKILL.md +244 -0
- package/ai-config/skills/infrastructure/chaos-spring/SKILL.md +378 -0
- package/ai-config/skills/infrastructure/devops-infra/SKILL.md +435 -0
- package/ai-config/skills/infrastructure/docker-containers/SKILL.md +420 -0
- package/ai-config/skills/infrastructure/kubernetes/SKILL.md +456 -0
- package/ai-config/skills/infrastructure/opentelemetry/SKILL.md +546 -0
- package/ai-config/skills/infrastructure/traefik-proxy/SKILL.md +474 -0
- package/ai-config/skills/infrastructure/woodpecker-ci/SKILL.md +315 -0
- package/ai-config/skills/mobile/ionic-capacitor/SKILL.md +504 -0
- package/ai-config/skills/mobile/mobile-ionic/SKILL.md +448 -0
- package/ai-config/skills/prompt-improver/SKILL.md +125 -0
- package/ai-config/skills/quality/ghagga-review/SKILL.md +216 -0
- package/ai-config/skills/references/hooks-patterns/SKILL.md +238 -0
- package/ai-config/skills/references/mcp-servers/SKILL.md +275 -0
- package/ai-config/skills/references/plugins-reference/SKILL.md +110 -0
- package/ai-config/skills/references/skills-reference/SKILL.md +420 -0
- package/ai-config/skills/references/subagent-templates/SKILL.md +193 -0
- package/ai-config/skills/systems-iot/modbus-protocol/SKILL.md +410 -0
- package/ai-config/skills/systems-iot/mqtt-rumqttc/SKILL.md +408 -0
- package/ai-config/skills/systems-iot/rust-systems/SKILL.md +386 -0
- package/ai-config/skills/systems-iot/tokio-async/SKILL.md +324 -0
- package/ai-config/skills/testing/playwright-e2e/SKILL.md +289 -0
- package/ai-config/skills/testing/testcontainers/SKILL.md +299 -0
- package/ai-config/skills/testing/vitest-testing/SKILL.md +381 -0
- package/ai-config/skills/workflow/ci-local-guide/SKILL.md +118 -0
- package/ai-config/skills/workflow/claude-automation-recommender/SKILL.md +299 -0
- package/ai-config/skills/workflow/claude-md-improver/SKILL.md +158 -0
- package/ai-config/skills/workflow/finishing-a-development-branch/SKILL.md +117 -0
- package/ai-config/skills/workflow/git-github/SKILL.md +334 -0
- package/ai-config/skills/workflow/git-github/references/examples.md +160 -0
- package/ai-config/skills/workflow/git-workflow/SKILL.md +214 -0
- package/ai-config/skills/workflow/ide-plugins/SKILL.md +277 -0
- package/ai-config/skills/workflow/ide-plugins-intellij/SKILL.md +401 -0
- package/ai-config/skills/workflow/obsidian-brain-workflow/SKILL.md +199 -0
- package/ai-config/skills/workflow/using-git-worktrees/SKILL.md +100 -0
- package/ai-config/skills/workflow/verification-before-completion/SKILL.md +73 -0
- package/ai-config/skills/workflow/wave-workflow/SKILL.md +178 -0
- package/ci-local/README.md +170 -0
- package/ci-local/ci-local.sh +297 -0
- package/ci-local/hooks/commit-msg +74 -0
- package/ci-local/hooks/pre-commit +162 -0
- package/ci-local/hooks/pre-push +41 -0
- package/ci-local/install.sh +49 -0
- package/ci-local/semgrep.yml +214 -0
- package/dist/commands/analyze.d.ts +9 -0
- package/dist/commands/analyze.d.ts.map +1 -0
- package/dist/commands/analyze.js +55 -0
- package/dist/commands/analyze.js.map +1 -0
- package/dist/commands/analyze.test.d.ts +2 -0
- package/dist/commands/analyze.test.d.ts.map +1 -0
- package/dist/commands/analyze.test.js +145 -0
- package/dist/commands/analyze.test.js.map +1 -0
- package/dist/commands/doctor.d.ts +7 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +158 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/doctor.test.d.ts +2 -0
- package/dist/commands/doctor.test.d.ts.map +1 -0
- package/dist/commands/doctor.test.js +200 -0
- package/dist/commands/doctor.test.js.map +1 -0
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +283 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/init.test.d.ts +2 -0
- package/dist/commands/init.test.d.ts.map +1 -0
- package/dist/commands/init.test.js +271 -0
- package/dist/commands/init.test.js.map +1 -0
- package/dist/commands/sync.d.ts +8 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +201 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/constants.d.ts +21 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +57 -0
- package/dist/constants.js.map +1 -0
- package/dist/e2e/aggressive.e2e.test.d.ts +2 -0
- package/dist/e2e/aggressive.e2e.test.d.ts.map +1 -0
- package/dist/e2e/aggressive.e2e.test.js +350 -0
- package/dist/e2e/aggressive.e2e.test.js.map +1 -0
- package/dist/e2e/commands.e2e.test.d.ts +2 -0
- package/dist/e2e/commands.e2e.test.d.ts.map +1 -0
- package/dist/e2e/commands.e2e.test.js +213 -0
- package/dist/e2e/commands.e2e.test.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +82 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/common.d.ts +17 -0
- package/dist/lib/common.d.ts.map +1 -0
- package/dist/lib/common.js +111 -0
- package/dist/lib/common.js.map +1 -0
- package/dist/lib/common.test.d.ts +2 -0
- package/dist/lib/common.test.d.ts.map +1 -0
- package/dist/lib/common.test.js +316 -0
- package/dist/lib/common.test.js.map +1 -0
- package/dist/lib/frontmatter.d.ts +18 -0
- package/dist/lib/frontmatter.d.ts.map +1 -0
- package/dist/lib/frontmatter.js +61 -0
- package/dist/lib/frontmatter.js.map +1 -0
- package/dist/lib/frontmatter.test.d.ts +2 -0
- package/dist/lib/frontmatter.test.d.ts.map +1 -0
- package/dist/lib/frontmatter.test.js +257 -0
- package/dist/lib/frontmatter.test.js.map +1 -0
- package/dist/lib/template.d.ts +24 -0
- package/dist/lib/template.d.ts.map +1 -0
- package/dist/lib/template.js +78 -0
- package/dist/lib/template.js.map +1 -0
- package/dist/lib/template.test.d.ts +2 -0
- package/dist/lib/template.test.d.ts.map +1 -0
- package/dist/lib/template.test.js +201 -0
- package/dist/lib/template.test.js.map +1 -0
- package/dist/types/index.d.ts +48 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/ui/AnalyzeUI.d.ts +7 -0
- package/dist/ui/AnalyzeUI.d.ts.map +1 -0
- package/dist/ui/AnalyzeUI.js +100 -0
- package/dist/ui/AnalyzeUI.js.map +1 -0
- package/dist/ui/App.d.ts +13 -0
- package/dist/ui/App.d.ts.map +1 -0
- package/dist/ui/App.js +100 -0
- package/dist/ui/App.js.map +1 -0
- package/dist/ui/CIContext.d.ts +9 -0
- package/dist/ui/CIContext.d.ts.map +1 -0
- package/dist/ui/CIContext.js +9 -0
- package/dist/ui/CIContext.js.map +1 -0
- package/dist/ui/CISelector.d.ts +8 -0
- package/dist/ui/CISelector.d.ts.map +1 -0
- package/dist/ui/CISelector.js +45 -0
- package/dist/ui/CISelector.js.map +1 -0
- package/dist/ui/Doctor.d.ts +3 -0
- package/dist/ui/Doctor.d.ts.map +1 -0
- package/dist/ui/Doctor.js +89 -0
- package/dist/ui/Doctor.js.map +1 -0
- package/dist/ui/Header.d.ts +8 -0
- package/dist/ui/Header.d.ts.map +1 -0
- package/dist/ui/Header.js +30 -0
- package/dist/ui/Header.js.map +1 -0
- package/dist/ui/MemorySelector.d.ts +8 -0
- package/dist/ui/MemorySelector.d.ts.map +1 -0
- package/dist/ui/MemorySelector.js +46 -0
- package/dist/ui/MemorySelector.js.map +1 -0
- package/dist/ui/NameInput.d.ts +8 -0
- package/dist/ui/NameInput.d.ts.map +1 -0
- package/dist/ui/NameInput.js +69 -0
- package/dist/ui/NameInput.js.map +1 -0
- package/dist/ui/OptionSelector.d.ts +12 -0
- package/dist/ui/OptionSelector.d.ts.map +1 -0
- package/dist/ui/OptionSelector.js +69 -0
- package/dist/ui/OptionSelector.js.map +1 -0
- package/dist/ui/Progress.d.ts +11 -0
- package/dist/ui/Progress.d.ts.map +1 -0
- package/dist/ui/Progress.js +58 -0
- package/dist/ui/Progress.js.map +1 -0
- package/dist/ui/StackSelector.d.ts +9 -0
- package/dist/ui/StackSelector.d.ts.map +1 -0
- package/dist/ui/StackSelector.js +65 -0
- package/dist/ui/StackSelector.js.map +1 -0
- package/dist/ui/Summary.d.ts +12 -0
- package/dist/ui/Summary.d.ts.map +1 -0
- package/dist/ui/Summary.js +114 -0
- package/dist/ui/Summary.js.map +1 -0
- package/dist/ui/SyncUI.d.ts +10 -0
- package/dist/ui/SyncUI.d.ts.map +1 -0
- package/dist/ui/SyncUI.js +64 -0
- package/dist/ui/SyncUI.js.map +1 -0
- package/dist/ui/Welcome.d.ts +7 -0
- package/dist/ui/Welcome.d.ts.map +1 -0
- package/dist/ui/Welcome.js +45 -0
- package/dist/ui/Welcome.js.map +1 -0
- package/dist/ui/theme.d.ts +10 -0
- package/dist/ui/theme.d.ts.map +1 -0
- package/dist/ui/theme.js +9 -0
- package/dist/ui/theme.js.map +1 -0
- package/modules/engram/.gitignore-snippet.txt +6 -0
- package/modules/engram/.mcp-config-snippet.json +11 -0
- package/modules/engram/README.md +146 -0
- package/modules/engram/install-engram.sh +216 -0
- package/modules/ghagga/.env.example +43 -0
- package/modules/ghagga/README.md +153 -0
- package/modules/ghagga/docker-compose.yml +80 -0
- package/modules/ghagga/setup-ghagga.sh +139 -0
- package/modules/memory-simple/.project/NOTES.md +22 -0
- package/modules/memory-simple/README.md +23 -0
- package/modules/obsidian-brain/.obsidian/app.json +23 -0
- package/modules/obsidian-brain/.obsidian/appearance.json +5 -0
- package/modules/obsidian-brain/.obsidian/bookmarks.json +34 -0
- package/modules/obsidian-brain/.obsidian/community-plugins.json +1 -0
- package/modules/obsidian-brain/.obsidian/core-plugins-migration.json +21 -0
- package/modules/obsidian-brain/.obsidian/core-plugins.json +18 -0
- package/modules/obsidian-brain/.obsidian/daily-notes.json +5 -0
- package/modules/obsidian-brain/.obsidian/graph.json +37 -0
- package/modules/obsidian-brain/.obsidian/hotkeys.json +14 -0
- package/modules/obsidian-brain/.obsidian/plugins/dataview/data.json +25 -0
- package/modules/obsidian-brain/.obsidian/plugins/obsidian-kanban/data.json +29 -0
- package/modules/obsidian-brain/.obsidian/plugins/templater-obsidian/data.json +18 -0
- package/modules/obsidian-brain/.obsidian/snippets/project-memory.css +71 -0
- package/modules/obsidian-brain/.obsidian-gitignore-snippet.txt +8 -0
- package/modules/obsidian-brain/.project/Attachments/.gitkeep +0 -0
- package/modules/obsidian-brain/.project/Memory/BLOCKERS.md +78 -0
- package/modules/obsidian-brain/.project/Memory/CONTEXT.md +102 -0
- package/modules/obsidian-brain/.project/Memory/DASHBOARD.md +73 -0
- package/modules/obsidian-brain/.project/Memory/DECISIONS.md +87 -0
- package/modules/obsidian-brain/.project/Memory/KANBAN.md +15 -0
- package/modules/obsidian-brain/.project/Memory/README.md +61 -0
- package/modules/obsidian-brain/.project/Memory/WAVES.md +78 -0
- package/modules/obsidian-brain/.project/Sessions/TEMPLATE.md +99 -0
- package/modules/obsidian-brain/.project/Templates/ADR.md +33 -0
- package/modules/obsidian-brain/.project/Templates/Blocker.md +21 -0
- package/modules/obsidian-brain/.project/Templates/Session.md +88 -0
- package/modules/obsidian-brain/README.md +268 -0
- package/modules/obsidian-brain/new-wave.sh +182 -0
- package/package.json +51 -0
- package/schemas/agent.schema.json +34 -0
- package/schemas/ai-config.schema.json +28 -0
- package/schemas/skill.schema.json +44 -0
- package/src/commands/analyze.test.ts +145 -0
- package/src/commands/analyze.ts +69 -0
- package/src/commands/doctor.test.ts +208 -0
- package/src/commands/doctor.ts +163 -0
- package/src/commands/init.test.ts +298 -0
- package/src/commands/init.ts +285 -0
- package/src/constants.ts +69 -0
- package/src/e2e/aggressive.e2e.test.ts +557 -0
- package/src/e2e/commands.e2e.test.ts +298 -0
- package/src/index.tsx +106 -0
- package/src/lib/common.test.ts +318 -0
- package/src/lib/common.ts +127 -0
- package/src/lib/frontmatter.test.ts +291 -0
- package/src/lib/frontmatter.ts +77 -0
- package/src/lib/template.test.ts +226 -0
- package/src/lib/template.ts +99 -0
- package/src/types/index.ts +53 -0
- package/src/ui/AnalyzeUI.tsx +133 -0
- package/src/ui/App.tsx +175 -0
- package/src/ui/CIContext.tsx +25 -0
- package/src/ui/CISelector.tsx +72 -0
- package/src/ui/Doctor.tsx +122 -0
- package/src/ui/Header.tsx +48 -0
- package/src/ui/MemorySelector.tsx +73 -0
- package/src/ui/NameInput.tsx +82 -0
- package/src/ui/OptionSelector.tsx +100 -0
- package/src/ui/Progress.tsx +88 -0
- package/src/ui/StackSelector.tsx +101 -0
- package/src/ui/Summary.tsx +134 -0
- package/src/ui/Welcome.tsx +54 -0
- package/src/ui/theme.ts +10 -0
- package/stryker.config.json +19 -0
- package/tasks/_TEMPLATE/files-edited.md +3 -0
- package/tasks/_TEMPLATE/plan.md +3 -0
- package/tasks/_TEMPLATE/research.md +3 -0
- package/tasks/_TEMPLATE/verification.md +5 -0
- package/templates/common/dependabot/cargo.yml +11 -0
- package/templates/common/dependabot/github-actions.yml +16 -0
- package/templates/common/dependabot/gomod.yml +15 -0
- package/templates/common/dependabot/gradle.yml +15 -0
- package/templates/common/dependabot/header.yml +3 -0
- package/templates/common/dependabot/maven.yml +15 -0
- package/templates/common/dependabot/npm.yml +20 -0
- package/templates/common/dependabot/pip.yml +11 -0
- package/templates/dependabot.yml +162 -0
- package/templates/github/ci-go.yml +41 -0
- package/templates/github/ci-java.yml +45 -0
- package/templates/github/ci-monorepo.yml +150 -0
- package/templates/github/ci-node.yml +42 -0
- package/templates/github/ci-python.yml +42 -0
- package/templates/github/ci-rust.yml +42 -0
- package/templates/github/dependabot-automerge.yml +40 -0
- package/templates/gitlab/gitlab-ci-go.yml +88 -0
- package/templates/gitlab/gitlab-ci-java.yml +79 -0
- package/templates/gitlab/gitlab-ci-monorepo.yml +126 -0
- package/templates/gitlab/gitlab-ci-node.yml +63 -0
- package/templates/gitlab/gitlab-ci-python.yml +147 -0
- package/templates/gitlab/gitlab-ci-rust.yml +67 -0
- package/templates/global/claude-settings.json +98 -0
- package/templates/global/codex-config.toml +8 -0
- package/templates/global/copilot-instructions/base-rules.instructions.md +13 -0
- package/templates/global/copilot-instructions/sdd-orchestrator.instructions.md +37 -0
- package/templates/global/gemini-commands/cleanup.toml +20 -0
- package/templates/global/gemini-commands/commit.toml +15 -0
- package/templates/global/gemini-commands/dead-code.toml +22 -0
- package/templates/global/gemini-commands/plan.toml +30 -0
- package/templates/global/gemini-commands/review.toml +17 -0
- package/templates/global/gemini-commands/sdd-apply.toml +22 -0
- package/templates/global/gemini-commands/sdd-ff.toml +14 -0
- package/templates/global/gemini-commands/sdd-new.toml +21 -0
- package/templates/global/gemini-commands/sdd-verify.toml +21 -0
- package/templates/global/gemini-commands/tdd.toml +26 -0
- package/templates/global/gemini-settings.json +8 -0
- package/templates/global/opencode-config.json +44 -0
- package/templates/global/sdd-instructions.md +47 -0
- package/templates/global/sdd-orchestrator-claude.md +46 -0
- package/templates/global/sdd-orchestrator-copilot.md +34 -0
- package/templates/renovate.json +69 -0
- package/templates/woodpecker/monorepo/backend.yml +34 -0
- package/templates/woodpecker/monorepo/frontend.yml +34 -0
- package/templates/woodpecker/monorepo/summary.yml +25 -0
- package/templates/woodpecker/woodpecker-go.yml +51 -0
- package/templates/woodpecker/woodpecker-java.yml +67 -0
- package/templates/woodpecker/woodpecker-node.yml +47 -0
- package/templates/woodpecker/woodpecker-python.yml +108 -0
- package/templates/woodpecker/woodpecker-rust.yml +57 -0
- package/tsconfig.json +19 -0
- package/vitest.config.ts +16 -0
- package/workflows/reusable-build-go.yml +111 -0
- package/workflows/reusable-build-java.yml +120 -0
- package/workflows/reusable-build-node.yml +145 -0
- package/workflows/reusable-build-python.yml +159 -0
- package/workflows/reusable-build-rust.yml +135 -0
- package/workflows/reusable-docker.yml +120 -0
- package/workflows/reusable-ghagga-review.yml +165 -0
- package/workflows/reusable-release.yml +91 -0
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aggressive E2E tests for javi-forge init command.
|
|
3
|
+
*
|
|
4
|
+
* These tests do REAL filesystem operations in sandboxed temp directories.
|
|
5
|
+
* They verify that `init` actually creates the expected project structure,
|
|
6
|
+
* files, and content — not dry-run.
|
|
7
|
+
*
|
|
8
|
+
* Prerequisites: `pnpm build` must be run before these tests.
|
|
9
|
+
*/
|
|
10
|
+
import { execFile } from 'child_process'
|
|
11
|
+
import { promisify } from 'util'
|
|
12
|
+
import path from 'path'
|
|
13
|
+
import os from 'os'
|
|
14
|
+
import fs from 'fs-extra'
|
|
15
|
+
import crypto from 'crypto'
|
|
16
|
+
import { describe, it, expect, afterEach } from 'vitest'
|
|
17
|
+
|
|
18
|
+
const execFileAsync = promisify(execFile)
|
|
19
|
+
const CLI_PATH = path.resolve(__dirname, '../../dist/index.js')
|
|
20
|
+
|
|
21
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
const sandboxes: string[] = []
|
|
24
|
+
|
|
25
|
+
async function createSandbox(): Promise<string> {
|
|
26
|
+
const dir = path.join(os.tmpdir(), `javi-forge-aggressive-${crypto.randomUUID()}`)
|
|
27
|
+
await fs.ensureDir(dir)
|
|
28
|
+
sandboxes.push(dir)
|
|
29
|
+
return dir
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
afterEach(async () => {
|
|
33
|
+
for (const dir of sandboxes) {
|
|
34
|
+
await fs.remove(dir).catch(() => {})
|
|
35
|
+
}
|
|
36
|
+
sandboxes.length = 0
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Run the CLI for real (no --dry-run) in a sandbox.
|
|
41
|
+
* Always sets CI=1 and --batch for non-interactive mode.
|
|
42
|
+
* Defaults: --no-ai-sync by NOT including aiSync (OptionSelector auto-confirms
|
|
43
|
+
* with defaults which includes aiSync, so we accept javi-ai errors gracefully).
|
|
44
|
+
*/
|
|
45
|
+
async function runInit(
|
|
46
|
+
args: string[],
|
|
47
|
+
cwd: string,
|
|
48
|
+
timeout = 60_000
|
|
49
|
+
): Promise<{ stdout: string; stderr: string; exitCode: number }> {
|
|
50
|
+
try {
|
|
51
|
+
const { stdout, stderr } = await execFileAsync(
|
|
52
|
+
'node',
|
|
53
|
+
[CLI_PATH, 'init', '--batch', ...args],
|
|
54
|
+
{
|
|
55
|
+
timeout,
|
|
56
|
+
cwd,
|
|
57
|
+
env: { ...process.env, FORCE_COLOR: '0', CI: '1' },
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
return { stdout, stderr, exitCode: 0 }
|
|
61
|
+
} catch (e: any) {
|
|
62
|
+
return {
|
|
63
|
+
stdout: e.stdout ?? '',
|
|
64
|
+
stderr: e.stderr ?? '',
|
|
65
|
+
exitCode: e.code ?? 1,
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Get the project directory inside a sandbox */
|
|
71
|
+
function projectDir(sandbox: string, name: string): string {
|
|
72
|
+
return path.join(sandbox, name)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Check if a file exists in the project */
|
|
76
|
+
async function fileExists(sandbox: string, name: string, ...segments: string[]): Promise<boolean> {
|
|
77
|
+
return fs.pathExists(path.join(sandbox, name, ...segments))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Read a file from the project */
|
|
81
|
+
async function readProjectFile(sandbox: string, name: string, ...segments: string[]): Promise<string> {
|
|
82
|
+
return fs.readFile(path.join(sandbox, name, ...segments), 'utf-8')
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ── Project creation tests ───────────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
describe('Project creation: init creates complete project', () => {
|
|
88
|
+
it('init --stack node --ci github: creates complete project structure', async () => {
|
|
89
|
+
const sandbox = await createSandbox()
|
|
90
|
+
const { exitCode } = await runInit(
|
|
91
|
+
['--project-name', 'test-app', '--stack', 'node', '--ci', 'github', '--memory', 'none'],
|
|
92
|
+
sandbox
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
expect(exitCode).toBe(0)
|
|
96
|
+
|
|
97
|
+
// .git/ directory (git initialized)
|
|
98
|
+
expect(await fileExists(sandbox, 'test-app', '.git')).toBe(true)
|
|
99
|
+
|
|
100
|
+
// .github/workflows/ci.yml (CI template)
|
|
101
|
+
expect(await fileExists(sandbox, 'test-app', '.github', 'workflows', 'ci.yml')).toBe(true)
|
|
102
|
+
|
|
103
|
+
// .github/dependabot.yml (dependabot config)
|
|
104
|
+
expect(await fileExists(sandbox, 'test-app', '.github', 'dependabot.yml')).toBe(true)
|
|
105
|
+
|
|
106
|
+
// .gitignore (non-empty)
|
|
107
|
+
expect(await fileExists(sandbox, 'test-app', '.gitignore')).toBe(true)
|
|
108
|
+
const gitignore = await readProjectFile(sandbox, 'test-app', '.gitignore')
|
|
109
|
+
expect(gitignore.length).toBeGreaterThan(0)
|
|
110
|
+
|
|
111
|
+
// .javi-forge/manifest.json (valid JSON)
|
|
112
|
+
expect(await fileExists(sandbox, 'test-app', '.javi-forge', 'manifest.json')).toBe(true)
|
|
113
|
+
const manifest = await fs.readJson(path.join(sandbox, 'test-app', '.javi-forge', 'manifest.json'))
|
|
114
|
+
expect(manifest).toBeDefined()
|
|
115
|
+
expect(manifest.stack).toBe('node')
|
|
116
|
+
})
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
// ── CI content per stack ─────────────────────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
describe('CI content per stack', () => {
|
|
122
|
+
it('init --stack python --ci github: CI contains Python content', async () => {
|
|
123
|
+
const sandbox = await createSandbox()
|
|
124
|
+
await runInit(
|
|
125
|
+
['--project-name', 'py-app', '--stack', 'python', '--ci', 'github', '--memory', 'none'],
|
|
126
|
+
sandbox
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
const ciContent = await readProjectFile(sandbox, 'py-app', '.github', 'workflows', 'ci.yml')
|
|
130
|
+
const lower = ciContent.toLowerCase()
|
|
131
|
+
expect(
|
|
132
|
+
lower.includes('pytest') || lower.includes('pip') || lower.includes('python')
|
|
133
|
+
).toBe(true)
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('init --stack go --ci github: CI contains Go content', async () => {
|
|
137
|
+
const sandbox = await createSandbox()
|
|
138
|
+
await runInit(
|
|
139
|
+
['--project-name', 'go-app', '--stack', 'go', '--ci', 'github', '--memory', 'none'],
|
|
140
|
+
sandbox
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
const ciContent = await readProjectFile(sandbox, 'go-app', '.github', 'workflows', 'ci.yml')
|
|
144
|
+
const lower = ciContent.toLowerCase()
|
|
145
|
+
expect(
|
|
146
|
+
lower.includes('go test') || lower.includes('golangci') || lower.includes('go-version')
|
|
147
|
+
).toBe(true)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
it('init --stack java-gradle --ci github: CI contains Gradle content', async () => {
|
|
151
|
+
const sandbox = await createSandbox()
|
|
152
|
+
await runInit(
|
|
153
|
+
['--project-name', 'java-app', '--stack', 'java-gradle', '--ci', 'github', '--memory', 'none'],
|
|
154
|
+
sandbox
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
const ciContent = await readProjectFile(sandbox, 'java-app', '.github', 'workflows', 'ci.yml')
|
|
158
|
+
const lower = ciContent.toLowerCase()
|
|
159
|
+
expect(
|
|
160
|
+
lower.includes('gradle') || lower.includes('java') || lower.includes('jdk')
|
|
161
|
+
).toBe(true)
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('init --stack rust --ci github: CI contains Rust/Cargo content', async () => {
|
|
165
|
+
const sandbox = await createSandbox()
|
|
166
|
+
await runInit(
|
|
167
|
+
['--project-name', 'rust-app', '--stack', 'rust', '--ci', 'github', '--memory', 'none'],
|
|
168
|
+
sandbox
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
const ciContent = await readProjectFile(sandbox, 'rust-app', '.github', 'workflows', 'ci.yml')
|
|
172
|
+
const lower = ciContent.toLowerCase()
|
|
173
|
+
expect(
|
|
174
|
+
lower.includes('cargo') || lower.includes('clippy') || lower.includes('rust')
|
|
175
|
+
).toBe(true)
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
// ── CI providers ─────────────────────────────────────────────────────────────
|
|
180
|
+
|
|
181
|
+
describe('CI providers', () => {
|
|
182
|
+
it('init --ci gitlab: creates .gitlab-ci.yml', async () => {
|
|
183
|
+
const sandbox = await createSandbox()
|
|
184
|
+
await runInit(
|
|
185
|
+
['--project-name', 'gl-app', '--stack', 'node', '--ci', 'gitlab', '--memory', 'none'],
|
|
186
|
+
sandbox
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
expect(await fileExists(sandbox, 'gl-app', '.gitlab-ci.yml')).toBe(true)
|
|
190
|
+
// Should NOT have GitHub workflows
|
|
191
|
+
expect(await fileExists(sandbox, 'gl-app', '.github', 'workflows')).toBe(false)
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
it('init --ci woodpecker: creates .woodpecker.yml', async () => {
|
|
195
|
+
const sandbox = await createSandbox()
|
|
196
|
+
await runInit(
|
|
197
|
+
['--project-name', 'wp-app', '--stack', 'node', '--ci', 'woodpecker', '--memory', 'none'],
|
|
198
|
+
sandbox
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
const hasYml = await fileExists(sandbox, 'wp-app', '.woodpecker.yml')
|
|
202
|
+
const hasDir = await fileExists(sandbox, 'wp-app', '.woodpecker')
|
|
203
|
+
expect(hasYml || hasDir).toBe(true)
|
|
204
|
+
})
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
// ── Memory module tests ──────────────────────────────────────────────────────
|
|
208
|
+
|
|
209
|
+
describe('Memory modules', () => {
|
|
210
|
+
it('init --memory engram: installs engram module', async () => {
|
|
211
|
+
const sandbox = await createSandbox()
|
|
212
|
+
await runInit(
|
|
213
|
+
['--project-name', 'eng-app', '--stack', 'node', '--ci', 'github', '--memory', 'engram'],
|
|
214
|
+
sandbox
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
// Engram module files copied to .javi-forge/modules/engram/
|
|
218
|
+
expect(await fileExists(sandbox, 'eng-app', '.javi-forge', 'modules', 'engram')).toBe(true)
|
|
219
|
+
|
|
220
|
+
// .mcp-config-snippet.json copied to project root
|
|
221
|
+
expect(await fileExists(sandbox, 'eng-app', '.mcp-config-snippet.json')).toBe(true)
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
it('init --memory obsidian-brain: installs obsidian brain module', async () => {
|
|
225
|
+
const sandbox = await createSandbox()
|
|
226
|
+
await runInit(
|
|
227
|
+
['--project-name', 'obs-app', '--stack', 'node', '--ci', 'github', '--memory', 'obsidian-brain'],
|
|
228
|
+
sandbox
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
// Module copied to .javi-forge/modules/obsidian-brain/
|
|
232
|
+
expect(await fileExists(sandbox, 'obs-app', '.javi-forge', 'modules', 'obsidian-brain')).toBe(true)
|
|
233
|
+
|
|
234
|
+
// .project/Memory structure inside the module copy
|
|
235
|
+
expect(
|
|
236
|
+
await fileExists(sandbox, 'obs-app', '.javi-forge', 'modules', 'obsidian-brain', '.project', 'Memory', 'CONTEXT.md')
|
|
237
|
+
).toBe(true)
|
|
238
|
+
expect(
|
|
239
|
+
await fileExists(sandbox, 'obs-app', '.javi-forge', 'modules', 'obsidian-brain', '.project', 'Memory', 'KANBAN.md')
|
|
240
|
+
).toBe(true)
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
it('init --memory memory-simple: installs simple memory module', async () => {
|
|
244
|
+
const sandbox = await createSandbox()
|
|
245
|
+
await runInit(
|
|
246
|
+
['--project-name', 'sim-app', '--stack', 'node', '--ci', 'github', '--memory', 'memory-simple'],
|
|
247
|
+
sandbox
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
// Module copied to .javi-forge/modules/memory-simple/
|
|
251
|
+
expect(await fileExists(sandbox, 'sim-app', '.javi-forge', 'modules', 'memory-simple')).toBe(true)
|
|
252
|
+
|
|
253
|
+
// .project/NOTES.md inside the module copy
|
|
254
|
+
expect(
|
|
255
|
+
await fileExists(sandbox, 'sim-app', '.javi-forge', 'modules', 'memory-simple', '.project', 'NOTES.md')
|
|
256
|
+
).toBe(true)
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
it('init --memory none: no memory module installed', async () => {
|
|
260
|
+
const sandbox = await createSandbox()
|
|
261
|
+
await runInit(
|
|
262
|
+
['--project-name', 'no-mem', '--stack', 'node', '--ci', 'github', '--memory', 'none'],
|
|
263
|
+
sandbox
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
// No modules directory for memory
|
|
267
|
+
const modulesDir = path.join(sandbox, 'no-mem', '.javi-forge', 'modules')
|
|
268
|
+
const hasModulesDir = await fs.pathExists(modulesDir)
|
|
269
|
+
if (hasModulesDir) {
|
|
270
|
+
const entries = await fs.readdir(modulesDir)
|
|
271
|
+
// Should not contain engram, obsidian-brain, or memory-simple
|
|
272
|
+
const memoryModules = entries.filter(e =>
|
|
273
|
+
['engram', 'obsidian-brain', 'memory-simple'].includes(e)
|
|
274
|
+
)
|
|
275
|
+
expect(memoryModules).toHaveLength(0)
|
|
276
|
+
}
|
|
277
|
+
})
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
// ── Dependabot ecosystem tests ───────────────────────────────────────────────
|
|
281
|
+
|
|
282
|
+
describe('Dependabot ecosystems', () => {
|
|
283
|
+
it('node project: dependabot has npm ecosystem', async () => {
|
|
284
|
+
const sandbox = await createSandbox()
|
|
285
|
+
await runInit(
|
|
286
|
+
['--project-name', 'dep-node', '--stack', 'node', '--ci', 'github', '--memory', 'none'],
|
|
287
|
+
sandbox
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
const content = await readProjectFile(sandbox, 'dep-node', '.github', 'dependabot.yml')
|
|
291
|
+
expect(content).toContain('npm')
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
it('python project: dependabot has pip ecosystem', async () => {
|
|
295
|
+
const sandbox = await createSandbox()
|
|
296
|
+
await runInit(
|
|
297
|
+
['--project-name', 'dep-py', '--stack', 'python', '--ci', 'github', '--memory', 'none'],
|
|
298
|
+
sandbox
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
const content = await readProjectFile(sandbox, 'dep-py', '.github', 'dependabot.yml')
|
|
302
|
+
expect(content).toContain('pip')
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
it('go project: dependabot has gomod ecosystem', async () => {
|
|
306
|
+
const sandbox = await createSandbox()
|
|
307
|
+
await runInit(
|
|
308
|
+
['--project-name', 'dep-go', '--stack', 'go', '--ci', 'github', '--memory', 'none'],
|
|
309
|
+
sandbox
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
const content = await readProjectFile(sandbox, 'dep-go', '.github', 'dependabot.yml')
|
|
313
|
+
expect(content).toContain('gomod')
|
|
314
|
+
})
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
// ── SDD tests ────────────────────────────────────────────────────────────────
|
|
318
|
+
|
|
319
|
+
describe('SDD (Spec-Driven Development)', () => {
|
|
320
|
+
it('init with SDD default (CI auto-confirms with sdd=true): creates openspec/', async () => {
|
|
321
|
+
const sandbox = await createSandbox()
|
|
322
|
+
// In CI/batch mode, OptionSelector defaults to sdd=true
|
|
323
|
+
await runInit(
|
|
324
|
+
['--project-name', 'sdd-app', '--stack', 'node', '--ci', 'github', '--memory', 'none'],
|
|
325
|
+
sandbox
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
expect(await fileExists(sandbox, 'sdd-app', 'openspec')).toBe(true)
|
|
329
|
+
expect(await fileExists(sandbox, 'sdd-app', 'openspec', 'README.md')).toBe(true)
|
|
330
|
+
})
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
// ── GHAGGA tests ─────────────────────────────────────────────────────────────
|
|
334
|
+
|
|
335
|
+
describe('GHAGGA review system', () => {
|
|
336
|
+
it('init --ghagga --ci github: creates ghagga workflow', async () => {
|
|
337
|
+
const sandbox = await createSandbox()
|
|
338
|
+
await runInit(
|
|
339
|
+
['--project-name', 'ghagga-app', '--stack', 'node', '--ci', 'github', '--memory', 'none', '--ghagga'],
|
|
340
|
+
sandbox
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
// GHAGGA module installed
|
|
344
|
+
expect(await fileExists(sandbox, 'ghagga-app', '.javi-forge', 'modules', 'ghagga')).toBe(true)
|
|
345
|
+
|
|
346
|
+
// Ghagga workflow file
|
|
347
|
+
const workflowDir = path.join(sandbox, 'ghagga-app', '.github', 'workflows')
|
|
348
|
+
if (await fs.pathExists(workflowDir)) {
|
|
349
|
+
const files = await fs.readdir(workflowDir)
|
|
350
|
+
const hasGhaggaWorkflow = files.some(f => f.toLowerCase().includes('ghagga'))
|
|
351
|
+
expect(hasGhaggaWorkflow).toBe(true)
|
|
352
|
+
}
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
it('init without --ghagga: no ghagga module or workflow', async () => {
|
|
356
|
+
const sandbox = await createSandbox()
|
|
357
|
+
await runInit(
|
|
358
|
+
['--project-name', 'no-ghagga', '--stack', 'node', '--ci', 'github', '--memory', 'none'],
|
|
359
|
+
sandbox
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
// No ghagga module
|
|
363
|
+
expect(
|
|
364
|
+
await fileExists(sandbox, 'no-ghagga', '.javi-forge', 'modules', 'ghagga')
|
|
365
|
+
).toBe(false)
|
|
366
|
+
|
|
367
|
+
// No ghagga workflow
|
|
368
|
+
const workflowDir = path.join(sandbox, 'no-ghagga', '.github', 'workflows')
|
|
369
|
+
if (await fs.pathExists(workflowDir)) {
|
|
370
|
+
const files = await fs.readdir(workflowDir)
|
|
371
|
+
const hasGhaggaWorkflow = files.some(f => f.toLowerCase().includes('ghagga'))
|
|
372
|
+
expect(hasGhaggaWorkflow).toBe(false)
|
|
373
|
+
}
|
|
374
|
+
})
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
// ── Manifest tests ───────────────────────────────────────────────────────────
|
|
378
|
+
|
|
379
|
+
describe('Manifest metadata', () => {
|
|
380
|
+
it('manifest contains correct metadata fields', async () => {
|
|
381
|
+
const sandbox = await createSandbox()
|
|
382
|
+
await runInit(
|
|
383
|
+
['--project-name', 'meta-app', '--stack', 'python', '--ci', 'github', '--memory', 'engram'],
|
|
384
|
+
sandbox
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
const manifest = await fs.readJson(
|
|
388
|
+
path.join(sandbox, 'meta-app', '.javi-forge', 'manifest.json')
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
expect(manifest).toHaveProperty('stack')
|
|
392
|
+
expect(manifest).toHaveProperty('ciProvider')
|
|
393
|
+
expect(manifest).toHaveProperty('memory')
|
|
394
|
+
expect(manifest).toHaveProperty('createdAt')
|
|
395
|
+
expect(manifest.stack).toBe('python')
|
|
396
|
+
expect(manifest.ciProvider).toBe('github')
|
|
397
|
+
expect(manifest.memory).toBe('engram')
|
|
398
|
+
expect(manifest.projectName).toBe('meta-app')
|
|
399
|
+
})
|
|
400
|
+
|
|
401
|
+
it('different stacks produce different manifests', async () => {
|
|
402
|
+
const sandbox = await createSandbox()
|
|
403
|
+
|
|
404
|
+
await runInit(
|
|
405
|
+
['--project-name', 'proj-node', '--stack', 'node', '--ci', 'github', '--memory', 'none'],
|
|
406
|
+
sandbox
|
|
407
|
+
)
|
|
408
|
+
await runInit(
|
|
409
|
+
['--project-name', 'proj-go', '--stack', 'go', '--ci', 'github', '--memory', 'none'],
|
|
410
|
+
sandbox
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
const manifestNode = await fs.readJson(
|
|
414
|
+
path.join(sandbox, 'proj-node', '.javi-forge', 'manifest.json')
|
|
415
|
+
)
|
|
416
|
+
const manifestGo = await fs.readJson(
|
|
417
|
+
path.join(sandbox, 'proj-go', '.javi-forge', 'manifest.json')
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
expect(manifestNode.stack).toBe('node')
|
|
421
|
+
expect(manifestGo.stack).toBe('go')
|
|
422
|
+
expect(manifestNode.stack).not.toBe(manifestGo.stack)
|
|
423
|
+
})
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
// ── Gitignore tests ──────────────────────────────────────────────────────────
|
|
427
|
+
|
|
428
|
+
describe('Gitignore generation', () => {
|
|
429
|
+
it('generated .gitignore is non-empty with at least 5 lines', async () => {
|
|
430
|
+
const sandbox = await createSandbox()
|
|
431
|
+
await runInit(
|
|
432
|
+
['--project-name', 'gi-app', '--stack', 'node', '--ci', 'github', '--memory', 'none'],
|
|
433
|
+
sandbox
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
const content = await readProjectFile(sandbox, 'gi-app', '.gitignore')
|
|
437
|
+
const lines = content.split('\n').filter(l => l.trim().length > 0)
|
|
438
|
+
expect(lines.length).toBeGreaterThanOrEqual(5)
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
it('generated .gitignore contains common patterns', async () => {
|
|
442
|
+
const sandbox = await createSandbox()
|
|
443
|
+
await runInit(
|
|
444
|
+
['--project-name', 'gi-app2', '--stack', 'node', '--ci', 'github', '--memory', 'none'],
|
|
445
|
+
sandbox
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
const content = await readProjectFile(sandbox, 'gi-app2', '.gitignore')
|
|
449
|
+
// The template has common patterns like .env, .idea, .DS_Store
|
|
450
|
+
expect(
|
|
451
|
+
content.includes('.env') ||
|
|
452
|
+
content.includes('node_modules') ||
|
|
453
|
+
content.includes('.DS_Store') ||
|
|
454
|
+
content.includes('.idea')
|
|
455
|
+
).toBe(true)
|
|
456
|
+
})
|
|
457
|
+
})
|
|
458
|
+
|
|
459
|
+
// ── Idempotency tests ────────────────────────────────────────────────────────
|
|
460
|
+
|
|
461
|
+
describe('Idempotency', () => {
|
|
462
|
+
it('init in already-initialized project does not crash on second run', async () => {
|
|
463
|
+
const sandbox = await createSandbox()
|
|
464
|
+
|
|
465
|
+
// First run
|
|
466
|
+
const first = await runInit(
|
|
467
|
+
['--project-name', 'idem-app', '--stack', 'node', '--ci', 'github', '--memory', 'none'],
|
|
468
|
+
sandbox
|
|
469
|
+
)
|
|
470
|
+
expect(first.exitCode).toBe(0)
|
|
471
|
+
|
|
472
|
+
// Second run — same project name in same sandbox
|
|
473
|
+
const second = await runInit(
|
|
474
|
+
['--project-name', 'idem-app', '--stack', 'node', '--ci', 'github', '--memory', 'none'],
|
|
475
|
+
sandbox
|
|
476
|
+
)
|
|
477
|
+
// Should not crash — exits 0
|
|
478
|
+
expect(second.exitCode).toBe(0)
|
|
479
|
+
|
|
480
|
+
// Project should still be valid
|
|
481
|
+
expect(await fileExists(sandbox, 'idem-app', '.javi-forge', 'manifest.json')).toBe(true)
|
|
482
|
+
})
|
|
483
|
+
})
|
|
484
|
+
|
|
485
|
+
// ── CI Local tests ───────────────────────────────────────────────────────────
|
|
486
|
+
|
|
487
|
+
describe('CI Local setup', () => {
|
|
488
|
+
it('init creates ci-local if source dir exists', async () => {
|
|
489
|
+
const sandbox = await createSandbox()
|
|
490
|
+
await runInit(
|
|
491
|
+
['--project-name', 'ci-app', '--stack', 'node', '--ci', 'github', '--memory', 'none'],
|
|
492
|
+
sandbox
|
|
493
|
+
)
|
|
494
|
+
|
|
495
|
+
// ci-local dir should exist (copied from forge root)
|
|
496
|
+
const ciLocalDir = path.join(sandbox, 'ci-app', 'ci-local')
|
|
497
|
+
const hasCILocal = await fs.pathExists(ciLocalDir)
|
|
498
|
+
if (hasCILocal) {
|
|
499
|
+
// Should have ci-local.sh or hooks
|
|
500
|
+
const hasCIScript = await fs.pathExists(path.join(ciLocalDir, 'ci-local.sh'))
|
|
501
|
+
const hasHooks = await fs.pathExists(path.join(ciLocalDir, 'hooks'))
|
|
502
|
+
expect(hasCIScript || hasHooks).toBe(true)
|
|
503
|
+
}
|
|
504
|
+
// If ci-local source doesn't exist at forge root, this step is skipped — that's OK
|
|
505
|
+
expect(true).toBe(true)
|
|
506
|
+
})
|
|
507
|
+
})
|
|
508
|
+
|
|
509
|
+
// ── Cross-stack CI generation ────────────────────────────────────────────────
|
|
510
|
+
|
|
511
|
+
describe('Cross-stack CI generation', () => {
|
|
512
|
+
const stacks = ['node', 'python', 'go', 'rust', 'java-gradle'] as const
|
|
513
|
+
|
|
514
|
+
for (const stack of stacks) {
|
|
515
|
+
it(`init --stack ${stack} --ci github: generates CI workflow file`, async () => {
|
|
516
|
+
const sandbox = await createSandbox()
|
|
517
|
+
const name = `ci-${stack}`
|
|
518
|
+
await runInit(
|
|
519
|
+
['--project-name', name, '--stack', stack, '--ci', 'github', '--memory', 'none'],
|
|
520
|
+
sandbox
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
expect(await fileExists(sandbox, name, '.github', 'workflows', 'ci.yml')).toBe(true)
|
|
524
|
+
const content = await readProjectFile(sandbox, name, '.github', 'workflows', 'ci.yml')
|
|
525
|
+
expect(content.length).toBeGreaterThan(10)
|
|
526
|
+
})
|
|
527
|
+
}
|
|
528
|
+
})
|
|
529
|
+
|
|
530
|
+
// ── Dependabot with github-actions fragment ──────────────────────────────────
|
|
531
|
+
|
|
532
|
+
describe('Dependabot includes github-actions', () => {
|
|
533
|
+
it('dependabot.yml contains github-actions update section', async () => {
|
|
534
|
+
const sandbox = await createSandbox()
|
|
535
|
+
await runInit(
|
|
536
|
+
['--project-name', 'dbot-app', '--stack', 'node', '--ci', 'github', '--memory', 'none'],
|
|
537
|
+
sandbox
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
const content = await readProjectFile(sandbox, 'dbot-app', '.github', 'dependabot.yml')
|
|
541
|
+
expect(content).toContain('github-actions')
|
|
542
|
+
})
|
|
543
|
+
})
|
|
544
|
+
|
|
545
|
+
// ── GitLab does not create dependabot ────────────────────────────────────────
|
|
546
|
+
|
|
547
|
+
describe('Non-GitHub CI skips dependabot', () => {
|
|
548
|
+
it('gitlab CI does not create .github/dependabot.yml', async () => {
|
|
549
|
+
const sandbox = await createSandbox()
|
|
550
|
+
await runInit(
|
|
551
|
+
['--project-name', 'gl-nodep', '--stack', 'node', '--ci', 'gitlab', '--memory', 'none'],
|
|
552
|
+
sandbox
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
expect(await fileExists(sandbox, 'gl-nodep', '.github', 'dependabot.yml')).toBe(false)
|
|
556
|
+
})
|
|
557
|
+
})
|