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.
Files changed (500) hide show
  1. package/.gitignore.template +105 -0
  2. package/.releaserc +44 -0
  3. package/README.md +45 -0
  4. package/ai-config/.skillignore +15 -0
  5. package/ai-config/AUTO_INVOKE.md +300 -0
  6. package/ai-config/agents/_TEMPLATE.md +93 -0
  7. package/ai-config/agents/business/api-designer.md +1657 -0
  8. package/ai-config/agents/business/business-analyst.md +1331 -0
  9. package/ai-config/agents/business/product-strategist.md +206 -0
  10. package/ai-config/agents/business/project-manager.md +178 -0
  11. package/ai-config/agents/business/requirements-analyst.md +1277 -0
  12. package/ai-config/agents/business/technical-writer.md +1679 -0
  13. package/ai-config/agents/creative/ux-designer.md +205 -0
  14. package/ai-config/agents/data-ai/ai-engineer.md +487 -0
  15. package/ai-config/agents/data-ai/analytics-engineer.md +953 -0
  16. package/ai-config/agents/data-ai/data-engineer.md +173 -0
  17. package/ai-config/agents/data-ai/data-scientist.md +672 -0
  18. package/ai-config/agents/data-ai/mlops-engineer.md +814 -0
  19. package/ai-config/agents/data-ai/prompt-engineer.md +772 -0
  20. package/ai-config/agents/development/angular-expert.md +620 -0
  21. package/ai-config/agents/development/backend-architect.md +795 -0
  22. package/ai-config/agents/development/database-specialist.md +212 -0
  23. package/ai-config/agents/development/frontend-specialist.md +686 -0
  24. package/ai-config/agents/development/fullstack-engineer.md +668 -0
  25. package/ai-config/agents/development/golang-pro.md +338 -0
  26. package/ai-config/agents/development/java-enterprise.md +400 -0
  27. package/ai-config/agents/development/javascript-pro.md +422 -0
  28. package/ai-config/agents/development/nextjs-pro.md +474 -0
  29. package/ai-config/agents/development/python-pro.md +570 -0
  30. package/ai-config/agents/development/react-pro.md +487 -0
  31. package/ai-config/agents/development/rust-pro.md +246 -0
  32. package/ai-config/agents/development/spring-boot-4-expert.md +326 -0
  33. package/ai-config/agents/development/typescript-pro.md +336 -0
  34. package/ai-config/agents/development/vue-specialist.md +605 -0
  35. package/ai-config/agents/infrastructure/cloud-architect.md +472 -0
  36. package/ai-config/agents/infrastructure/deployment-manager.md +358 -0
  37. package/ai-config/agents/infrastructure/devops-engineer.md +455 -0
  38. package/ai-config/agents/infrastructure/incident-responder.md +519 -0
  39. package/ai-config/agents/infrastructure/kubernetes-expert.md +705 -0
  40. package/ai-config/agents/infrastructure/monitoring-specialist.md +674 -0
  41. package/ai-config/agents/infrastructure/performance-engineer.md +658 -0
  42. package/ai-config/agents/orchestrator.md +241 -0
  43. package/ai-config/agents/quality/accessibility-auditor.md +1204 -0
  44. package/ai-config/agents/quality/code-reviewer-compact.md +123 -0
  45. package/ai-config/agents/quality/code-reviewer.md +363 -0
  46. package/ai-config/agents/quality/dependency-manager.md +743 -0
  47. package/ai-config/agents/quality/e2e-test-specialist.md +1005 -0
  48. package/ai-config/agents/quality/performance-tester.md +1086 -0
  49. package/ai-config/agents/quality/security-auditor.md +133 -0
  50. package/ai-config/agents/quality/test-engineer.md +453 -0
  51. package/ai-config/agents/specialists/api-designer.md +87 -0
  52. package/ai-config/agents/specialists/backend-architect.md +73 -0
  53. package/ai-config/agents/specialists/code-reviewer.md +77 -0
  54. package/ai-config/agents/specialists/db-optimizer.md +75 -0
  55. package/ai-config/agents/specialists/devops-engineer.md +83 -0
  56. package/ai-config/agents/specialists/documentation-writer.md +78 -0
  57. package/ai-config/agents/specialists/frontend-developer.md +75 -0
  58. package/ai-config/agents/specialists/performance-analyst.md +82 -0
  59. package/ai-config/agents/specialists/refactor-specialist.md +74 -0
  60. package/ai-config/agents/specialists/security-auditor.md +74 -0
  61. package/ai-config/agents/specialists/test-engineer.md +81 -0
  62. package/ai-config/agents/specialists/ux-consultant.md +76 -0
  63. package/ai-config/agents/specialized/agent-generator.md +1190 -0
  64. package/ai-config/agents/specialized/blockchain-developer.md +149 -0
  65. package/ai-config/agents/specialized/code-migrator.md +892 -0
  66. package/ai-config/agents/specialized/context-manager.md +978 -0
  67. package/ai-config/agents/specialized/documentation-writer.md +1078 -0
  68. package/ai-config/agents/specialized/ecommerce-expert.md +1756 -0
  69. package/ai-config/agents/specialized/embedded-engineer.md +1714 -0
  70. package/ai-config/agents/specialized/error-detective.md +1034 -0
  71. package/ai-config/agents/specialized/fintech-specialist.md +1659 -0
  72. package/ai-config/agents/specialized/freelance-project-planner-v2.md +1988 -0
  73. package/ai-config/agents/specialized/freelance-project-planner-v3.md +2136 -0
  74. package/ai-config/agents/specialized/freelance-project-planner-v4.md +4503 -0
  75. package/ai-config/agents/specialized/freelance-project-planner.md +722 -0
  76. package/ai-config/agents/specialized/game-developer.md +1963 -0
  77. package/ai-config/agents/specialized/healthcare-dev.md +1620 -0
  78. package/ai-config/agents/specialized/mobile-developer.md +188 -0
  79. package/ai-config/agents/specialized/parallel-plan-executor.md +506 -0
  80. package/ai-config/agents/specialized/plan-executor.md +485 -0
  81. package/ai-config/agents/specialized/solo-dev-planner-modular/00-INDEX.md +485 -0
  82. package/ai-config/agents/specialized/solo-dev-planner-modular/01-CORE.md +3493 -0
  83. package/ai-config/agents/specialized/solo-dev-planner-modular/02-SELF-CORRECTION.md +778 -0
  84. package/ai-config/agents/specialized/solo-dev-planner-modular/03-PROGRESSIVE-SETUP.md +918 -0
  85. package/ai-config/agents/specialized/solo-dev-planner-modular/04-DEPLOYMENT.md +1537 -0
  86. package/ai-config/agents/specialized/solo-dev-planner-modular/05-TESTING.md +2633 -0
  87. package/ai-config/agents/specialized/solo-dev-planner-modular/06-OPERATIONS.md +5610 -0
  88. package/ai-config/agents/specialized/solo-dev-planner-modular/INSTALL.md +335 -0
  89. package/ai-config/agents/specialized/solo-dev-planner-modular/QUICK-REFERENCE.txt +215 -0
  90. package/ai-config/agents/specialized/solo-dev-planner-modular/README.md +260 -0
  91. package/ai-config/agents/specialized/solo-dev-planner-modular/START-HERE.md +379 -0
  92. package/ai-config/agents/specialized/solo-dev-planner-modular/WORKFLOW-DIAGRAM.md +355 -0
  93. package/ai-config/agents/specialized/solo-dev-planner-modular/solo-dev-planner.md +279 -0
  94. package/ai-config/agents/specialized/template-writer.md +347 -0
  95. package/ai-config/agents/specialized/test-runner.md +99 -0
  96. package/ai-config/agents/specialized/vibekanban-smart-worker.md +244 -0
  97. package/ai-config/agents/specialized/wave-executor.md +138 -0
  98. package/ai-config/agents/specialized/workflow-optimizer.md +1114 -0
  99. package/ai-config/commands/git/changelog.md +32 -0
  100. package/ai-config/commands/git/ci-local.md +70 -0
  101. package/ai-config/commands/git/commit.md +35 -0
  102. package/ai-config/commands/git/fix-issue.md +23 -0
  103. package/ai-config/commands/git/pr-create.md +42 -0
  104. package/ai-config/commands/git/pr-review.md +50 -0
  105. package/ai-config/commands/git/worktree.md +39 -0
  106. package/ai-config/commands/refactoring/cleanup.md +24 -0
  107. package/ai-config/commands/refactoring/dead-code.md +40 -0
  108. package/ai-config/commands/refactoring/extract.md +31 -0
  109. package/ai-config/commands/testing/e2e.md +30 -0
  110. package/ai-config/commands/testing/tdd.md +36 -0
  111. package/ai-config/commands/testing/test-coverage.md +30 -0
  112. package/ai-config/commands/testing/test-fix.md +24 -0
  113. package/ai-config/commands/workflow/generate-agents-md.md +85 -0
  114. package/ai-config/commands/workflow/planning.md +47 -0
  115. package/ai-config/commands/workflows/compound.md +89 -0
  116. package/ai-config/commands/workflows/plan.md +77 -0
  117. package/ai-config/commands/workflows/review.md +78 -0
  118. package/ai-config/commands/workflows/work.md +75 -0
  119. package/ai-config/config.yaml +18 -0
  120. package/ai-config/hooks/_TEMPLATE.md +96 -0
  121. package/ai-config/hooks/block-dangerous-commands.md +75 -0
  122. package/ai-config/hooks/commit-guard.md +90 -0
  123. package/ai-config/hooks/context-loader.md +73 -0
  124. package/ai-config/hooks/improve-prompt.md +91 -0
  125. package/ai-config/hooks/learning-log.md +72 -0
  126. package/ai-config/hooks/model-router.md +86 -0
  127. package/ai-config/hooks/secret-scanner.md +64 -0
  128. package/ai-config/hooks/skill-validator.md +102 -0
  129. package/ai-config/hooks/task-artifact.md +114 -0
  130. package/ai-config/hooks/validate-workflow.md +100 -0
  131. package/ai-config/prompts/base.md +71 -0
  132. package/ai-config/prompts/modes/debug.md +34 -0
  133. package/ai-config/prompts/modes/deploy.md +40 -0
  134. package/ai-config/prompts/modes/research.md +32 -0
  135. package/ai-config/prompts/modes/review.md +33 -0
  136. package/ai-config/prompts/review-policy.md +79 -0
  137. package/ai-config/skills/_TEMPLATE.md +157 -0
  138. package/ai-config/skills/backend/api-gateway/SKILL.md +254 -0
  139. package/ai-config/skills/backend/bff-concepts/SKILL.md +239 -0
  140. package/ai-config/skills/backend/bff-spring/SKILL.md +364 -0
  141. package/ai-config/skills/backend/chi-router/SKILL.md +396 -0
  142. package/ai-config/skills/backend/error-handling/SKILL.md +255 -0
  143. package/ai-config/skills/backend/exceptions-spring/SKILL.md +323 -0
  144. package/ai-config/skills/backend/fastapi/SKILL.md +302 -0
  145. package/ai-config/skills/backend/gateway-spring/SKILL.md +390 -0
  146. package/ai-config/skills/backend/go-backend/SKILL.md +457 -0
  147. package/ai-config/skills/backend/gradle-multimodule/SKILL.md +274 -0
  148. package/ai-config/skills/backend/graphql-concepts/SKILL.md +352 -0
  149. package/ai-config/skills/backend/graphql-spring/SKILL.md +398 -0
  150. package/ai-config/skills/backend/grpc-concepts/SKILL.md +283 -0
  151. package/ai-config/skills/backend/grpc-spring/SKILL.md +445 -0
  152. package/ai-config/skills/backend/jwt-auth/SKILL.md +412 -0
  153. package/ai-config/skills/backend/notifications-concepts/SKILL.md +259 -0
  154. package/ai-config/skills/backend/recommendations-concepts/SKILL.md +261 -0
  155. package/ai-config/skills/backend/search-concepts/SKILL.md +263 -0
  156. package/ai-config/skills/backend/search-spring/SKILL.md +375 -0
  157. package/ai-config/skills/backend/spring-boot-4/SKILL.md +172 -0
  158. package/ai-config/skills/backend/websockets/SKILL.md +532 -0
  159. package/ai-config/skills/data-ai/ai-ml/SKILL.md +423 -0
  160. package/ai-config/skills/data-ai/analytics-concepts/SKILL.md +195 -0
  161. package/ai-config/skills/data-ai/analytics-spring/SKILL.md +340 -0
  162. package/ai-config/skills/data-ai/duckdb-analytics/SKILL.md +440 -0
  163. package/ai-config/skills/data-ai/langchain/SKILL.md +238 -0
  164. package/ai-config/skills/data-ai/mlflow/SKILL.md +302 -0
  165. package/ai-config/skills/data-ai/onnx-inference/SKILL.md +290 -0
  166. package/ai-config/skills/data-ai/powerbi/SKILL.md +352 -0
  167. package/ai-config/skills/data-ai/pytorch/SKILL.md +274 -0
  168. package/ai-config/skills/data-ai/scikit-learn/SKILL.md +321 -0
  169. package/ai-config/skills/data-ai/vector-db/SKILL.md +301 -0
  170. package/ai-config/skills/database/graph-databases/SKILL.md +218 -0
  171. package/ai-config/skills/database/graph-spring/SKILL.md +361 -0
  172. package/ai-config/skills/database/pgx-postgres/SKILL.md +512 -0
  173. package/ai-config/skills/database/redis-cache/SKILL.md +343 -0
  174. package/ai-config/skills/database/sqlite-embedded/SKILL.md +388 -0
  175. package/ai-config/skills/database/timescaledb/SKILL.md +320 -0
  176. package/ai-config/skills/docs/api-documentation/SKILL.md +293 -0
  177. package/ai-config/skills/docs/docs-spring/SKILL.md +377 -0
  178. package/ai-config/skills/docs/mustache-templates/SKILL.md +190 -0
  179. package/ai-config/skills/docs/technical-docs/SKILL.md +447 -0
  180. package/ai-config/skills/frontend/astro-ssr/SKILL.md +441 -0
  181. package/ai-config/skills/frontend/frontend-design/SKILL.md +54 -0
  182. package/ai-config/skills/frontend/frontend-web/SKILL.md +368 -0
  183. package/ai-config/skills/frontend/mantine-ui/SKILL.md +396 -0
  184. package/ai-config/skills/frontend/tanstack-query/SKILL.md +439 -0
  185. package/ai-config/skills/frontend/zod-validation/SKILL.md +417 -0
  186. package/ai-config/skills/frontend/zustand-state/SKILL.md +350 -0
  187. package/ai-config/skills/infrastructure/chaos-engineering/SKILL.md +244 -0
  188. package/ai-config/skills/infrastructure/chaos-spring/SKILL.md +378 -0
  189. package/ai-config/skills/infrastructure/devops-infra/SKILL.md +435 -0
  190. package/ai-config/skills/infrastructure/docker-containers/SKILL.md +420 -0
  191. package/ai-config/skills/infrastructure/kubernetes/SKILL.md +456 -0
  192. package/ai-config/skills/infrastructure/opentelemetry/SKILL.md +546 -0
  193. package/ai-config/skills/infrastructure/traefik-proxy/SKILL.md +474 -0
  194. package/ai-config/skills/infrastructure/woodpecker-ci/SKILL.md +315 -0
  195. package/ai-config/skills/mobile/ionic-capacitor/SKILL.md +504 -0
  196. package/ai-config/skills/mobile/mobile-ionic/SKILL.md +448 -0
  197. package/ai-config/skills/prompt-improver/SKILL.md +125 -0
  198. package/ai-config/skills/quality/ghagga-review/SKILL.md +216 -0
  199. package/ai-config/skills/references/hooks-patterns/SKILL.md +238 -0
  200. package/ai-config/skills/references/mcp-servers/SKILL.md +275 -0
  201. package/ai-config/skills/references/plugins-reference/SKILL.md +110 -0
  202. package/ai-config/skills/references/skills-reference/SKILL.md +420 -0
  203. package/ai-config/skills/references/subagent-templates/SKILL.md +193 -0
  204. package/ai-config/skills/systems-iot/modbus-protocol/SKILL.md +410 -0
  205. package/ai-config/skills/systems-iot/mqtt-rumqttc/SKILL.md +408 -0
  206. package/ai-config/skills/systems-iot/rust-systems/SKILL.md +386 -0
  207. package/ai-config/skills/systems-iot/tokio-async/SKILL.md +324 -0
  208. package/ai-config/skills/testing/playwright-e2e/SKILL.md +289 -0
  209. package/ai-config/skills/testing/testcontainers/SKILL.md +299 -0
  210. package/ai-config/skills/testing/vitest-testing/SKILL.md +381 -0
  211. package/ai-config/skills/workflow/ci-local-guide/SKILL.md +118 -0
  212. package/ai-config/skills/workflow/claude-automation-recommender/SKILL.md +299 -0
  213. package/ai-config/skills/workflow/claude-md-improver/SKILL.md +158 -0
  214. package/ai-config/skills/workflow/finishing-a-development-branch/SKILL.md +117 -0
  215. package/ai-config/skills/workflow/git-github/SKILL.md +334 -0
  216. package/ai-config/skills/workflow/git-github/references/examples.md +160 -0
  217. package/ai-config/skills/workflow/git-workflow/SKILL.md +214 -0
  218. package/ai-config/skills/workflow/ide-plugins/SKILL.md +277 -0
  219. package/ai-config/skills/workflow/ide-plugins-intellij/SKILL.md +401 -0
  220. package/ai-config/skills/workflow/obsidian-brain-workflow/SKILL.md +199 -0
  221. package/ai-config/skills/workflow/using-git-worktrees/SKILL.md +100 -0
  222. package/ai-config/skills/workflow/verification-before-completion/SKILL.md +73 -0
  223. package/ai-config/skills/workflow/wave-workflow/SKILL.md +178 -0
  224. package/ci-local/README.md +170 -0
  225. package/ci-local/ci-local.sh +297 -0
  226. package/ci-local/hooks/commit-msg +74 -0
  227. package/ci-local/hooks/pre-commit +162 -0
  228. package/ci-local/hooks/pre-push +41 -0
  229. package/ci-local/install.sh +49 -0
  230. package/ci-local/semgrep.yml +214 -0
  231. package/dist/commands/analyze.d.ts +9 -0
  232. package/dist/commands/analyze.d.ts.map +1 -0
  233. package/dist/commands/analyze.js +55 -0
  234. package/dist/commands/analyze.js.map +1 -0
  235. package/dist/commands/analyze.test.d.ts +2 -0
  236. package/dist/commands/analyze.test.d.ts.map +1 -0
  237. package/dist/commands/analyze.test.js +145 -0
  238. package/dist/commands/analyze.test.js.map +1 -0
  239. package/dist/commands/doctor.d.ts +7 -0
  240. package/dist/commands/doctor.d.ts.map +1 -0
  241. package/dist/commands/doctor.js +158 -0
  242. package/dist/commands/doctor.js.map +1 -0
  243. package/dist/commands/doctor.test.d.ts +2 -0
  244. package/dist/commands/doctor.test.d.ts.map +1 -0
  245. package/dist/commands/doctor.test.js +200 -0
  246. package/dist/commands/doctor.test.js.map +1 -0
  247. package/dist/commands/init.d.ts +9 -0
  248. package/dist/commands/init.d.ts.map +1 -0
  249. package/dist/commands/init.js +283 -0
  250. package/dist/commands/init.js.map +1 -0
  251. package/dist/commands/init.test.d.ts +2 -0
  252. package/dist/commands/init.test.d.ts.map +1 -0
  253. package/dist/commands/init.test.js +271 -0
  254. package/dist/commands/init.test.js.map +1 -0
  255. package/dist/commands/sync.d.ts +8 -0
  256. package/dist/commands/sync.d.ts.map +1 -0
  257. package/dist/commands/sync.js +201 -0
  258. package/dist/commands/sync.js.map +1 -0
  259. package/dist/constants.d.ts +21 -0
  260. package/dist/constants.d.ts.map +1 -0
  261. package/dist/constants.js +57 -0
  262. package/dist/constants.js.map +1 -0
  263. package/dist/e2e/aggressive.e2e.test.d.ts +2 -0
  264. package/dist/e2e/aggressive.e2e.test.d.ts.map +1 -0
  265. package/dist/e2e/aggressive.e2e.test.js +350 -0
  266. package/dist/e2e/aggressive.e2e.test.js.map +1 -0
  267. package/dist/e2e/commands.e2e.test.d.ts +2 -0
  268. package/dist/e2e/commands.e2e.test.d.ts.map +1 -0
  269. package/dist/e2e/commands.e2e.test.js +213 -0
  270. package/dist/e2e/commands.e2e.test.js.map +1 -0
  271. package/dist/index.d.ts +3 -0
  272. package/dist/index.d.ts.map +1 -0
  273. package/dist/index.js +82 -0
  274. package/dist/index.js.map +1 -0
  275. package/dist/lib/common.d.ts +17 -0
  276. package/dist/lib/common.d.ts.map +1 -0
  277. package/dist/lib/common.js +111 -0
  278. package/dist/lib/common.js.map +1 -0
  279. package/dist/lib/common.test.d.ts +2 -0
  280. package/dist/lib/common.test.d.ts.map +1 -0
  281. package/dist/lib/common.test.js +316 -0
  282. package/dist/lib/common.test.js.map +1 -0
  283. package/dist/lib/frontmatter.d.ts +18 -0
  284. package/dist/lib/frontmatter.d.ts.map +1 -0
  285. package/dist/lib/frontmatter.js +61 -0
  286. package/dist/lib/frontmatter.js.map +1 -0
  287. package/dist/lib/frontmatter.test.d.ts +2 -0
  288. package/dist/lib/frontmatter.test.d.ts.map +1 -0
  289. package/dist/lib/frontmatter.test.js +257 -0
  290. package/dist/lib/frontmatter.test.js.map +1 -0
  291. package/dist/lib/template.d.ts +24 -0
  292. package/dist/lib/template.d.ts.map +1 -0
  293. package/dist/lib/template.js +78 -0
  294. package/dist/lib/template.js.map +1 -0
  295. package/dist/lib/template.test.d.ts +2 -0
  296. package/dist/lib/template.test.d.ts.map +1 -0
  297. package/dist/lib/template.test.js +201 -0
  298. package/dist/lib/template.test.js.map +1 -0
  299. package/dist/types/index.d.ts +48 -0
  300. package/dist/types/index.d.ts.map +1 -0
  301. package/dist/types/index.js +2 -0
  302. package/dist/types/index.js.map +1 -0
  303. package/dist/ui/AnalyzeUI.d.ts +7 -0
  304. package/dist/ui/AnalyzeUI.d.ts.map +1 -0
  305. package/dist/ui/AnalyzeUI.js +100 -0
  306. package/dist/ui/AnalyzeUI.js.map +1 -0
  307. package/dist/ui/App.d.ts +13 -0
  308. package/dist/ui/App.d.ts.map +1 -0
  309. package/dist/ui/App.js +100 -0
  310. package/dist/ui/App.js.map +1 -0
  311. package/dist/ui/CIContext.d.ts +9 -0
  312. package/dist/ui/CIContext.d.ts.map +1 -0
  313. package/dist/ui/CIContext.js +9 -0
  314. package/dist/ui/CIContext.js.map +1 -0
  315. package/dist/ui/CISelector.d.ts +8 -0
  316. package/dist/ui/CISelector.d.ts.map +1 -0
  317. package/dist/ui/CISelector.js +45 -0
  318. package/dist/ui/CISelector.js.map +1 -0
  319. package/dist/ui/Doctor.d.ts +3 -0
  320. package/dist/ui/Doctor.d.ts.map +1 -0
  321. package/dist/ui/Doctor.js +89 -0
  322. package/dist/ui/Doctor.js.map +1 -0
  323. package/dist/ui/Header.d.ts +8 -0
  324. package/dist/ui/Header.d.ts.map +1 -0
  325. package/dist/ui/Header.js +30 -0
  326. package/dist/ui/Header.js.map +1 -0
  327. package/dist/ui/MemorySelector.d.ts +8 -0
  328. package/dist/ui/MemorySelector.d.ts.map +1 -0
  329. package/dist/ui/MemorySelector.js +46 -0
  330. package/dist/ui/MemorySelector.js.map +1 -0
  331. package/dist/ui/NameInput.d.ts +8 -0
  332. package/dist/ui/NameInput.d.ts.map +1 -0
  333. package/dist/ui/NameInput.js +69 -0
  334. package/dist/ui/NameInput.js.map +1 -0
  335. package/dist/ui/OptionSelector.d.ts +12 -0
  336. package/dist/ui/OptionSelector.d.ts.map +1 -0
  337. package/dist/ui/OptionSelector.js +69 -0
  338. package/dist/ui/OptionSelector.js.map +1 -0
  339. package/dist/ui/Progress.d.ts +11 -0
  340. package/dist/ui/Progress.d.ts.map +1 -0
  341. package/dist/ui/Progress.js +58 -0
  342. package/dist/ui/Progress.js.map +1 -0
  343. package/dist/ui/StackSelector.d.ts +9 -0
  344. package/dist/ui/StackSelector.d.ts.map +1 -0
  345. package/dist/ui/StackSelector.js +65 -0
  346. package/dist/ui/StackSelector.js.map +1 -0
  347. package/dist/ui/Summary.d.ts +12 -0
  348. package/dist/ui/Summary.d.ts.map +1 -0
  349. package/dist/ui/Summary.js +114 -0
  350. package/dist/ui/Summary.js.map +1 -0
  351. package/dist/ui/SyncUI.d.ts +10 -0
  352. package/dist/ui/SyncUI.d.ts.map +1 -0
  353. package/dist/ui/SyncUI.js +64 -0
  354. package/dist/ui/SyncUI.js.map +1 -0
  355. package/dist/ui/Welcome.d.ts +7 -0
  356. package/dist/ui/Welcome.d.ts.map +1 -0
  357. package/dist/ui/Welcome.js +45 -0
  358. package/dist/ui/Welcome.js.map +1 -0
  359. package/dist/ui/theme.d.ts +10 -0
  360. package/dist/ui/theme.d.ts.map +1 -0
  361. package/dist/ui/theme.js +9 -0
  362. package/dist/ui/theme.js.map +1 -0
  363. package/modules/engram/.gitignore-snippet.txt +6 -0
  364. package/modules/engram/.mcp-config-snippet.json +11 -0
  365. package/modules/engram/README.md +146 -0
  366. package/modules/engram/install-engram.sh +216 -0
  367. package/modules/ghagga/.env.example +43 -0
  368. package/modules/ghagga/README.md +153 -0
  369. package/modules/ghagga/docker-compose.yml +80 -0
  370. package/modules/ghagga/setup-ghagga.sh +139 -0
  371. package/modules/memory-simple/.project/NOTES.md +22 -0
  372. package/modules/memory-simple/README.md +23 -0
  373. package/modules/obsidian-brain/.obsidian/app.json +23 -0
  374. package/modules/obsidian-brain/.obsidian/appearance.json +5 -0
  375. package/modules/obsidian-brain/.obsidian/bookmarks.json +34 -0
  376. package/modules/obsidian-brain/.obsidian/community-plugins.json +1 -0
  377. package/modules/obsidian-brain/.obsidian/core-plugins-migration.json +21 -0
  378. package/modules/obsidian-brain/.obsidian/core-plugins.json +18 -0
  379. package/modules/obsidian-brain/.obsidian/daily-notes.json +5 -0
  380. package/modules/obsidian-brain/.obsidian/graph.json +37 -0
  381. package/modules/obsidian-brain/.obsidian/hotkeys.json +14 -0
  382. package/modules/obsidian-brain/.obsidian/plugins/dataview/data.json +25 -0
  383. package/modules/obsidian-brain/.obsidian/plugins/obsidian-kanban/data.json +29 -0
  384. package/modules/obsidian-brain/.obsidian/plugins/templater-obsidian/data.json +18 -0
  385. package/modules/obsidian-brain/.obsidian/snippets/project-memory.css +71 -0
  386. package/modules/obsidian-brain/.obsidian-gitignore-snippet.txt +8 -0
  387. package/modules/obsidian-brain/.project/Attachments/.gitkeep +0 -0
  388. package/modules/obsidian-brain/.project/Memory/BLOCKERS.md +78 -0
  389. package/modules/obsidian-brain/.project/Memory/CONTEXT.md +102 -0
  390. package/modules/obsidian-brain/.project/Memory/DASHBOARD.md +73 -0
  391. package/modules/obsidian-brain/.project/Memory/DECISIONS.md +87 -0
  392. package/modules/obsidian-brain/.project/Memory/KANBAN.md +15 -0
  393. package/modules/obsidian-brain/.project/Memory/README.md +61 -0
  394. package/modules/obsidian-brain/.project/Memory/WAVES.md +78 -0
  395. package/modules/obsidian-brain/.project/Sessions/TEMPLATE.md +99 -0
  396. package/modules/obsidian-brain/.project/Templates/ADR.md +33 -0
  397. package/modules/obsidian-brain/.project/Templates/Blocker.md +21 -0
  398. package/modules/obsidian-brain/.project/Templates/Session.md +88 -0
  399. package/modules/obsidian-brain/README.md +268 -0
  400. package/modules/obsidian-brain/new-wave.sh +182 -0
  401. package/package.json +51 -0
  402. package/schemas/agent.schema.json +34 -0
  403. package/schemas/ai-config.schema.json +28 -0
  404. package/schemas/skill.schema.json +44 -0
  405. package/src/commands/analyze.test.ts +145 -0
  406. package/src/commands/analyze.ts +69 -0
  407. package/src/commands/doctor.test.ts +208 -0
  408. package/src/commands/doctor.ts +163 -0
  409. package/src/commands/init.test.ts +298 -0
  410. package/src/commands/init.ts +285 -0
  411. package/src/constants.ts +69 -0
  412. package/src/e2e/aggressive.e2e.test.ts +557 -0
  413. package/src/e2e/commands.e2e.test.ts +298 -0
  414. package/src/index.tsx +106 -0
  415. package/src/lib/common.test.ts +318 -0
  416. package/src/lib/common.ts +127 -0
  417. package/src/lib/frontmatter.test.ts +291 -0
  418. package/src/lib/frontmatter.ts +77 -0
  419. package/src/lib/template.test.ts +226 -0
  420. package/src/lib/template.ts +99 -0
  421. package/src/types/index.ts +53 -0
  422. package/src/ui/AnalyzeUI.tsx +133 -0
  423. package/src/ui/App.tsx +175 -0
  424. package/src/ui/CIContext.tsx +25 -0
  425. package/src/ui/CISelector.tsx +72 -0
  426. package/src/ui/Doctor.tsx +122 -0
  427. package/src/ui/Header.tsx +48 -0
  428. package/src/ui/MemorySelector.tsx +73 -0
  429. package/src/ui/NameInput.tsx +82 -0
  430. package/src/ui/OptionSelector.tsx +100 -0
  431. package/src/ui/Progress.tsx +88 -0
  432. package/src/ui/StackSelector.tsx +101 -0
  433. package/src/ui/Summary.tsx +134 -0
  434. package/src/ui/Welcome.tsx +54 -0
  435. package/src/ui/theme.ts +10 -0
  436. package/stryker.config.json +19 -0
  437. package/tasks/_TEMPLATE/files-edited.md +3 -0
  438. package/tasks/_TEMPLATE/plan.md +3 -0
  439. package/tasks/_TEMPLATE/research.md +3 -0
  440. package/tasks/_TEMPLATE/verification.md +5 -0
  441. package/templates/common/dependabot/cargo.yml +11 -0
  442. package/templates/common/dependabot/github-actions.yml +16 -0
  443. package/templates/common/dependabot/gomod.yml +15 -0
  444. package/templates/common/dependabot/gradle.yml +15 -0
  445. package/templates/common/dependabot/header.yml +3 -0
  446. package/templates/common/dependabot/maven.yml +15 -0
  447. package/templates/common/dependabot/npm.yml +20 -0
  448. package/templates/common/dependabot/pip.yml +11 -0
  449. package/templates/dependabot.yml +162 -0
  450. package/templates/github/ci-go.yml +41 -0
  451. package/templates/github/ci-java.yml +45 -0
  452. package/templates/github/ci-monorepo.yml +150 -0
  453. package/templates/github/ci-node.yml +42 -0
  454. package/templates/github/ci-python.yml +42 -0
  455. package/templates/github/ci-rust.yml +42 -0
  456. package/templates/github/dependabot-automerge.yml +40 -0
  457. package/templates/gitlab/gitlab-ci-go.yml +88 -0
  458. package/templates/gitlab/gitlab-ci-java.yml +79 -0
  459. package/templates/gitlab/gitlab-ci-monorepo.yml +126 -0
  460. package/templates/gitlab/gitlab-ci-node.yml +63 -0
  461. package/templates/gitlab/gitlab-ci-python.yml +147 -0
  462. package/templates/gitlab/gitlab-ci-rust.yml +67 -0
  463. package/templates/global/claude-settings.json +98 -0
  464. package/templates/global/codex-config.toml +8 -0
  465. package/templates/global/copilot-instructions/base-rules.instructions.md +13 -0
  466. package/templates/global/copilot-instructions/sdd-orchestrator.instructions.md +37 -0
  467. package/templates/global/gemini-commands/cleanup.toml +20 -0
  468. package/templates/global/gemini-commands/commit.toml +15 -0
  469. package/templates/global/gemini-commands/dead-code.toml +22 -0
  470. package/templates/global/gemini-commands/plan.toml +30 -0
  471. package/templates/global/gemini-commands/review.toml +17 -0
  472. package/templates/global/gemini-commands/sdd-apply.toml +22 -0
  473. package/templates/global/gemini-commands/sdd-ff.toml +14 -0
  474. package/templates/global/gemini-commands/sdd-new.toml +21 -0
  475. package/templates/global/gemini-commands/sdd-verify.toml +21 -0
  476. package/templates/global/gemini-commands/tdd.toml +26 -0
  477. package/templates/global/gemini-settings.json +8 -0
  478. package/templates/global/opencode-config.json +44 -0
  479. package/templates/global/sdd-instructions.md +47 -0
  480. package/templates/global/sdd-orchestrator-claude.md +46 -0
  481. package/templates/global/sdd-orchestrator-copilot.md +34 -0
  482. package/templates/renovate.json +69 -0
  483. package/templates/woodpecker/monorepo/backend.yml +34 -0
  484. package/templates/woodpecker/monorepo/frontend.yml +34 -0
  485. package/templates/woodpecker/monorepo/summary.yml +25 -0
  486. package/templates/woodpecker/woodpecker-go.yml +51 -0
  487. package/templates/woodpecker/woodpecker-java.yml +67 -0
  488. package/templates/woodpecker/woodpecker-node.yml +47 -0
  489. package/templates/woodpecker/woodpecker-python.yml +108 -0
  490. package/templates/woodpecker/woodpecker-rust.yml +57 -0
  491. package/tsconfig.json +19 -0
  492. package/vitest.config.ts +16 -0
  493. package/workflows/reusable-build-go.yml +111 -0
  494. package/workflows/reusable-build-java.yml +120 -0
  495. package/workflows/reusable-build-node.yml +145 -0
  496. package/workflows/reusable-build-python.yml +159 -0
  497. package/workflows/reusable-build-rust.yml +135 -0
  498. package/workflows/reusable-docker.yml +120 -0
  499. package/workflows/reusable-ghagga-review.yml +165 -0
  500. 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
+ })