@polymorphism-tech/morph-spec 4.3.6 → 4.5.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 (375) hide show
  1. package/.morph/.morphversion +3 -3
  2. package/.morph/analytics/threads-log.jsonl +44 -9
  3. package/.morph/config/config.json +2 -3
  4. package/.morph/framework/standards/STANDARDS.json +812 -0
  5. package/.morph/{standards → framework/standards}/ai-agents/team-orchestration.md +3 -3
  6. package/.morph/framework/standards/integration/mcp/mcp-tools.md +384 -0
  7. package/.morph/{templates → framework/templates}/README.md +17 -17
  8. package/.morph/{templates → framework/templates}/REGISTRY.json +48 -233
  9. package/.morph/framework/templates/code/dotnet/contracts/contracts.cs.hbs +172 -0
  10. package/.morph/{templates → framework/templates}/context/CONTEXT-FEATURE.md +1 -1
  11. package/.morph/{templates → framework/templates}/context/CONTEXT.md +3 -3
  12. package/.morph/framework/templates/docs/clarifications.md +253 -0
  13. package/.morph/framework/templates/docs/onboarding.md +123 -0
  14. package/.morph/framework/templates/docs/schema-analysis.md +119 -0
  15. package/.morph/{templates → framework/templates}/docs/spec.md +149 -149
  16. package/.morph/framework/templates/docs/ui-components.md +124 -0
  17. package/.morph/framework/templates/docs/ui-design-system.md +76 -0
  18. package/.morph/framework/templates/docs/ui-flows.md +167 -0
  19. package/.morph/framework/templates/docs/ui-mockups.md +98 -0
  20. package/.morph/{templates → framework/templates}/examples/spec-examples.md +1 -1
  21. package/.morph/{templates → framework/templates}/infrastructure/github/README.md +11 -11
  22. package/.morph/{templates → framework/templates}/infrastructure/github/workflows/deploy-azure-app-service.yml.hbs +2 -2
  23. package/.morph/{templates → framework/templates}/meta-prompts/parallel-workers/parallel-worker.md +2 -2
  24. package/.morph/{templates → framework/templates}/meta-prompts/validators/pre-commit-validator.md +1 -1
  25. package/.morph/logs/tool-failures.log +51 -0
  26. package/.morph/memory/pre-compact-2026-02-22T17-01-01-658Z.json +16 -0
  27. package/.morph/state.json +1 -1
  28. package/CLAUDE.md +20 -119
  29. package/README.md +20 -18
  30. package/bin/detect-agents.js +1 -1
  31. package/bin/morph-spec.js +116 -266
  32. package/bin/task-manager.cjs +2 -2
  33. package/bin/validate.js +1 -1
  34. package/claude-plugin.json +14 -0
  35. package/docs/claude-alignment-report.md +137 -0
  36. package/docs/plans/2026-02-22-claude-docs-morph-alignment-analysis.md +512 -0
  37. package/docs/plans/2026-02-22-claude-settings.md +515 -0
  38. package/docs/plans/2026-02-22-morph-cc-alignment-impl.md +728 -0
  39. package/docs/plans/2026-02-22-morph-spec-next.md +478 -0
  40. package/docs/plans/2026-02-22-native-alignment-design.md +199 -0
  41. package/docs/plans/2026-02-22-native-alignment-impl.md +925 -0
  42. package/docs/plans/2026-02-22-native-enrichment-design.md +244 -0
  43. package/docs/plans/2026-02-22-native-enrichment.md +735 -0
  44. package/framework/CLAUDE.md +77 -0
  45. package/framework/commands/morph-apply.md +9 -9
  46. package/framework/commands/morph-archive.md +8 -8
  47. package/framework/commands/morph-infra.md +1 -1
  48. package/framework/commands/morph-proposal.md +9 -9
  49. package/framework/commands/morph-status.md +3 -3
  50. package/framework/commands/morph-troubleshoot.md +1 -1
  51. package/framework/hooks/README.md +201 -282
  52. package/framework/hooks/claude-code/notification/approval-reminder.js +52 -0
  53. package/framework/hooks/claude-code/post-tool-use/dispatch.js +83 -0
  54. package/framework/hooks/claude-code/post-tool-use/handle-tool-failure.js +42 -0
  55. package/framework/hooks/claude-code/pre-compact/save-morph-context.js +61 -0
  56. package/framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js +71 -0
  57. package/framework/hooks/claude-code/pre-tool-use/protect-readonly-files.js +58 -0
  58. package/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +64 -0
  59. package/framework/hooks/claude-code/session-start/inject-morph-context.js +94 -0
  60. package/framework/hooks/claude-code/statusline.py +239 -0
  61. package/framework/hooks/claude-code/statusline.sh +7 -0
  62. package/framework/hooks/claude-code/stop/validate-completion.js +88 -0
  63. package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +91 -0
  64. package/framework/hooks/shared/hook-response.js +45 -0
  65. package/framework/hooks/shared/phase-utils.js +129 -0
  66. package/framework/hooks/shared/state-reader.js +138 -0
  67. package/framework/hooks/shared/stdin-reader.js +26 -0
  68. package/framework/phases.json +145 -0
  69. package/framework/rules/csharp-standards.md +10 -0
  70. package/framework/rules/frontend-standards.md +14 -0
  71. package/framework/rules/infrastructure-standards.md +13 -0
  72. package/framework/rules/morph-workflow.md +86 -0
  73. package/framework/rules/testing-standards.md +11 -0
  74. package/framework/skills/level-0-meta/brainstorming.md +133 -0
  75. package/framework/skills/level-0-meta/code-review.md +12 -4
  76. package/framework/skills/level-0-meta/mcp-registry.json +207 -0
  77. package/framework/skills/level-0-meta/morph-checklist.md +9 -1
  78. package/framework/skills/level-0-meta/simulation-checklist.md +9 -1
  79. package/framework/skills/level-0-meta/tool-usage-guide.md +335 -0
  80. package/framework/skills/level-0-meta/verification-before-completion.md +145 -0
  81. package/framework/skills/level-1-workflows/morph-replicate.md +9 -1
  82. package/framework/skills/level-1-workflows/phase-clarify.md +65 -4
  83. package/framework/skills/level-1-workflows/phase-codebase-analysis.md +182 -0
  84. package/framework/skills/level-1-workflows/phase-design.md +342 -80
  85. package/framework/skills/level-1-workflows/phase-implement.md +254 -0
  86. package/framework/skills/level-1-workflows/phase-setup.md +76 -10
  87. package/framework/skills/level-1-workflows/phase-tasks.md +88 -7
  88. package/framework/skills/level-1-workflows/phase-uiux.md +95 -17
  89. package/framework/skills/level-2-domains/ai-agents/ai-system-architect.md +8 -1
  90. package/framework/skills/level-2-domains/architecture/po-pm-advisor.md +8 -1
  91. package/framework/skills/level-2-domains/architecture/prompt-engineer.md +8 -1
  92. package/framework/skills/level-2-domains/architecture/seo-growth-hacker.md +8 -1
  93. package/framework/skills/level-2-domains/architecture/standards-architect.md +11 -4
  94. package/framework/skills/level-2-domains/backend/api-designer.md +8 -1
  95. package/framework/skills/level-2-domains/backend/dotnet-senior.md +8 -1
  96. package/framework/skills/level-2-domains/backend/ef-modeler.md +8 -1
  97. package/framework/skills/level-2-domains/backend/hangfire-orchestrator.md +9 -2
  98. package/framework/skills/level-2-domains/backend/ms-agent-expert.md +8 -1
  99. package/framework/skills/level-2-domains/frontend/blazor-builder.md +8 -1
  100. package/framework/skills/level-2-domains/frontend/nextjs-expert.md +8 -1
  101. package/framework/skills/level-2-domains/frontend/ui-ux-designer.md +9 -2
  102. package/framework/skills/level-2-domains/infrastructure/azure-architect.md +8 -1
  103. package/framework/skills/level-2-domains/infrastructure/azure-deploy-specialist.md +8 -1
  104. package/framework/skills/level-2-domains/infrastructure/bicep-architect.md +8 -1
  105. package/framework/skills/level-2-domains/infrastructure/container-specialist.md +8 -1
  106. package/framework/skills/level-2-domains/infrastructure/devops-engineer.md +8 -1
  107. package/framework/skills/level-2-domains/integrations/asaas-financial.md +8 -1
  108. package/framework/skills/level-2-domains/integrations/azure-identity.md +8 -1
  109. package/framework/skills/level-2-domains/integrations/clerk-auth.md +8 -1
  110. package/framework/skills/level-2-domains/integrations/{hangfire-orchestrator.md → hangfire-integration.md} +8 -1
  111. package/framework/skills/level-2-domains/integrations/resend-email.md +8 -1
  112. package/framework/skills/level-2-domains/quality/code-analyzer.md +10 -3
  113. package/framework/skills/level-2-domains/quality/testing-specialist.md +8 -1
  114. package/framework/standards/STANDARDS.json +812 -0
  115. package/framework/standards/ai-agents/team-orchestration.md +3 -3
  116. package/framework/standards/frontend/nextjs/nextjs-patterns.md +17 -0
  117. package/framework/standards/integration/mcp/mcp-tools.md +384 -0
  118. package/framework/templates/README.md +17 -17
  119. package/framework/templates/REGISTRY.json +48 -233
  120. package/framework/templates/code/dotnet/contracts/contracts.cs.hbs +172 -0
  121. package/framework/templates/context/CONTEXT-FEATURE.md +1 -1
  122. package/framework/templates/context/CONTEXT.md +3 -3
  123. package/framework/templates/docs/clarifications.md +253 -0
  124. package/framework/templates/docs/onboarding.md +123 -0
  125. package/framework/templates/docs/schema-analysis.md +119 -0
  126. package/framework/templates/docs/spec.md +149 -149
  127. package/framework/templates/docs/ui-components.md +124 -0
  128. package/framework/templates/docs/ui-design-system.md +76 -0
  129. package/framework/templates/docs/ui-flows.md +167 -0
  130. package/framework/templates/docs/ui-mockups.md +98 -0
  131. package/framework/templates/docs/user-stories.md +34 -0
  132. package/framework/templates/examples/spec-examples.md +1 -1
  133. package/framework/templates/infrastructure/github/README.md +11 -11
  134. package/framework/templates/infrastructure/github/workflows/deploy-azure-app-service.yml.hbs +2 -2
  135. package/framework/templates/meta-prompts/parallel-workers/parallel-worker.md +2 -2
  136. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +1 -1
  137. package/framework/workflows/configs/express.json +45 -0
  138. package/framework/workflows/configs/spec-only.json +43 -0
  139. package/framework/workflows/docs/enforcement-pipeline.md +8 -8
  140. package/framework/workflows/docs/full-morph.md +3 -3
  141. package/package.json +3 -2
  142. package/scripts/generate-refs.js +336 -0
  143. package/scripts/generate-standards-registry.js +44 -0
  144. package/scripts/validate-real.mjs +255 -0
  145. package/src/commands/feature/create-story.js +362 -361
  146. package/src/commands/feature/shard-spec.js +225 -224
  147. package/src/commands/feature/sprint-status.js +1 -1
  148. package/src/commands/generation/generate-onboarding.js +169 -0
  149. package/src/commands/generation/generate.js +2 -2
  150. package/src/commands/mcp/mcp-setup.js +315 -0
  151. package/src/commands/project/changes.js +66 -0
  152. package/src/commands/project/checkpoint.js +209 -0
  153. package/src/commands/project/cost.js +179 -0
  154. package/src/commands/project/diff.js +278 -0
  155. package/src/commands/project/doctor.js +55 -7
  156. package/src/commands/project/init.js +318 -76
  157. package/src/commands/project/revert.js +173 -0
  158. package/src/commands/project/standards.js +80 -0
  159. package/src/commands/project/status.js +376 -0
  160. package/src/commands/project/update-agents.js +23 -0
  161. package/src/commands/project/update.js +63 -30
  162. package/src/commands/state/advance-phase.js +4 -3
  163. package/src/commands/state/state.js +10 -3
  164. package/src/commands/state/validate-phase.js +19 -2
  165. package/src/commands/templates/template-customize.js +4 -4
  166. package/src/commands/templates/template-render.js +1 -1
  167. package/src/commands/templates/template-show.js +1 -1
  168. package/src/commands/validation/validate-feature.js +359 -0
  169. package/src/core/orchestrator.js +3 -38
  170. package/src/core/paths/output-schema.js +135 -0
  171. package/src/core/state/state-manager.js +831 -592
  172. package/src/core/templates/template-registry.js +2 -2
  173. package/src/core/workflows/workflow-detector.js +17 -1
  174. package/src/lib/agents/micro-agent-factory.js +1 -1
  175. package/src/lib/context/context-bundler.js +2 -1
  176. package/src/lib/detectors/claude-config-detector.js +392 -0
  177. package/src/lib/detectors/conversation-analyzer.js +4 -4
  178. package/src/lib/detectors/design-system-detector.js +6 -5
  179. package/src/lib/detectors/standards-generator.js +2 -2
  180. package/src/lib/generators/context-generator.js +539 -538
  181. package/src/lib/generators/recap-generator.js +1 -1
  182. package/src/lib/generators/settings-generator.js +210 -0
  183. package/src/lib/hooks/hook-executor.js +1 -1
  184. package/src/lib/installers/mcp-installer.js +299 -0
  185. package/src/lib/learning/learning-system.js +3 -3
  186. package/src/lib/orchestration/team-orchestrator.js +1 -1
  187. package/src/lib/standards/standards-context-injector.js +7 -7
  188. package/src/lib/threads/thread-coordinator.js +1 -1
  189. package/src/lib/troubleshooting/troubleshoot-grep.js +1 -1
  190. package/src/lib/validators/contracts/contract-compliance-validator.js +274 -273
  191. package/src/lib/validators/design-system/design-system-validator.js +1 -1
  192. package/src/lib/validators/spec-validator.js +258 -258
  193. package/src/lib/validators/validation-runner.js +270 -269
  194. package/src/utils/agents-installer.js +206 -0
  195. package/src/utils/claude-settings-manager.js +258 -0
  196. package/src/utils/file-copier.js +1 -1
  197. package/src/utils/hooks-installer.js +354 -28
  198. package/src/utils/skills-installer.js +74 -0
  199. package/.morph/project/context/detection-log.md +0 -16
  200. package/.morph/project/standards/inferred.md +0 -59
  201. package/framework/hooks/agent-stop/validate-and-continue.js +0 -96
  202. package/framework/hooks/agent-stop/validate-checkpoints.js +0 -101
  203. package/framework/hooks/agent-stop/validate-tests.js +0 -109
  204. package/framework/hooks/agent-teams/dispatch.js +0 -67
  205. package/framework/hooks/agent-teams/phase-advanced.js +0 -80
  206. package/framework/hooks/agent-teams/task-completed.js +0 -76
  207. package/framework/hooks/agent-teams/teammate-idle.js +0 -70
  208. package/src/commands/agents/agents-fuse.js +0 -97
  209. package/src/commands/agents/micro-agent.js +0 -112
  210. package/src/commands/agents/spawn-team.js +0 -237
  211. package/src/commands/agents/squad-template.js +0 -146
  212. package/src/commands/analytics/analytics.js +0 -176
  213. package/src/commands/context/context-prime.js +0 -63
  214. package/src/commands/context/core-four.js +0 -54
  215. package/src/commands/generation/generate-context.js +0 -40
  216. package/src/commands/project/detect-agents.js +0 -207
  217. package/src/commands/project/detect-workflow.js +0 -174
  218. package/src/commands/threads/thread-template.js +0 -103
  219. package/src/commands/threads/threads.js +0 -261
  220. package/src/commands/utils/session-summary.js +0 -291
  221. package/src/llm/analyzer.js +0 -215
  222. package/src/llm/few-shot-examples.js +0 -216
  223. package/src/llm/project-config-schema.json +0 -188
  224. package/src/llm/prompt-builder.js +0 -96
  225. /package/.morph/{project/context → context}/README.md +0 -0
  226. /package/.morph/{config → framework}/agents.json +0 -0
  227. /package/.morph/{standards → framework/standards}/ai-agents/blazor-ui.md +0 -0
  228. /package/.morph/{standards → framework/standards}/ai-agents/production.md +0 -0
  229. /package/.morph/{standards → framework/standards}/ai-agents/setup.md +0 -0
  230. /package/.morph/{standards → framework/standards}/ai-agents/workflows.md +0 -0
  231. /package/.morph/{standards → framework/standards}/architecture/ddd/aggregates.md +0 -0
  232. /package/.morph/{standards → framework/standards}/architecture/ddd/entities.md +0 -0
  233. /package/.morph/{standards → framework/standards}/architecture/ddd/value-objects.md +0 -0
  234. /package/.morph/{standards → framework/standards}/backend/api/minimal-api.md +0 -0
  235. /package/.morph/{standards → framework/standards}/backend/api/rest.md +0 -0
  236. /package/.morph/{standards → framework/standards}/backend/api/validation.md +0 -0
  237. /package/.morph/{standards → framework/standards}/backend/authentication/passkeys.md +0 -0
  238. /package/.morph/{standards → framework/standards}/backend/database/ef-core.md +0 -0
  239. /package/.morph/{standards → framework/standards}/backend/database/migrations.md +0 -0
  240. /package/.morph/{standards → framework/standards}/backend/database/postgresql/database.md +0 -0
  241. /package/.morph/{standards → framework/standards}/backend/database/repository-patterns.md +0 -0
  242. /package/.morph/{standards → framework/standards}/backend/database/vector-search-rag.md +0 -0
  243. /package/.morph/{standards → framework/standards}/backend/dotnet/async.md +0 -0
  244. /package/.morph/{standards → framework/standards}/backend/dotnet/core.md +0 -0
  245. /package/.morph/{standards → framework/standards}/backend/dotnet/di.md +0 -0
  246. /package/.morph/{standards → framework/standards}/backend/dotnet/program-cs-checklist.md +0 -0
  247. /package/.morph/{standards → framework/standards}/backend/integrations/asaas/asaas-api.md +0 -0
  248. /package/.morph/{standards → framework/standards}/backend/integrations/clerk/clerk-auth.md +0 -0
  249. /package/.morph/{standards → framework/standards}/backend/integrations/hangfire/hangfire-jobs.md +0 -0
  250. /package/.morph/{standards → framework/standards}/backend/integrations/resend/resend-email.md +0 -0
  251. /package/.morph/{standards → framework/standards}/context/analytics.md +0 -0
  252. /package/.morph/{standards → framework/standards}/context/bundles.md +0 -0
  253. /package/.morph/{standards → framework/standards}/context/priming.md +0 -0
  254. /package/.morph/{standards → framework/standards}/core/architecture.md +0 -0
  255. /package/.morph/{standards → framework/standards}/core/coding.md +0 -0
  256. /package/.morph/{standards → framework/standards}/core/git-branching-strategy.md +0 -0
  257. /package/.morph/{standards → framework/standards}/core/git.md +0 -0
  258. /package/.morph/{standards → framework/standards}/core/testing.md +0 -0
  259. /package/.morph/{standards → framework/standards}/data/nosql/blob-storage.md +0 -0
  260. /package/.morph/{standards → framework/standards}/data/nosql/cache/redis.md +0 -0
  261. /package/.morph/{standards → framework/standards}/data/nosql/cosmos-db.md +0 -0
  262. /package/.morph/{standards → framework/standards}/data/vector-search/azure-ai-search.md +0 -0
  263. /package/.morph/{standards → framework/standards}/data/vector-search/rag-chunking.md +0 -0
  264. /package/.morph/{standards → framework/standards}/frontend/blazor/design-checklist.md +0 -0
  265. /package/.morph/{standards → framework/standards}/frontend/blazor/fluent-ui-setup.md +0 -0
  266. /package/.morph/{standards → framework/standards}/frontend/blazor/fluent-ui.md +0 -0
  267. /package/.morph/{standards → framework/standards}/frontend/blazor/html-conversion.md +0 -0
  268. /package/.morph/{standards → framework/standards}/frontend/blazor/lifecycle.md +0 -0
  269. /package/.morph/{standards → framework/standards}/frontend/blazor/pitfalls.md +0 -0
  270. /package/.morph/{standards → framework/standards}/frontend/blazor/state.md +0 -0
  271. /package/.morph/{standards → framework/standards}/frontend/design-system/animations.md +0 -0
  272. /package/.morph/{standards → framework/standards}/frontend/design-system/naming.md +0 -0
  273. /package/.morph/{standards → framework/standards}/frontend/nextjs/nextjs-patterns.md +0 -0
  274. /package/.morph/{standards → framework/standards}/infrastructure/azure/azure.md +0 -0
  275. /package/.morph/{standards → framework/standards}/infrastructure/azure/bicep/bicep-patterns.md +0 -0
  276. /package/.morph/{standards → framework/standards}/infrastructure/azure/devops/azure-devops-setup.md +0 -0
  277. /package/.morph/{standards → framework/standards}/infrastructure/azure/devops/local-development.md +0 -0
  278. /package/.morph/{standards → framework/standards}/infrastructure/azure/services/functions.md +0 -0
  279. /package/.morph/{standards → framework/standards}/infrastructure/azure/services/service-bus.md +0 -0
  280. /package/.morph/{standards → framework/standards}/infrastructure/azure/services/storage.md +0 -0
  281. /package/.morph/{standards → framework/standards}/infrastructure/docker/easypanel-deploy.md +0 -0
  282. /package/.morph/{standards → framework/standards}/infrastructure/supabase/mcp-setup.md +0 -0
  283. /package/.morph/{standards → framework/standards}/infrastructure/supabase/supabase-auth.md +0 -0
  284. /package/.morph/{standards → framework/standards}/infrastructure/supabase/supabase-pgvector.md +0 -0
  285. /package/.morph/{standards → framework/standards}/infrastructure/supabase/supabase-rls.md +0 -0
  286. /package/.morph/{standards → framework/standards}/infrastructure/supabase/supabase-storage.md +0 -0
  287. /package/.morph/{standards → framework/standards}/integration/api/graphql.md +0 -0
  288. /package/.morph/{standards → framework/standards}/integration/api/grpc.md +0 -0
  289. /package/.morph/{standards → framework/standards}/integration/api/rest-design.md +0 -0
  290. /package/.morph/{standards → framework/standards}/integration/event-driven/cqrs.md +0 -0
  291. /package/.morph/{standards → framework/standards}/integration/event-driven/event-sourcing.md +0 -0
  292. /package/.morph/{standards → framework/standards}/integration/event-driven/service-bus.md +0 -0
  293. /package/.morph/{standards → framework/standards}/observability/logging.md +0 -0
  294. /package/.morph/{standards → framework/standards}/observability/metrics.md +0 -0
  295. /package/.morph/{standards → framework/standards}/observability/monitoring.md +0 -0
  296. /package/.morph/{standards → framework/standards}/observability/tracing.md +0 -0
  297. /package/.morph/{standards → framework/standards}/workflows/parallel-execution.md +0 -0
  298. /package/.morph/{standards → framework/standards}/workflows/thread-management.md +0 -0
  299. /package/.morph/{templates → framework/templates}/.idea/morph-templates.xml +0 -0
  300. /package/.morph/{templates → framework/templates}/.vscode/morph-templates.code-snippets +0 -0
  301. /package/.morph/{templates → framework/templates}/IDE-SNIPPETS.md +0 -0
  302. /package/.morph/{templates → framework/templates}/code/dotnet/backend/repository.cs +0 -0
  303. /package/.morph/{templates → framework/templates}/code/dotnet/backend/service.cs +0 -0
  304. /package/.morph/{templates → framework/templates}/code/dotnet/contracts/Commands.cs +0 -0
  305. /package/.morph/{templates → framework/templates}/code/dotnet/contracts/Entities.cs +0 -0
  306. /package/.morph/{templates → framework/templates}/code/dotnet/contracts/Queries.cs +0 -0
  307. /package/.morph/{templates → framework/templates}/code/dotnet/contracts/README.md +0 -0
  308. /package/.morph/{templates → framework/templates}/code/dotnet/contracts/api-contracts.cs +0 -0
  309. /package/.morph/{templates → framework/templates}/code/dotnet/contracts/contracts.cs +0 -0
  310. /package/.morph/{templates → framework/templates}/code/dotnet/database/migration.cs +0 -0
  311. /package/.morph/{templates → framework/templates}/code/dotnet/frontend/component.razor +0 -0
  312. /package/.morph/{templates → framework/templates}/code/dotnet/jobs/agent.cs +0 -0
  313. /package/.morph/{templates → framework/templates}/code/dotnet/jobs/job.cs +0 -0
  314. /package/.morph/{templates → framework/templates}/code/dotnet/test.cs +0 -0
  315. /package/.morph/{templates → framework/templates}/code/sql/rls-policy.sql +0 -0
  316. /package/.morph/{templates → framework/templates}/code/sql/supabase-migration.sql +0 -0
  317. /package/.morph/{templates → framework/templates}/code/sql/supabase-migration.template.sql +0 -0
  318. /package/.morph/{templates → framework/templates}/code/typescript/contracts.ts +0 -0
  319. /package/.morph/{templates → framework/templates}/docs/proposal.md +0 -0
  320. /package/.morph/{templates → framework/templates}/examples/design-system-examples.md +0 -0
  321. /package/.morph/{templates → framework/templates}/feature/decisions.md +0 -0
  322. /package/.morph/{templates → framework/templates}/feature/recap.md +0 -0
  323. /package/.morph/{templates → framework/templates}/feature/tasks.md +0 -0
  324. /package/.morph/{templates → framework/templates}/infrastructure/azure/Dockerfile.example +0 -0
  325. /package/.morph/{templates → framework/templates}/infrastructure/azure/README.md +0 -0
  326. /package/.morph/{templates → framework/templates}/infrastructure/azure/app-insights.bicep +0 -0
  327. /package/.morph/{templates → framework/templates}/infrastructure/azure/app-service.bicep +0 -0
  328. /package/.morph/{templates → framework/templates}/infrastructure/azure/container-app-env.bicep +0 -0
  329. /package/.morph/{templates → framework/templates}/infrastructure/azure/container-app.bicep +0 -0
  330. /package/.morph/{templates → framework/templates}/infrastructure/azure/deploy-checklist.md +0 -0
  331. /package/.morph/{templates → framework/templates}/infrastructure/azure/deploy.ps1 +0 -0
  332. /package/.morph/{templates → framework/templates}/infrastructure/azure/deploy.sh +0 -0
  333. /package/.morph/{templates → framework/templates}/infrastructure/azure/key-vault.bicep +0 -0
  334. /package/.morph/{templates → framework/templates}/infrastructure/azure/main.bicep +0 -0
  335. /package/.morph/{templates → framework/templates}/infrastructure/azure/parameters.dev.json +0 -0
  336. /package/.morph/{templates → framework/templates}/infrastructure/azure/parameters.prod.json +0 -0
  337. /package/.morph/{templates → framework/templates}/infrastructure/azure/parameters.staging.json +0 -0
  338. /package/.morph/{templates → framework/templates}/infrastructure/azure/sql-database.bicep +0 -0
  339. /package/.morph/{templates → framework/templates}/infrastructure/azure/storage.bicep +0 -0
  340. /package/.morph/{templates → framework/templates}/infrastructure/docker/Dockerfile.template +0 -0
  341. /package/.morph/{templates → framework/templates}/infrastructure/docker/docker-compose.template.yml +0 -0
  342. /package/.morph/{templates → framework/templates}/infrastructure/docker/dockerfile-api.dockerfile +0 -0
  343. /package/.morph/{templates → framework/templates}/infrastructure/docker/dockerfile-web.dockerfile +0 -0
  344. /package/.morph/{templates → framework/templates}/infrastructure/docker/easypanel.template.json +0 -0
  345. /package/.morph/{templates → framework/templates}/infrastructure/github/actions/azure-auth/action.yml.hbs +0 -0
  346. /package/.morph/{templates → framework/templates}/infrastructure/github/actions/docker-build-push/action.yml.hbs +0 -0
  347. /package/.morph/{templates → framework/templates}/infrastructure/github/actions/health-check/action.yml.hbs +0 -0
  348. /package/.morph/{templates → framework/templates}/infrastructure/github/workflows/deploy-easypanel.yml.hbs +0 -0
  349. /package/.morph/{templates → framework/templates}/infrastructure/github/workflows/docker-build-push.yml.hbs +0 -0
  350. /package/.morph/{templates → framework/templates}/infrastructure/github/workflows/dotnet-build.yml.hbs +0 -0
  351. /package/.morph/{templates → framework/templates}/integrations/asaas-client.cs +0 -0
  352. /package/.morph/{templates → framework/templates}/integrations/asaas-webhook.cs +0 -0
  353. /package/.morph/{templates → framework/templates}/integrations/azure-identity-config.cs +0 -0
  354. /package/.morph/{templates → framework/templates}/integrations/clerk-config.cs +0 -0
  355. /package/.morph/{templates → framework/templates}/meta-prompts/fusion/fusion-agent.md +0 -0
  356. /package/.morph/{templates → framework/templates}/meta-prompts/fusion/fusion-aggregator.md +0 -0
  357. /package/.morph/{templates → framework/templates}/meta-prompts/hops/hop-retry.md +0 -0
  358. /package/.morph/{templates → framework/templates}/meta-prompts/hops/hop-validation.md +0 -0
  359. /package/.morph/{templates → framework/templates}/meta-prompts/hops/hop-wrapper.md +0 -0
  360. /package/.morph/{templates → framework/templates}/meta-prompts/parallel-workers/parallel-coordinator.md +0 -0
  361. /package/.morph/{templates → framework/templates}/meta-prompts/squad-leaders/backend-squad.md +0 -0
  362. /package/.morph/{templates → framework/templates}/meta-prompts/squad-leaders/frontend-squad.md +0 -0
  363. /package/.morph/{templates → framework/templates}/meta-prompts/squad-leaders/squad-leader.md +0 -0
  364. /package/.morph/{templates → framework/templates}/meta-prompts/validators/checkpoint-validator.md +0 -0
  365. /package/.morph/{templates → framework/templates}/saas/subscription.cs +0 -0
  366. /package/.morph/{templates → framework/templates}/saas/tenant.cs +0 -0
  367. /package/.morph/{templates → framework/templates}/state.template.json +0 -0
  368. /package/.morph/{templates → framework/templates}/ui/FluentDesignTheme.cs +0 -0
  369. /package/.morph/{templates → framework/templates}/ui/MudTheme.cs +0 -0
  370. /package/.morph/{templates → framework/templates}/ui/design-system.css +0 -0
  371. /package/framework/hooks/{commit-msg → git/commit-msg}/conventional-commits.sh +0 -0
  372. /package/framework/hooks/{pre-commit → git/pre-commit}/agents.sh +0 -0
  373. /package/framework/hooks/{pre-commit → git/pre-commit}/orchestrator.sh +0 -0
  374. /package/framework/hooks/{pre-commit → git/pre-commit}/specs.sh +0 -0
  375. /package/framework/hooks/{pre-push → git/pre-push}/run-tests.sh +0 -0
@@ -1,538 +1,539 @@
1
- /**
2
- * @fileoverview Context Generator - Auto-generates CONTEXT.md files
3
- *
4
- * Populates CONTEXT.md templates with real project data from:
5
- * - state.json (features, phases, tasks, checkpoints)
6
- * - config.json (project settings, Azure resources, agent config)
7
- * - agents.json (hierarchical agent structure)
8
- * - project.md (project description)
9
- * - standards/*.md (project-specific standards)
10
- *
11
- * @module context-generator
12
- */
13
-
14
- import fs from 'fs/promises';
15
- import path from 'path';
16
- import { existsSync } from 'fs';
17
-
18
- /**
19
- * Resolve a template path: check .morph/templates/ first, then framework/templates/.
20
- * @param {string} projectPath
21
- * @param {string} templatePath - relative path e.g. 'context/CONTEXT.md'
22
- * @returns {string|null}
23
- */
24
- function resolveTemplatePath(projectPath, templatePath) {
25
- const projectLocal = path.join(projectPath, '.morph/templates', templatePath);
26
- if (existsSync(projectLocal)) return projectLocal;
27
- const framework = path.join(projectPath, 'framework/templates', templatePath);
28
- if (existsSync(framework)) return framework;
29
- return null;
30
- }
31
-
32
- /**
33
- * Load project state from state.json
34
- * @param {string} projectPath - Root path of the project
35
- * @returns {Promise<Object>} Parsed state
36
- */
37
- async function loadState(projectPath) {
38
- const statePath = path.join(projectPath, '.morph/state.json');
39
- try {
40
- const content = await fs.readFile(statePath, 'utf8');
41
- return JSON.parse(content);
42
- } catch (err) {
43
- return { features: [], checkpoints: [] };
44
- }
45
- }
46
-
47
- /**
48
- * Load project config from config.json
49
- * @param {string} projectPath - Root path of the project
50
- * @returns {Promise<Object>} Parsed config
51
- */
52
- async function loadConfig(projectPath) {
53
- const configPath = path.join(projectPath, '.morph/config/config.json');
54
- try {
55
- const content = await fs.readFile(configPath, 'utf8');
56
- return JSON.parse(content);
57
- } catch (err) {
58
- return null;
59
- }
60
- }
61
-
62
- /**
63
- * Load agents hierarchy from agents.json
64
- * @param {string} projectPath - Root path of the project
65
- * @returns {Promise<Object>} Parsed agents config
66
- */
67
- async function loadAgents(projectPath) {
68
- const agentsPath = path.join(projectPath, '.morph/config/agents.json');
69
- try {
70
- const content = await fs.readFile(agentsPath, 'utf8');
71
- return JSON.parse(content);
72
- } catch (err) {
73
- return null;
74
- }
75
- }
76
-
77
- /**
78
- * Load project description from project.md
79
- * @param {string} projectPath - Root path of the project
80
- * @returns {Promise<string>} Project description
81
- */
82
- async function loadProjectDescription(projectPath) {
83
- const projectMdPath = path.join(projectPath, '.morph/project.md');
84
- try {
85
- return await fs.readFile(projectMdPath, 'utf8');
86
- } catch (err) {
87
- return '';
88
- }
89
- }
90
-
91
- /**
92
- * Load project-specific standards
93
- * @param {string} projectPath - Root path of the project
94
- * @returns {Promise<Array>} Array of {name, path, description}
95
- */
96
- async function loadProjectStandards(projectPath) {
97
- const standardsDir = path.join(projectPath, '.morph/project/standards');
98
- try {
99
- const files = await fs.readdir(standardsDir);
100
- return files
101
- .filter(f => f.endsWith('.md'))
102
- .map(f => ({
103
- name: f.replace('.md', ''),
104
- path: `standards/${f}`,
105
- description: null
106
- }));
107
- } catch (err) {
108
- return [];
109
- }
110
- }
111
-
112
- /**
113
- * Get nested property value from object using dot notation
114
- * @param {Object} obj - Object to get value from
115
- * @param {string} path - Dot-notation path (e.g., 'tasks.completed')
116
- * @returns {*} Value at path or empty string
117
- */
118
- function getNestedValue(obj, path) {
119
- const keys = path.split('.');
120
- let value = obj;
121
-
122
- for (const key of keys) {
123
- if (value && typeof value === 'object' && key in value) {
124
- value = value[key];
125
- } else {
126
- return '';
127
- }
128
- }
129
-
130
- return value !== null && value !== undefined ? value : '';
131
- }
132
-
133
- /**
134
- * Simple template renderer (Handlebars-like syntax)
135
- * @param {string} template - Template string with {{VAR}} placeholders
136
- * @param {Object} data - Data object with values
137
- * @returns {string} Rendered template
138
- */
139
- function renderTemplate(template, data) {
140
- let result = template;
141
-
142
- // Handle {{#each VAR}}...{{/each}} blocks first (before variable replacement)
143
- result = result.replace(/{{#each\s+(\w+)}}([\s\S]*?){{\/each}}/g, (_, varName, itemTemplate) => {
144
- const array = data[varName];
145
- if (!Array.isArray(array) || array.length === 0) return '';
146
-
147
- return array.map(item => {
148
- let itemResult = itemTemplate;
149
-
150
- // Replace all {{prop}} and {{nested.prop}} in the item template
151
- itemResult = itemResult.replace(/{{([\w.]+)}}/g, (__, propPath) => {
152
- return String(getNestedValue(item, propPath));
153
- });
154
-
155
- return itemResult;
156
- }).join('');
157
- });
158
-
159
- // Handle {{#if VAR}}...{{/if}} blocks
160
- result = result.replace(/{{#if\s+(\w+)}}([\s\S]*?){{\/if}}/g, (_, varName, content) => {
161
- return data[varName] ? content : '';
162
- });
163
-
164
- // Handle {{#unless VAR}}...{{/unless}} blocks
165
- result = result.replace(/{{#unless\s+(\w+)}}([\s\S]*?){{\/unless}}/g, (_, varName, content) => {
166
- const value = data[varName];
167
- // Check if value is falsy, empty array, or empty object
168
- const isFalsy = !value ||
169
- (Array.isArray(value) && value.length === 0) ||
170
- (typeof value === 'object' && Object.keys(value).length === 0);
171
- return isFalsy ? content : '';
172
- });
173
-
174
- // Replace simple {{VAR}} and {{nested.prop}} placeholders
175
- result = result.replace(/{{([\w.]+)}}/g, (_, path) => {
176
- return String(getNestedValue(data, path));
177
- });
178
-
179
- return result;
180
- }
181
-
182
- /**
183
- * Generate project-level CONTEXT.md
184
- * @param {string} projectPath - Root path of the project
185
- * @returns {Promise<string>} Generated CONTEXT.md content
186
- */
187
- export async function generateProjectContext(projectPath) {
188
- const [state, config, agents, projectDesc, standards] = await Promise.all([
189
- loadState(projectPath),
190
- loadConfig(projectPath),
191
- loadAgents(projectPath),
192
- loadProjectDescription(projectPath),
193
- loadProjectStandards(projectPath)
194
- ]);
195
-
196
- // Load template with fallback (.morph/templates/ → framework/templates/)
197
- const templatePath = resolveTemplatePath(projectPath, 'context/CONTEXT.md');
198
-
199
- if (!templatePath) {
200
- throw new Error('CONTEXT.md template not found in stack templates or framework/templates/');
201
- }
202
-
203
- const template = await fs.readFile(templatePath, 'utf8');
204
-
205
- // Build data object
206
- const data = {
207
- PROJECT_NAME: config?.project?.name || 'Unknown Project',
208
- PROJECT_TYPE: config?.project?.type || 'blazor-server',
209
- PROJECT_DESCRIPTION: projectDesc || config?.project?.description || '',
210
- REPOSITORY_URL: config?.project?.repository || '',
211
- TIMESTAMP: new Date().toISOString(),
212
-
213
- // Stack detection
214
- BLAZOR_ENABLED: config?.project?.type?.includes('blazor') ? true : false,
215
- NEXTJS_ENABLED: config?.project?.type?.includes('nextjs') ? true : false,
216
- DOTNET_VERSION: '10.0',
217
- EF_VERSION: '10.0',
218
-
219
- // Azure config
220
- AZURE_LOCATION: config?.azure?.location || 'eastus2',
221
- CONTAINER_APPS_ENABLED: config?.azure?.resources?.containerApps?.enabled || false,
222
- CONTAINER_ENV: config?.azure?.resources?.containerApps?.environment || '',
223
- SQL_ENABLED: config?.azure?.resources?.sql?.enabled || false,
224
- SQL_SERVER: config?.azure?.resources?.sql?.server || '',
225
- SQL_DATABASE: config?.azure?.resources?.sql?.database || '',
226
- OPENAI_ENABLED: config?.azure?.resources?.openai?.enabled || false,
227
- OPENAI_MODEL: config?.azure?.resources?.openai?.model || '',
228
- MONITORING_ENABLED: config?.azure?.resources?.monitoring?.enabled || false,
229
- APP_INSIGHTS: config?.azure?.resources?.monitoring?.appInsights || '',
230
-
231
- // Active features (convert object to array if needed)
232
- ACTIVE_FEATURES: (Array.isArray(state?.features)
233
- ? state.features
234
- : Object.entries(state?.features || {}).map(([name, data]) => ({ name, ...data }))
235
- ).map(f => ({
236
- name: f.name,
237
- phase: f.phase,
238
- status: f.status,
239
- complexity: f.complexity || 'medium',
240
- activeAgents: (f.activeAgents || []).join(', '),
241
- description: f.description || '',
242
- tasks: {
243
- completed: f.tasks?.completed || 0,
244
- total: f.tasks?.total || 0
245
- },
246
- outputs: Array.isArray(f.outputs)
247
- ? f.outputs.join(', ')
248
- : (f.outputs ? Object.keys(f.outputs).join(', ') : '')
249
- })),
250
-
251
- // Project standards
252
- PROJECT_STANDARDS: standards,
253
-
254
- // Agents by tier
255
- AGENT_COUNT: agents?.total_agents || 0,
256
- TIER_1_AGENTS: Object.entries(agents?.agents || {})
257
- .filter(([id, agent]) => !id.startsWith('_') && agent.tier === 1)
258
- .map(([id, agent]) => ({
259
- id,
260
- title: agent.title,
261
- always_active: agent.always_active
262
- })),
263
- TIER_2_AGENTS: Object.entries(agents?.agents || {})
264
- .filter(([id, agent]) => !id.startsWith('_') && agent.tier === 2)
265
- .map(([id, agent]) => ({
266
- id,
267
- title: agent.title,
268
- domains: (agent.domains || []).join(', ')
269
- })),
270
- TIER_3_AGENTS: Object.entries(agents?.agents || {})
271
- .filter(([id, agent]) => !id.startsWith('_') && agent.tier === 3)
272
- .map(([id, agent]) => ({
273
- id,
274
- title: agent.title,
275
- reports_to: agent.relationships?.reports_to || 'N/A'
276
- })),
277
-
278
- // Agent Teams config
279
- AGENT_TEAMS_ENABLED: config?.agentTeams?.enabled || false,
280
- AGENT_TEAMS_DISPLAY_MODE: config?.agentTeams?.displayMode || 'auto',
281
- AGENT_TEAMS_THRESHOLDS: config?.agentTeams?.spawnThresholds || {},
282
-
283
- // Recent checkpoints
284
- RECENT_CHECKPOINTS: (state?.checkpoints || []).slice(-5).map(cp => ({
285
- timestamp: cp.timestamp,
286
- feature: cp.feature,
287
- phase: cp.phase,
288
- note: cp.note,
289
- validation: cp.validation
290
- })),
291
-
292
- // DevOps config
293
- DEVOPS_ENABLED: config?.devops ? true : false,
294
- DEVOPS_ORG: config?.devops?.organization || '',
295
- DEVOPS_PROJECT: config?.devops?.project || '',
296
- DEVOPS_BOARDS_ENABLED: config?.devops?.boards?.enabled || false,
297
- DEVOPS_AREA_PATH: config?.devops?.boards?.areaPath || '',
298
- DEVOPS_WIKI_ENABLED: config?.devops?.wiki?.enabled || false,
299
- DEVOPS_WIKI_NAME: config?.devops?.wiki?.wikiName || '',
300
- DEVOPS_BUILD_PIPELINE: config?.devops?.pipelines?.buildPipeline || '',
301
- DEVOPS_RELEASE_PIPELINE: config?.devops?.pipelines?.releasePipeline || '',
302
-
303
- // Version
304
- MORPH_VERSION: '3.0.0-hierarchical'
305
- };
306
-
307
- return renderTemplate(template, data);
308
- }
309
-
310
- /**
311
- * Generate feature-specific CONTEXT-{feature}.md
312
- * @param {string} projectPath - Root path of the project
313
- * @param {string} featureName - Feature name
314
- * @returns {Promise<string>} Generated CONTEXT-{feature}.md content
315
- */
316
- export async function generateFeatureContext(projectPath, featureName) {
317
- const [state, agents] = await Promise.all([
318
- loadState(projectPath),
319
- loadAgents(projectPath)
320
- ]);
321
-
322
- // Find feature in state (handle both array and object format)
323
- let feature;
324
- if (Array.isArray(state?.features)) {
325
- feature = state.features.find(f => f.name === featureName);
326
- } else if (state?.features && typeof state.features === 'object') {
327
- feature = state.features[featureName];
328
- if (feature) {
329
- feature = { name: featureName, ...feature };
330
- }
331
- }
332
-
333
- if (!feature) {
334
- throw new Error(`Feature "${featureName}" not found in state.json`);
335
- }
336
-
337
- // Load feature outputs
338
- const outputsDir = path.join(projectPath, `.morph/project/outputs/${featureName}`);
339
- let decisions = [];
340
- let tasks = [];
341
-
342
- try {
343
- const decisionsPath = path.join(outputsDir, 'decisions.md');
344
- const decisionsContent = await fs.readFile(decisionsPath, 'utf8');
345
- // Parse decisions.md (simplified - just extract ADR headers)
346
- const adrMatches = decisionsContent.matchAll(/##\s+ADR-(\d+):\s+(.+)/g);
347
- decisions = Array.from(adrMatches).map(match => ({
348
- id: match[1],
349
- title: match[2],
350
- status: 'Accepted',
351
- date: new Date().toISOString().split('T')[0],
352
- context: '',
353
- decision: ''
354
- }));
355
- } catch (err) {
356
- // No decisions.md yet
357
- }
358
-
359
- try {
360
- const tasksPath = path.join(outputsDir, 'tasks.json');
361
- const tasksContent = await fs.readFile(tasksPath, 'utf8');
362
- tasks = JSON.parse(tasksContent).tasks || [];
363
- } catch (err) {
364
- // No tasks.json yet
365
- }
366
-
367
- // Load template with fallback (.morph/templates/ → framework/templates/)
368
- const featureTemplatePath = resolveTemplatePath(projectPath, 'context/CONTEXT-FEATURE.md');
369
-
370
- if (!featureTemplatePath) {
371
- throw new Error('CONTEXT-FEATURE.md template not found in stack templates or framework/templates/');
372
- }
373
-
374
- const template = await fs.readFile(featureTemplatePath, 'utf8');
375
-
376
- // Build active agents list with hierarchy
377
- const activeAgentIds = feature.activeAgents || [];
378
- const activeAgents = activeAgentIds
379
- .filter(id => agents?.agents[id])
380
- .map(id => ({ id, ...agents.agents[id] }));
381
-
382
- // Group by tier and build squads
383
- const teamLead = activeAgents.find(a => a.tier === 1 && a.role === 'orchestrator');
384
- const domainLeaders = activeAgents.filter(a => a.tier === 2 && a.role === 'domain-leader');
385
- const specialists = activeAgents.filter(a => a.tier === 3 && a.role === 'specialist');
386
- const validators = activeAgents.filter(a => a.tier === 4 && a.role === 'validator');
387
-
388
- const squads = domainLeaders.map(leader => ({
389
- leader: {
390
- id: leader.id,
391
- title: leader.title
392
- },
393
- members: specialists
394
- .filter(s => s.relationships?.reports_to === leader.id)
395
- .map(s => ({
396
- id: s.id,
397
- title: s.title
398
- }))
399
- }));
400
-
401
- // Build data object
402
- const data = {
403
- FEATURE_NAME: feature.name,
404
- PHASE: feature.phase,
405
- WORKFLOW_TYPE: feature.workflow || 'standard',
406
- STATUS: feature.status,
407
- COMPLEXITY: feature.complexity || 'medium',
408
- DESCRIPTION: feature.description || '',
409
- TIMESTAMP: new Date().toISOString(),
410
-
411
- // Phase tracking
412
- PHASE_DESCRIPTION: `Current phase: ${feature.phase}`,
413
- COMPLETED_PHASES: [], // TODO: track phase history
414
- UPCOMING_PHASES: [], // TODO: calculate remaining phases
415
-
416
- // Tasks
417
- TASKS_TOTAL: feature.tasks?.total || tasks.length,
418
- TASKS_COMPLETED: feature.tasks?.completed || tasks.filter(t => t.status === 'completed').length,
419
- TASKS_IN_PROGRESS: tasks.filter(t => t.status === 'in_progress').length,
420
- TASKS_BLOCKED: tasks.filter(t => t.status === 'blocked').length,
421
- TASKS_PROGRESS: feature.tasks?.total > 0
422
- ? Math.round((feature.tasks.completed / feature.tasks.total) * 100)
423
- : 0,
424
- CURRENT_TASK: tasks.find(t => t.status === 'in_progress') || null,
425
-
426
- // Active agents
427
- ACTIVE_AGENTS_COUNT: activeAgents.length,
428
- TEAM_LEAD: teamLead ? {
429
- id: teamLead.id,
430
- title: teamLead.title
431
- } : null,
432
- SQUADS: squads,
433
- VALIDATORS: validators.map(v => ({
434
- id: v.id,
435
- title: v.title
436
- })),
437
-
438
- // Outputs (handle both object and array format)
439
- OUTPUTS: (Array.isArray(feature.outputs)
440
- ? feature.outputs.map(output => ({
441
- type: output,
442
- generated: true,
443
- path: `.morph/project/outputs/${featureName}/${output}`,
444
- generatedAt: null
445
- }))
446
- : Object.entries(feature.outputs || {}).map(([type, data]) => ({
447
- type,
448
- generated: data.created || false,
449
- path: data.path || `.morph/project/outputs/${featureName}/${type}`,
450
- generatedAt: data.createdAt || null
451
- }))
452
- ),
453
-
454
- // Decisions
455
- DECISIONS: decisions,
456
-
457
- // Validation
458
- VALIDATION_RESULTS: feature.lastValidation?.results || null,
459
- VALIDATION_PASSED: feature.lastValidation?.passed || false,
460
- VALIDATION_TIMESTAMP: feature.lastValidation?.timestamp || null,
461
-
462
- // Tasks list
463
- TASKS: tasks,
464
-
465
- // Checkpoints
466
- CHECKPOINTS: (state?.checkpoints || [])
467
- .filter(cp => cp.feature === featureName)
468
- .map(cp => ({
469
- timestamp: cp.timestamp,
470
- phase: cp.phase,
471
- tasksCompleted: cp.tasksCompleted,
472
- tasksTotal: cp.tasksTotal,
473
- note: cp.note,
474
- validation: cp.validation
475
- })),
476
-
477
- // Related standards
478
- RELATED_STANDARDS: [], // TODO: link standards based on activeAgents
479
-
480
- // File paths
481
- CURRENT_TASK_ID: tasks.find(t => t.status === 'in_progress')?.id || '',
482
- PROPOSAL_PATH: `.morph/project/outputs/${featureName}/proposal.md`,
483
- SPEC_PATH: `.morph/project/outputs/${featureName}/spec.md`,
484
- CONTRACTS_PATH: `.morph/project/outputs/${featureName}/contracts.cs`,
485
- TASKS_PATH: `.morph/project/outputs/${featureName}/tasks.json`,
486
- DECISIONS_PATH: `.morph/project/outputs/${featureName}/decisions.md`,
487
- RECAP_PATH: `.morph/project/outputs/${featureName}/recap.md`,
488
-
489
- // Version
490
- MORPH_VERSION: '3.0.0-hierarchical',
491
- FEATURE_ID: feature.id || featureName
492
- };
493
-
494
- return renderTemplate(template, data);
495
- }
496
-
497
- /**
498
- * Write generated context to file
499
- * @param {string} projectPath - Root path of the project
500
- * @param {string} content - Generated content
501
- * @param {string} filename - Output filename
502
- * @returns {Promise<string>} Path to written file
503
- */
504
- export async function writeContext(projectPath, content, filename = 'CONTEXT.md') {
505
- const contextDir = path.join(projectPath, '.morph/project/context');
506
- await fs.mkdir(contextDir, { recursive: true });
507
-
508
- const outputPath = path.join(contextDir, filename);
509
- await fs.writeFile(outputPath, content, 'utf8');
510
-
511
- return outputPath;
512
- }
513
-
514
- /**
515
- * Generate and write both project and feature context files
516
- * @param {string} projectPath - Root path of the project
517
- * @param {string} featureName - Optional feature name (if null, only generates project context)
518
- * @returns {Promise<Object>} Paths to generated files
519
- */
520
- export async function generateContext(projectPath, featureName = null) {
521
- const results = {};
522
-
523
- // Always generate project context
524
- const projectContext = await generateProjectContext(projectPath);
525
- results.projectContext = await writeContext(projectPath, projectContext, 'CONTEXT.md');
526
-
527
- // Generate feature context if requested
528
- if (featureName) {
529
- const featureContext = await generateFeatureContext(projectPath, featureName);
530
- results.featureContext = await writeContext(
531
- projectPath,
532
- featureContext,
533
- `CONTEXT-${featureName}.md`
534
- );
535
- }
536
-
537
- return results;
538
- }
1
+ /**
2
+ * @fileoverview Context Generator - Auto-generates CONTEXT.md files
3
+ *
4
+ * Populates CONTEXT.md templates with real project data from:
5
+ * - state.json (features, phases, tasks, checkpoints)
6
+ * - config.json (project settings, Azure resources, agent config)
7
+ * - agents.json (hierarchical agent structure)
8
+ * - project.md (project description)
9
+ * - standards/*.md (project-specific standards)
10
+ *
11
+ * @module context-generator
12
+ */
13
+
14
+ import fs from 'fs/promises';
15
+ import path from 'path';
16
+ import { existsSync } from 'fs';
17
+ import { getOutputPath } from '../../core/paths/output-schema.js';
18
+
19
+ /**
20
+ * Resolve a template path: check .morph/framework/templates/ first, then framework/templates/.
21
+ * @param {string} projectPath
22
+ * @param {string} templatePath - relative path e.g. 'context/CONTEXT.md'
23
+ * @returns {string|null}
24
+ */
25
+ function resolveTemplatePath(projectPath, templatePath) {
26
+ const projectLocal = path.join(projectPath, '.morph/framework/templates', templatePath);
27
+ if (existsSync(projectLocal)) return projectLocal;
28
+ const framework = path.join(projectPath, 'framework/templates', templatePath);
29
+ if (existsSync(framework)) return framework;
30
+ return null;
31
+ }
32
+
33
+ /**
34
+ * Load project state from state.json
35
+ * @param {string} projectPath - Root path of the project
36
+ * @returns {Promise<Object>} Parsed state
37
+ */
38
+ async function loadState(projectPath) {
39
+ const statePath = path.join(projectPath, '.morph/state.json');
40
+ try {
41
+ const content = await fs.readFile(statePath, 'utf8');
42
+ return JSON.parse(content);
43
+ } catch (err) {
44
+ return { features: [], checkpoints: [] };
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Load project config from config.json
50
+ * @param {string} projectPath - Root path of the project
51
+ * @returns {Promise<Object>} Parsed config
52
+ */
53
+ async function loadConfig(projectPath) {
54
+ const configPath = path.join(projectPath, '.morph/config/config.json');
55
+ try {
56
+ const content = await fs.readFile(configPath, 'utf8');
57
+ return JSON.parse(content);
58
+ } catch (err) {
59
+ return null;
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Load agents hierarchy from agents.json
65
+ * @param {string} projectPath - Root path of the project
66
+ * @returns {Promise<Object>} Parsed agents config
67
+ */
68
+ async function loadAgents(projectPath) {
69
+ const agentsPath = path.join(projectPath, '.morph/framework/agents.json');
70
+ try {
71
+ const content = await fs.readFile(agentsPath, 'utf8');
72
+ return JSON.parse(content);
73
+ } catch (err) {
74
+ return null;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Load project description from project.md
80
+ * @param {string} projectPath - Root path of the project
81
+ * @returns {Promise<string>} Project description
82
+ */
83
+ async function loadProjectDescription(projectPath) {
84
+ const projectMdPath = path.join(projectPath, '.morph/project.md');
85
+ try {
86
+ return await fs.readFile(projectMdPath, 'utf8');
87
+ } catch (err) {
88
+ return '';
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Load project-specific standards
94
+ * @param {string} projectPath - Root path of the project
95
+ * @returns {Promise<Array>} Array of {name, path, description}
96
+ */
97
+ async function loadProjectStandards(projectPath) {
98
+ const standardsDir = path.join(projectPath, '.morph/context');
99
+ try {
100
+ const files = await fs.readdir(standardsDir);
101
+ return files
102
+ .filter(f => f.endsWith('.md'))
103
+ .map(f => ({
104
+ name: f.replace('.md', ''),
105
+ path: `standards/${f}`,
106
+ description: null
107
+ }));
108
+ } catch (err) {
109
+ return [];
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Get nested property value from object using dot notation
115
+ * @param {Object} obj - Object to get value from
116
+ * @param {string} path - Dot-notation path (e.g., 'tasks.completed')
117
+ * @returns {*} Value at path or empty string
118
+ */
119
+ function getNestedValue(obj, path) {
120
+ const keys = path.split('.');
121
+ let value = obj;
122
+
123
+ for (const key of keys) {
124
+ if (value && typeof value === 'object' && key in value) {
125
+ value = value[key];
126
+ } else {
127
+ return '';
128
+ }
129
+ }
130
+
131
+ return value !== null && value !== undefined ? value : '';
132
+ }
133
+
134
+ /**
135
+ * Simple template renderer (Handlebars-like syntax)
136
+ * @param {string} template - Template string with {{VAR}} placeholders
137
+ * @param {Object} data - Data object with values
138
+ * @returns {string} Rendered template
139
+ */
140
+ function renderTemplate(template, data) {
141
+ let result = template;
142
+
143
+ // Handle {{#each VAR}}...{{/each}} blocks first (before variable replacement)
144
+ result = result.replace(/{{#each\s+(\w+)}}([\s\S]*?){{\/each}}/g, (_, varName, itemTemplate) => {
145
+ const array = data[varName];
146
+ if (!Array.isArray(array) || array.length === 0) return '';
147
+
148
+ return array.map(item => {
149
+ let itemResult = itemTemplate;
150
+
151
+ // Replace all {{prop}} and {{nested.prop}} in the item template
152
+ itemResult = itemResult.replace(/{{([\w.]+)}}/g, (__, propPath) => {
153
+ return String(getNestedValue(item, propPath));
154
+ });
155
+
156
+ return itemResult;
157
+ }).join('');
158
+ });
159
+
160
+ // Handle {{#if VAR}}...{{/if}} blocks
161
+ result = result.replace(/{{#if\s+(\w+)}}([\s\S]*?){{\/if}}/g, (_, varName, content) => {
162
+ return data[varName] ? content : '';
163
+ });
164
+
165
+ // Handle {{#unless VAR}}...{{/unless}} blocks
166
+ result = result.replace(/{{#unless\s+(\w+)}}([\s\S]*?){{\/unless}}/g, (_, varName, content) => {
167
+ const value = data[varName];
168
+ // Check if value is falsy, empty array, or empty object
169
+ const isFalsy = !value ||
170
+ (Array.isArray(value) && value.length === 0) ||
171
+ (typeof value === 'object' && Object.keys(value).length === 0);
172
+ return isFalsy ? content : '';
173
+ });
174
+
175
+ // Replace simple {{VAR}} and {{nested.prop}} placeholders
176
+ result = result.replace(/{{([\w.]+)}}/g, (_, path) => {
177
+ return String(getNestedValue(data, path));
178
+ });
179
+
180
+ return result;
181
+ }
182
+
183
+ /**
184
+ * Generate project-level CONTEXT.md
185
+ * @param {string} projectPath - Root path of the project
186
+ * @returns {Promise<string>} Generated CONTEXT.md content
187
+ */
188
+ export async function generateProjectContext(projectPath) {
189
+ const [state, config, agents, projectDesc, standards] = await Promise.all([
190
+ loadState(projectPath),
191
+ loadConfig(projectPath),
192
+ loadAgents(projectPath),
193
+ loadProjectDescription(projectPath),
194
+ loadProjectStandards(projectPath)
195
+ ]);
196
+
197
+ // Load template with fallback (.morph/framework/templates/ → framework/templates/)
198
+ const templatePath = resolveTemplatePath(projectPath, 'context/CONTEXT.md');
199
+
200
+ if (!templatePath) {
201
+ throw new Error('CONTEXT.md template not found in stack templates or framework/templates/');
202
+ }
203
+
204
+ const template = await fs.readFile(templatePath, 'utf8');
205
+
206
+ // Build data object
207
+ const data = {
208
+ PROJECT_NAME: config?.project?.name || 'Unknown Project',
209
+ PROJECT_TYPE: config?.project?.type || 'blazor-server',
210
+ PROJECT_DESCRIPTION: projectDesc || config?.project?.description || '',
211
+ REPOSITORY_URL: config?.project?.repository || '',
212
+ TIMESTAMP: new Date().toISOString(),
213
+
214
+ // Stack detection
215
+ BLAZOR_ENABLED: config?.project?.type?.includes('blazor') ? true : false,
216
+ NEXTJS_ENABLED: config?.project?.type?.includes('nextjs') ? true : false,
217
+ DOTNET_VERSION: '10.0',
218
+ EF_VERSION: '10.0',
219
+
220
+ // Azure config
221
+ AZURE_LOCATION: config?.azure?.location || 'eastus2',
222
+ CONTAINER_APPS_ENABLED: config?.azure?.resources?.containerApps?.enabled || false,
223
+ CONTAINER_ENV: config?.azure?.resources?.containerApps?.environment || '',
224
+ SQL_ENABLED: config?.azure?.resources?.sql?.enabled || false,
225
+ SQL_SERVER: config?.azure?.resources?.sql?.server || '',
226
+ SQL_DATABASE: config?.azure?.resources?.sql?.database || '',
227
+ OPENAI_ENABLED: config?.azure?.resources?.openai?.enabled || false,
228
+ OPENAI_MODEL: config?.azure?.resources?.openai?.model || '',
229
+ MONITORING_ENABLED: config?.azure?.resources?.monitoring?.enabled || false,
230
+ APP_INSIGHTS: config?.azure?.resources?.monitoring?.appInsights || '',
231
+
232
+ // Active features (convert object to array if needed)
233
+ ACTIVE_FEATURES: (Array.isArray(state?.features)
234
+ ? state.features
235
+ : Object.entries(state?.features || {}).map(([name, data]) => ({ name, ...data }))
236
+ ).map(f => ({
237
+ name: f.name,
238
+ phase: f.phase,
239
+ status: f.status,
240
+ complexity: f.complexity || 'medium',
241
+ activeAgents: (f.activeAgents || []).join(', '),
242
+ description: f.description || '',
243
+ tasks: {
244
+ completed: f.tasks?.completed || 0,
245
+ total: f.tasks?.total || 0
246
+ },
247
+ outputs: Array.isArray(f.outputs)
248
+ ? f.outputs.join(', ')
249
+ : (f.outputs ? Object.keys(f.outputs).join(', ') : '')
250
+ })),
251
+
252
+ // Project standards
253
+ PROJECT_STANDARDS: standards,
254
+
255
+ // Agents by tier
256
+ AGENT_COUNT: agents?.total_agents || 0,
257
+ TIER_1_AGENTS: Object.entries(agents?.agents || {})
258
+ .filter(([id, agent]) => !id.startsWith('_') && agent.tier === 1)
259
+ .map(([id, agent]) => ({
260
+ id,
261
+ title: agent.title,
262
+ always_active: agent.always_active
263
+ })),
264
+ TIER_2_AGENTS: Object.entries(agents?.agents || {})
265
+ .filter(([id, agent]) => !id.startsWith('_') && agent.tier === 2)
266
+ .map(([id, agent]) => ({
267
+ id,
268
+ title: agent.title,
269
+ domains: (agent.domains || []).join(', ')
270
+ })),
271
+ TIER_3_AGENTS: Object.entries(agents?.agents || {})
272
+ .filter(([id, agent]) => !id.startsWith('_') && agent.tier === 3)
273
+ .map(([id, agent]) => ({
274
+ id,
275
+ title: agent.title,
276
+ reports_to: agent.relationships?.reports_to || 'N/A'
277
+ })),
278
+
279
+ // Agent Teams config
280
+ AGENT_TEAMS_ENABLED: config?.agentTeams?.enabled || false,
281
+ AGENT_TEAMS_DISPLAY_MODE: config?.agentTeams?.displayMode || 'auto',
282
+ AGENT_TEAMS_THRESHOLDS: config?.agentTeams?.spawnThresholds || {},
283
+
284
+ // Recent checkpoints
285
+ RECENT_CHECKPOINTS: (state?.checkpoints || []).slice(-5).map(cp => ({
286
+ timestamp: cp.timestamp,
287
+ feature: cp.feature,
288
+ phase: cp.phase,
289
+ note: cp.note,
290
+ validation: cp.validation
291
+ })),
292
+
293
+ // DevOps config
294
+ DEVOPS_ENABLED: config?.devops ? true : false,
295
+ DEVOPS_ORG: config?.devops?.organization || '',
296
+ DEVOPS_PROJECT: config?.devops?.project || '',
297
+ DEVOPS_BOARDS_ENABLED: config?.devops?.boards?.enabled || false,
298
+ DEVOPS_AREA_PATH: config?.devops?.boards?.areaPath || '',
299
+ DEVOPS_WIKI_ENABLED: config?.devops?.wiki?.enabled || false,
300
+ DEVOPS_WIKI_NAME: config?.devops?.wiki?.wikiName || '',
301
+ DEVOPS_BUILD_PIPELINE: config?.devops?.pipelines?.buildPipeline || '',
302
+ DEVOPS_RELEASE_PIPELINE: config?.devops?.pipelines?.releasePipeline || '',
303
+
304
+ // Version
305
+ MORPH_VERSION: '3.0.0-hierarchical'
306
+ };
307
+
308
+ return renderTemplate(template, data);
309
+ }
310
+
311
+ /**
312
+ * Generate feature-specific CONTEXT-{feature}.md
313
+ * @param {string} projectPath - Root path of the project
314
+ * @param {string} featureName - Feature name
315
+ * @returns {Promise<string>} Generated CONTEXT-{feature}.md content
316
+ */
317
+ export async function generateFeatureContext(projectPath, featureName) {
318
+ const [state, agents] = await Promise.all([
319
+ loadState(projectPath),
320
+ loadAgents(projectPath)
321
+ ]);
322
+
323
+ // Find feature in state (handle both array and object format)
324
+ let feature;
325
+ if (Array.isArray(state?.features)) {
326
+ feature = state.features.find(f => f.name === featureName);
327
+ } else if (state?.features && typeof state.features === 'object') {
328
+ feature = state.features[featureName];
329
+ if (feature) {
330
+ feature = { name: featureName, ...feature };
331
+ }
332
+ }
333
+
334
+ if (!feature) {
335
+ throw new Error(`Feature "${featureName}" not found in state.json`);
336
+ }
337
+
338
+ // Load feature outputs
339
+ const outputsDir = path.join(projectPath, `.morph/features/${featureName}`);
340
+ let decisions = [];
341
+ let tasks = [];
342
+
343
+ try {
344
+ const decisionsPath = path.join(outputsDir, '1-design', 'decisions.md');
345
+ const decisionsContent = await fs.readFile(decisionsPath, 'utf8');
346
+ // Parse decisions.md (simplified - just extract ADR headers)
347
+ const adrMatches = decisionsContent.matchAll(/##\s+ADR-(\d+):\s+(.+)/g);
348
+ decisions = Array.from(adrMatches).map(match => ({
349
+ id: match[1],
350
+ title: match[2],
351
+ status: 'Accepted',
352
+ date: new Date().toISOString().split('T')[0],
353
+ context: '',
354
+ decision: ''
355
+ }));
356
+ } catch (err) {
357
+ // No decisions.md yet
358
+ }
359
+
360
+ try {
361
+ const tasksPath = path.join(outputsDir, '3-tasks', 'tasks.json');
362
+ const tasksContent = await fs.readFile(tasksPath, 'utf8');
363
+ tasks = JSON.parse(tasksContent).tasks || [];
364
+ } catch (err) {
365
+ // No tasks.json yet
366
+ }
367
+
368
+ // Load template with fallback (.morph/framework/templates/ → framework/templates/)
369
+ const featureTemplatePath = resolveTemplatePath(projectPath, 'context/CONTEXT-FEATURE.md');
370
+
371
+ if (!featureTemplatePath) {
372
+ throw new Error('CONTEXT-FEATURE.md template not found in stack templates or framework/templates/');
373
+ }
374
+
375
+ const template = await fs.readFile(featureTemplatePath, 'utf8');
376
+
377
+ // Build active agents list with hierarchy
378
+ const activeAgentIds = feature.activeAgents || [];
379
+ const activeAgents = activeAgentIds
380
+ .filter(id => agents?.agents[id])
381
+ .map(id => ({ id, ...agents.agents[id] }));
382
+
383
+ // Group by tier and build squads
384
+ const teamLead = activeAgents.find(a => a.tier === 1 && a.role === 'orchestrator');
385
+ const domainLeaders = activeAgents.filter(a => a.tier === 2 && a.role === 'domain-leader');
386
+ const specialists = activeAgents.filter(a => a.tier === 3 && a.role === 'specialist');
387
+ const validators = activeAgents.filter(a => a.tier === 4 && a.role === 'validator');
388
+
389
+ const squads = domainLeaders.map(leader => ({
390
+ leader: {
391
+ id: leader.id,
392
+ title: leader.title
393
+ },
394
+ members: specialists
395
+ .filter(s => s.relationships?.reports_to === leader.id)
396
+ .map(s => ({
397
+ id: s.id,
398
+ title: s.title
399
+ }))
400
+ }));
401
+
402
+ // Build data object
403
+ const data = {
404
+ FEATURE_NAME: feature.name,
405
+ PHASE: feature.phase,
406
+ WORKFLOW_TYPE: feature.workflow || 'standard',
407
+ STATUS: feature.status,
408
+ COMPLEXITY: feature.complexity || 'medium',
409
+ DESCRIPTION: feature.description || '',
410
+ TIMESTAMP: new Date().toISOString(),
411
+
412
+ // Phase tracking
413
+ PHASE_DESCRIPTION: `Current phase: ${feature.phase}`,
414
+ COMPLETED_PHASES: [], // TODO: track phase history
415
+ UPCOMING_PHASES: [], // TODO: calculate remaining phases
416
+
417
+ // Tasks
418
+ TASKS_TOTAL: feature.tasks?.total || tasks.length,
419
+ TASKS_COMPLETED: feature.tasks?.completed || tasks.filter(t => t.status === 'completed').length,
420
+ TASKS_IN_PROGRESS: tasks.filter(t => t.status === 'in_progress').length,
421
+ TASKS_BLOCKED: tasks.filter(t => t.status === 'blocked').length,
422
+ TASKS_PROGRESS: feature.tasks?.total > 0
423
+ ? Math.round((feature.tasks.completed / feature.tasks.total) * 100)
424
+ : 0,
425
+ CURRENT_TASK: tasks.find(t => t.status === 'in_progress') || null,
426
+
427
+ // Active agents
428
+ ACTIVE_AGENTS_COUNT: activeAgents.length,
429
+ TEAM_LEAD: teamLead ? {
430
+ id: teamLead.id,
431
+ title: teamLead.title
432
+ } : null,
433
+ SQUADS: squads,
434
+ VALIDATORS: validators.map(v => ({
435
+ id: v.id,
436
+ title: v.title
437
+ })),
438
+
439
+ // Outputs (handle both object and array format)
440
+ OUTPUTS: (Array.isArray(feature.outputs)
441
+ ? feature.outputs.map(output => ({
442
+ type: output,
443
+ generated: true,
444
+ path: `.morph/features/${featureName}/${output}`,
445
+ generatedAt: null
446
+ }))
447
+ : Object.entries(feature.outputs || {}).map(([type, data]) => ({
448
+ type,
449
+ generated: data.created || false,
450
+ path: data.path || `.morph/features/${featureName}/${type}`,
451
+ generatedAt: data.createdAt || null
452
+ }))
453
+ ),
454
+
455
+ // Decisions
456
+ DECISIONS: decisions,
457
+
458
+ // Validation
459
+ VALIDATION_RESULTS: feature.lastValidation?.results || null,
460
+ VALIDATION_PASSED: feature.lastValidation?.passed || false,
461
+ VALIDATION_TIMESTAMP: feature.lastValidation?.timestamp || null,
462
+
463
+ // Tasks list
464
+ TASKS: tasks,
465
+
466
+ // Checkpoints
467
+ CHECKPOINTS: (state?.checkpoints || [])
468
+ .filter(cp => cp.feature === featureName)
469
+ .map(cp => ({
470
+ timestamp: cp.timestamp,
471
+ phase: cp.phase,
472
+ tasksCompleted: cp.tasksCompleted,
473
+ tasksTotal: cp.tasksTotal,
474
+ note: cp.note,
475
+ validation: cp.validation
476
+ })),
477
+
478
+ // Related standards
479
+ RELATED_STANDARDS: [], // TODO: link standards based on activeAgents
480
+
481
+ // File paths
482
+ CURRENT_TASK_ID: tasks.find(t => t.status === 'in_progress')?.id || '',
483
+ PROPOSAL_PATH: getOutputPath(featureName, 'proposal'),
484
+ SPEC_PATH: getOutputPath(featureName, 'spec'),
485
+ CONTRACTS_PATH: getOutputPath(featureName, 'contracts'),
486
+ TASKS_PATH: `.morph/features/${featureName}/3-tasks/tasks.json`,
487
+ DECISIONS_PATH: getOutputPath(featureName, 'decisions'),
488
+ RECAP_PATH: getOutputPath(featureName, 'recap'),
489
+
490
+ // Version
491
+ MORPH_VERSION: '3.0.0-hierarchical',
492
+ FEATURE_ID: feature.id || featureName
493
+ };
494
+
495
+ return renderTemplate(template, data);
496
+ }
497
+
498
+ /**
499
+ * Write generated context to file
500
+ * @param {string} projectPath - Root path of the project
501
+ * @param {string} content - Generated content
502
+ * @param {string} filename - Output filename
503
+ * @returns {Promise<string>} Path to written file
504
+ */
505
+ export async function writeContext(projectPath, content, filename = 'CONTEXT.md') {
506
+ const contextDir = path.join(projectPath, '.morph/context');
507
+ await fs.mkdir(contextDir, { recursive: true });
508
+
509
+ const outputPath = path.join(contextDir, filename);
510
+ await fs.writeFile(outputPath, content, 'utf8');
511
+
512
+ return outputPath;
513
+ }
514
+
515
+ /**
516
+ * Generate and write both project and feature context files
517
+ * @param {string} projectPath - Root path of the project
518
+ * @param {string} featureName - Optional feature name (if null, only generates project context)
519
+ * @returns {Promise<Object>} Paths to generated files
520
+ */
521
+ export async function generateContext(projectPath, featureName = null) {
522
+ const results = {};
523
+
524
+ // Always generate project context
525
+ const projectContext = await generateProjectContext(projectPath);
526
+ results.projectContext = await writeContext(projectPath, projectContext, 'CONTEXT.md');
527
+
528
+ // Generate feature context if requested
529
+ if (featureName) {
530
+ const featureContext = await generateFeatureContext(projectPath, featureName);
531
+ results.featureContext = await writeContext(
532
+ projectPath,
533
+ featureContext,
534
+ `CONTEXT-${featureName}.md`
535
+ );
536
+ }
537
+
538
+ return results;
539
+ }