@polymorphism-tech/morph-spec 4.5.0 → 4.7.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 (292) hide show
  1. package/CLAUDE.md +77 -56
  2. package/README.md +394 -700
  3. package/docs/ARCHITECTURE.md +331 -0
  4. package/docs/CHEATSHEET.md +221 -0
  5. package/docs/COMMAND-FLOWS.md +368 -0
  6. package/docs/QUICKSTART.md +212 -0
  7. package/docs/examples/order-management/contracts.cs +84 -0
  8. package/docs/examples/order-management/proposal.md +24 -0
  9. package/docs/examples/order-management/spec.md +162 -0
  10. package/docs/plans/2026-02-23-ddd-architecture-refactor.md +1153 -0
  11. package/docs/plans/2026-02-23-ddd-nextsteps.md +682 -0
  12. package/docs/plans/2026-02-23-infra-architect-refactor.md +437 -0
  13. package/docs/plans/2026-02-23-nextjs-code-review-design.md +156 -0
  14. package/docs/plans/2026-02-23-nextjs-code-review-impl.md +1254 -0
  15. package/docs/plans/2026-02-23-nextjs-standards-design.md +149 -0
  16. package/docs/plans/2026-02-23-nextjs-standards-impl.md +1846 -0
  17. package/framework/{skills/level-2-domains → agents}/README.md +14 -14
  18. package/framework/{skills/level-2-domains → agents}/ai-agents/ai-system-architect.md +1 -4
  19. package/framework/{skills/level-2-domains → agents}/architecture/po-pm-advisor.md +1 -2
  20. package/framework/{skills/level-2-domains → agents}/architecture/prompt-engineer.md +1 -2
  21. package/framework/{skills/level-2-domains → agents}/architecture/seo-growth-hacker.md +1 -2
  22. package/framework/{skills/level-2-domains → agents}/architecture/standards-architect.md +159 -162
  23. package/framework/agents/backend/api-designer.md +103 -0
  24. package/framework/{skills/level-2-domains → agents}/backend/dotnet-senior.md +1 -2
  25. package/framework/agents/backend/ef-modeler.md +119 -0
  26. package/framework/{skills/level-2-domains → agents}/backend/hangfire-orchestrator.md +1 -4
  27. package/framework/{skills/level-2-domains → agents}/backend/ms-agent-expert.md +1 -4
  28. package/framework/{skills/level-2-domains → agents}/frontend/blazor-builder.md +1 -4
  29. package/framework/agents/frontend/nextjs-expert.md +118 -0
  30. package/framework/{skills/level-2-domains → agents}/frontend/ui-ux-designer.md +1 -2
  31. package/framework/{skills/level-2-domains → agents}/infrastructure/azure-architect.md +147 -148
  32. package/framework/{skills/level-2-domains → agents}/infrastructure/azure-deploy-specialist.md +1 -2
  33. package/framework/{skills/level-2-domains → agents}/infrastructure/bicep-architect.md +1 -4
  34. package/framework/{skills/level-2-domains → agents}/infrastructure/container-specialist.md +1 -4
  35. package/framework/{skills/level-2-domains → agents}/infrastructure/devops-engineer.md +1 -4
  36. package/framework/agents/infrastructure/infra-architect.md +45 -0
  37. package/framework/{skills/level-2-domains → agents}/integrations/asaas-financial.md +1 -4
  38. package/framework/{skills/level-2-domains → agents}/integrations/azure-identity.md +1 -4
  39. package/framework/{skills/level-2-domains → agents}/integrations/clerk-auth.md +1 -4
  40. package/framework/{skills/level-2-domains → agents}/integrations/hangfire-integration.md +1 -2
  41. package/framework/{skills/level-2-domains → agents}/integrations/resend-email.md +1 -4
  42. package/framework/{skills/level-2-domains → agents}/quality/code-analyzer.md +1 -4
  43. package/framework/{skills/level-2-domains → agents}/quality/testing-specialist.md +1 -4
  44. package/framework/agents.json +1145 -278
  45. package/framework/hooks/claude-code/statusline.py +384 -85
  46. package/framework/hooks/shared/phase-utils.js +129 -129
  47. package/framework/rules/frontend-standards.md +0 -3
  48. package/framework/rules/nextjs-standards.md +17 -0
  49. package/framework/skills/README.md +66 -0
  50. package/framework/skills/level-0-meta/{brainstorming.md → brainstorming/SKILL.md} +3 -1
  51. package/framework/skills/level-0-meta/brainstorming/references/proposal-example.md +138 -0
  52. package/framework/skills/level-0-meta/{code-review.md → code-review/SKILL.md} +3 -2
  53. package/framework/skills/level-0-meta/code-review/references/review-example.md +164 -0
  54. package/framework/skills/level-0-meta/code-review/scripts/scan-csharp.mjs +121 -0
  55. package/framework/skills/level-0-meta/code-review-nextjs/SKILL.md +147 -0
  56. package/framework/skills/level-0-meta/code-review-nextjs/references/review-example-nextjs.md +254 -0
  57. package/framework/skills/level-0-meta/{morph-checklist.md → morph-checklist/SKILL.md} +2 -5
  58. package/framework/skills/{level-1-workflows/morph-replicate.md → level-0-meta/morph-replicate/SKILL.md} +6 -7
  59. package/framework/skills/level-0-meta/{simulation-checklist.md → simulation-checklist/SKILL.md} +3 -6
  60. package/framework/skills/level-0-meta/{tool-usage-guide.md → tool-usage-guide/SKILL.md} +4 -5
  61. package/framework/skills/level-0-meta/{verification-before-completion.md → verification-before-completion/SKILL.md} +3 -1
  62. package/framework/skills/level-0-meta/verification-before-completion/scripts/check-phase-outputs.mjs +110 -0
  63. package/framework/skills/level-1-workflows/{phase-clarify.md → phase-clarify/SKILL.md} +3 -3
  64. package/framework/skills/level-1-workflows/phase-clarify/references/clarifications-example.md +117 -0
  65. package/framework/skills/level-1-workflows/{phase-codebase-analysis.md → phase-codebase-analysis/SKILL.md} +2 -3
  66. package/framework/skills/level-1-workflows/{phase-design.md → phase-design/SKILL.md} +46 -182
  67. package/framework/skills/level-1-workflows/phase-design/references/spec-example.md +253 -0
  68. package/framework/skills/level-1-workflows/{phase-implement.md → phase-implement/SKILL.md} +3 -3
  69. package/framework/skills/level-1-workflows/phase-implement/references/recap-example.md +132 -0
  70. package/framework/skills/level-1-workflows/{phase-setup.md → phase-setup/SKILL.md} +2 -3
  71. package/framework/skills/level-1-workflows/{phase-tasks.md → phase-tasks/SKILL.md} +42 -3
  72. package/framework/skills/level-1-workflows/phase-tasks/references/tasks-example.md +231 -0
  73. package/framework/skills/level-1-workflows/phase-tasks/scripts/validate-tasks.mjs +112 -0
  74. package/framework/skills/level-1-workflows/{phase-uiux.md → phase-uiux/SKILL.md} +2 -3
  75. package/framework/standards/STANDARDS.json +121 -0
  76. package/framework/standards/architecture/ddd/bounded-contexts.md +105 -0
  77. package/framework/standards/architecture/ddd/complexity-levels.md +108 -0
  78. package/framework/standards/architecture/ddd/ubiquitous-language.md +58 -0
  79. package/framework/standards/frontend/nextjs/app-router.md +123 -0
  80. package/framework/standards/frontend/nextjs/components.md +132 -0
  81. package/framework/standards/frontend/nextjs/data-fetching.md +126 -0
  82. package/framework/standards/frontend/nextjs/forms.md +128 -0
  83. package/framework/standards/frontend/nextjs/naming-conventions.md +67 -0
  84. package/framework/standards/frontend/nextjs/project-structure.md +102 -0
  85. package/framework/standards/frontend/nextjs/state-management.md +72 -0
  86. package/framework/standards/frontend/nextjs/testing.md +111 -0
  87. package/framework/templates/REGISTRY.json +538 -142
  88. package/framework/templates/code/dotnet/contracts/contracts-level1.cs +69 -0
  89. package/framework/templates/code/dotnet/contracts/contracts-level2.cs +86 -0
  90. package/framework/templates/code/dotnet/contracts/contracts-level3.cs +41 -0
  91. package/framework/templates/docs/spec.md +49 -0
  92. package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +43 -0
  93. package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -0
  94. package/framework/templates/frontend/nextjs/env.mjs.hbs +32 -0
  95. package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -0
  96. package/framework/templates/frontend/nextjs/page.tsx.hbs +22 -0
  97. package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -0
  98. package/framework/templates/frontend/nextjs/use-feature.ts.hbs +54 -0
  99. package/framework/templates/project-structure/dotnet-ddd.md +70 -0
  100. package/framework/workflows/docs/enforcement-pipeline.md +2 -1
  101. package/package.json +1 -1
  102. package/scripts/scan-nextjs.mjs +169 -0
  103. package/src/commands/project/doctor.js +52 -1
  104. package/src/commands/project/init.js +19 -65
  105. package/src/commands/project/update.js +7 -63
  106. package/src/lib/detectors/claude-config-detector.js +1 -3
  107. package/src/lib/standards/standards-context-injector.js +5 -0
  108. package/src/lib/validators/nextjs/index.js +6 -0
  109. package/src/lib/validators/nextjs/next-component-validator.js +181 -0
  110. package/src/lib/validators/validation-runner.js +5 -0
  111. package/src/utils/agents-installer.js +16 -4
  112. package/src/utils/skills-installer.js +59 -15
  113. package/.morph/.morphversion +0 -5
  114. package/.morph/analytics/threads-log.jsonl +0 -44
  115. package/.morph/config/config.json +0 -8
  116. package/.morph/context/README.md +0 -17
  117. package/.morph/framework/agents.json +0 -948
  118. package/.morph/framework/standards/STANDARDS.json +0 -812
  119. package/.morph/framework/standards/ai-agents/blazor-ui.md +0 -364
  120. package/.morph/framework/standards/ai-agents/production.md +0 -415
  121. package/.morph/framework/standards/ai-agents/setup.md +0 -418
  122. package/.morph/framework/standards/ai-agents/team-orchestration.md +0 -479
  123. package/.morph/framework/standards/ai-agents/workflows.md +0 -354
  124. package/.morph/framework/standards/architecture/ddd/aggregates.md +0 -120
  125. package/.morph/framework/standards/architecture/ddd/entities.md +0 -99
  126. package/.morph/framework/standards/architecture/ddd/value-objects.md +0 -124
  127. package/.morph/framework/standards/backend/api/minimal-api.md +0 -494
  128. package/.morph/framework/standards/backend/api/rest.md +0 -492
  129. package/.morph/framework/standards/backend/api/validation.md +0 -88
  130. package/.morph/framework/standards/backend/authentication/passkeys.md +0 -428
  131. package/.morph/framework/standards/backend/database/ef-core.md +0 -199
  132. package/.morph/framework/standards/backend/database/migrations.md +0 -393
  133. package/.morph/framework/standards/backend/database/postgresql/database.md +0 -352
  134. package/.morph/framework/standards/backend/database/repository-patterns.md +0 -528
  135. package/.morph/framework/standards/backend/database/vector-search-rag.md +0 -541
  136. package/.morph/framework/standards/backend/dotnet/async.md +0 -366
  137. package/.morph/framework/standards/backend/dotnet/core.md +0 -117
  138. package/.morph/framework/standards/backend/dotnet/di.md +0 -439
  139. package/.morph/framework/standards/backend/dotnet/program-cs-checklist.md +0 -92
  140. package/.morph/framework/standards/backend/integrations/asaas/asaas-api.md +0 -216
  141. package/.morph/framework/standards/backend/integrations/clerk/clerk-auth.md +0 -290
  142. package/.morph/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +0 -350
  143. package/.morph/framework/standards/backend/integrations/resend/resend-email.md +0 -385
  144. package/.morph/framework/standards/context/analytics.md +0 -96
  145. package/.morph/framework/standards/context/bundles.md +0 -110
  146. package/.morph/framework/standards/context/priming.md +0 -78
  147. package/.morph/framework/standards/core/architecture.md +0 -185
  148. package/.morph/framework/standards/core/coding.md +0 -214
  149. package/.morph/framework/standards/core/git-branching-strategy.md +0 -403
  150. package/.morph/framework/standards/core/git.md +0 -185
  151. package/.morph/framework/standards/core/testing.md +0 -295
  152. package/.morph/framework/standards/data/nosql/blob-storage.md +0 -102
  153. package/.morph/framework/standards/data/nosql/cache/redis.md +0 -97
  154. package/.morph/framework/standards/data/nosql/cosmos-db.md +0 -118
  155. package/.morph/framework/standards/data/vector-search/azure-ai-search.md +0 -121
  156. package/.morph/framework/standards/data/vector-search/rag-chunking.md +0 -104
  157. package/.morph/framework/standards/frontend/blazor/design-checklist.md +0 -222
  158. package/.morph/framework/standards/frontend/blazor/fluent-ui-setup.md +0 -595
  159. package/.morph/framework/standards/frontend/blazor/fluent-ui.md +0 -137
  160. package/.morph/framework/standards/frontend/blazor/html-conversion.md +0 -184
  161. package/.morph/framework/standards/frontend/blazor/lifecycle.md +0 -195
  162. package/.morph/framework/standards/frontend/blazor/pitfalls.md +0 -198
  163. package/.morph/framework/standards/frontend/blazor/state.md +0 -191
  164. package/.morph/framework/standards/frontend/design-system/animations.md +0 -151
  165. package/.morph/framework/standards/frontend/design-system/naming.md +0 -64
  166. package/.morph/framework/standards/frontend/nextjs/nextjs-patterns.md +0 -198
  167. package/.morph/framework/standards/infrastructure/azure/azure.md +0 -624
  168. package/.morph/framework/standards/infrastructure/azure/bicep/bicep-patterns.md +0 -422
  169. package/.morph/framework/standards/infrastructure/azure/devops/azure-devops-setup.md +0 -516
  170. package/.morph/framework/standards/infrastructure/azure/devops/local-development.md +0 -520
  171. package/.morph/framework/standards/infrastructure/azure/services/functions.md +0 -486
  172. package/.morph/framework/standards/infrastructure/azure/services/service-bus.md +0 -459
  173. package/.morph/framework/standards/infrastructure/azure/services/storage.md +0 -407
  174. package/.morph/framework/standards/infrastructure/docker/easypanel-deploy.md +0 -196
  175. package/.morph/framework/standards/infrastructure/supabase/mcp-setup.md +0 -252
  176. package/.morph/framework/standards/infrastructure/supabase/supabase-auth.md +0 -176
  177. package/.morph/framework/standards/infrastructure/supabase/supabase-pgvector.md +0 -169
  178. package/.morph/framework/standards/infrastructure/supabase/supabase-rls.md +0 -184
  179. package/.morph/framework/standards/infrastructure/supabase/supabase-storage.md +0 -153
  180. package/.morph/framework/standards/integration/api/graphql.md +0 -91
  181. package/.morph/framework/standards/integration/api/grpc.md +0 -114
  182. package/.morph/framework/standards/integration/api/rest-design.md +0 -95
  183. package/.morph/framework/standards/integration/event-driven/cqrs.md +0 -101
  184. package/.morph/framework/standards/integration/event-driven/event-sourcing.md +0 -124
  185. package/.morph/framework/standards/integration/event-driven/service-bus.md +0 -95
  186. package/.morph/framework/standards/integration/mcp/mcp-tools.md +0 -384
  187. package/.morph/framework/standards/observability/logging.md +0 -131
  188. package/.morph/framework/standards/observability/metrics.md +0 -121
  189. package/.morph/framework/standards/observability/monitoring.md +0 -114
  190. package/.morph/framework/standards/observability/tracing.md +0 -132
  191. package/.morph/framework/standards/workflows/parallel-execution.md +0 -112
  192. package/.morph/framework/standards/workflows/thread-management.md +0 -113
  193. package/.morph/framework/templates/.idea/morph-templates.xml +0 -92
  194. package/.morph/framework/templates/.vscode/morph-templates.code-snippets +0 -186
  195. package/.morph/framework/templates/IDE-SNIPPETS.md +0 -266
  196. package/.morph/framework/templates/README.md +0 -814
  197. package/.morph/framework/templates/REGISTRY.json +0 -1492
  198. package/.morph/framework/templates/code/dotnet/backend/repository.cs +0 -141
  199. package/.morph/framework/templates/code/dotnet/backend/service.cs +0 -139
  200. package/.morph/framework/templates/code/dotnet/contracts/Commands.cs +0 -74
  201. package/.morph/framework/templates/code/dotnet/contracts/Entities.cs +0 -25
  202. package/.morph/framework/templates/code/dotnet/contracts/Queries.cs +0 -74
  203. package/.morph/framework/templates/code/dotnet/contracts/README.md +0 -74
  204. package/.morph/framework/templates/code/dotnet/contracts/api-contracts.cs +0 -173
  205. package/.morph/framework/templates/code/dotnet/contracts/contracts.cs +0 -217
  206. package/.morph/framework/templates/code/dotnet/contracts/contracts.cs.hbs +0 -172
  207. package/.morph/framework/templates/code/dotnet/database/migration.cs +0 -83
  208. package/.morph/framework/templates/code/dotnet/frontend/component.razor +0 -239
  209. package/.morph/framework/templates/code/dotnet/jobs/agent.cs +0 -163
  210. package/.morph/framework/templates/code/dotnet/jobs/job.cs +0 -171
  211. package/.morph/framework/templates/code/dotnet/test.cs +0 -239
  212. package/.morph/framework/templates/code/sql/rls-policy.sql +0 -57
  213. package/.morph/framework/templates/code/sql/supabase-migration.sql +0 -100
  214. package/.morph/framework/templates/code/sql/supabase-migration.template.sql +0 -113
  215. package/.morph/framework/templates/code/typescript/contracts.ts +0 -168
  216. package/.morph/framework/templates/context/CONTEXT-FEATURE.md +0 -276
  217. package/.morph/framework/templates/context/CONTEXT.md +0 -181
  218. package/.morph/framework/templates/docs/clarifications.md +0 -253
  219. package/.morph/framework/templates/docs/onboarding.md +0 -123
  220. package/.morph/framework/templates/docs/proposal.md +0 -182
  221. package/.morph/framework/templates/docs/schema-analysis.md +0 -119
  222. package/.morph/framework/templates/docs/spec.md +0 -149
  223. package/.morph/framework/templates/docs/ui-components.md +0 -124
  224. package/.morph/framework/templates/docs/ui-design-system.md +0 -76
  225. package/.morph/framework/templates/docs/ui-flows.md +0 -167
  226. package/.morph/framework/templates/docs/ui-mockups.md +0 -98
  227. package/.morph/framework/templates/examples/design-system-examples.md +0 -357
  228. package/.morph/framework/templates/examples/spec-examples.md +0 -90
  229. package/.morph/framework/templates/feature/decisions.md +0 -187
  230. package/.morph/framework/templates/feature/recap.md +0 -146
  231. package/.morph/framework/templates/feature/tasks.md +0 -199
  232. package/.morph/framework/templates/infrastructure/azure/Dockerfile.example +0 -82
  233. package/.morph/framework/templates/infrastructure/azure/README.md +0 -286
  234. package/.morph/framework/templates/infrastructure/azure/app-insights.bicep +0 -63
  235. package/.morph/framework/templates/infrastructure/azure/app-service.bicep +0 -164
  236. package/.morph/framework/templates/infrastructure/azure/container-app-env.bicep +0 -49
  237. package/.morph/framework/templates/infrastructure/azure/container-app.bicep +0 -156
  238. package/.morph/framework/templates/infrastructure/azure/deploy-checklist.md +0 -426
  239. package/.morph/framework/templates/infrastructure/azure/deploy.ps1 +0 -229
  240. package/.morph/framework/templates/infrastructure/azure/deploy.sh +0 -208
  241. package/.morph/framework/templates/infrastructure/azure/key-vault.bicep +0 -91
  242. package/.morph/framework/templates/infrastructure/azure/main.bicep +0 -189
  243. package/.morph/framework/templates/infrastructure/azure/parameters.dev.json +0 -29
  244. package/.morph/framework/templates/infrastructure/azure/parameters.prod.json +0 -29
  245. package/.morph/framework/templates/infrastructure/azure/parameters.staging.json +0 -29
  246. package/.morph/framework/templates/infrastructure/azure/sql-database.bicep +0 -103
  247. package/.morph/framework/templates/infrastructure/azure/storage.bicep +0 -106
  248. package/.morph/framework/templates/infrastructure/docker/Dockerfile.template +0 -58
  249. package/.morph/framework/templates/infrastructure/docker/docker-compose.template.yml +0 -67
  250. package/.morph/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +0 -38
  251. package/.morph/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +0 -48
  252. package/.morph/framework/templates/infrastructure/docker/easypanel.template.json +0 -54
  253. package/.morph/framework/templates/infrastructure/github/README.md +0 -593
  254. package/.morph/framework/templates/infrastructure/github/actions/azure-auth/action.yml.hbs +0 -22
  255. package/.morph/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +0 -45
  256. package/.morph/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +0 -27
  257. package/.morph/framework/templates/infrastructure/github/workflows/deploy-azure-app-service.yml.hbs +0 -61
  258. package/.morph/framework/templates/infrastructure/github/workflows/deploy-easypanel.yml.hbs +0 -31
  259. package/.morph/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +0 -59
  260. package/.morph/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +0 -39
  261. package/.morph/framework/templates/integrations/asaas-client.cs +0 -387
  262. package/.morph/framework/templates/integrations/asaas-webhook.cs +0 -351
  263. package/.morph/framework/templates/integrations/azure-identity-config.cs +0 -288
  264. package/.morph/framework/templates/integrations/clerk-config.cs +0 -258
  265. package/.morph/framework/templates/meta-prompts/fusion/fusion-agent.md +0 -76
  266. package/.morph/framework/templates/meta-prompts/fusion/fusion-aggregator.md +0 -100
  267. package/.morph/framework/templates/meta-prompts/hops/hop-retry.md +0 -78
  268. package/.morph/framework/templates/meta-prompts/hops/hop-validation.md +0 -97
  269. package/.morph/framework/templates/meta-prompts/hops/hop-wrapper.md +0 -36
  270. package/.morph/framework/templates/meta-prompts/parallel-workers/parallel-coordinator.md +0 -113
  271. package/.morph/framework/templates/meta-prompts/parallel-workers/parallel-worker.md +0 -80
  272. package/.morph/framework/templates/meta-prompts/squad-leaders/backend-squad.md +0 -90
  273. package/.morph/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +0 -126
  274. package/.morph/framework/templates/meta-prompts/squad-leaders/squad-leader.md +0 -43
  275. package/.morph/framework/templates/meta-prompts/validators/checkpoint-validator.md +0 -107
  276. package/.morph/framework/templates/meta-prompts/validators/pre-commit-validator.md +0 -95
  277. package/.morph/framework/templates/saas/subscription.cs +0 -347
  278. package/.morph/framework/templates/saas/tenant.cs +0 -338
  279. package/.morph/framework/templates/state.template.json +0 -17
  280. package/.morph/framework/templates/ui/FluentDesignTheme.cs +0 -149
  281. package/.morph/framework/templates/ui/MudTheme.cs +0 -281
  282. package/.morph/framework/templates/ui/design-system.css +0 -226
  283. package/.morph/logs/tool-failures.log +0 -51
  284. package/.morph/memory/pre-compact-2026-02-22T17-01-01-658Z.json +0 -16
  285. package/.morph/state.json +0 -48
  286. package/framework/skills/level-2-domains/backend/api-designer.md +0 -66
  287. package/framework/skills/level-2-domains/backend/ef-modeler.md +0 -65
  288. package/framework/skills/level-2-domains/frontend/nextjs-expert.md +0 -161
  289. package/framework/skills/level-3-technologies/README.md +0 -7
  290. package/framework/skills/level-4-patterns/README.md +0 -7
  291. package/framework/templates/code/dotnet/contracts/contracts.cs +0 -217
  292. package/framework/templates/code/dotnet/contracts/contracts.cs.hbs +0 -172
@@ -8,7 +8,10 @@ import json
8
8
  import os
9
9
  import subprocess
10
10
  import time
11
+ import re
12
+ import hashlib
11
13
  from pathlib import Path
14
+ from datetime import datetime, timezone
12
15
 
13
16
  # Ensure UTF-8 output on Windows (stdout defaults to CP1252 otherwise)
14
17
  if hasattr(sys.stdout, 'reconfigure'):
@@ -27,10 +30,52 @@ GRAY = '\033[90m'
27
30
  WHITE = '\033[97m'
28
31
 
29
32
 
33
+ # ── MORPH framework constants (derived from phases.json / trust-manager.js) ──
34
+
35
+ # Ordered core phases for pipeline mini-map (5 positions, optional phases mapped)
36
+ # uiux maps to position 2 (same slot as design — they're mutually exclusive in practice)
37
+ PHASE_POSITIONS = {
38
+ 'proposal': 1, 'setup': 1,
39
+ 'uiux': 2, 'design': 2,
40
+ 'clarify': 3,
41
+ 'tasks': 4,
42
+ 'implement': 5, 'sync': 5,
43
+ }
44
+ PHASE_ABBREV = {
45
+ 'proposal': 'prop', 'setup': 'setup',
46
+ 'uiux': 'ui', 'design': 'design',
47
+ 'clarify': 'clarify', 'tasks': 'tasks',
48
+ 'implement': 'impl', 'sync': 'sync',
49
+ }
50
+ PIPELINE_TOTAL = 5
51
+
52
+ # Approval gates per phase (from phases.json pausePoints)
53
+ PHASE_GATES = {
54
+ 'proposal': 'proposal',
55
+ 'uiux': 'uiux',
56
+ 'design': 'design',
57
+ 'tasks': 'tasks',
58
+ }
59
+
60
+ # Trust level thresholds and badges (from trust-manager.js)
61
+ # passRate >= threshold → level
62
+ TRUST_LEVELS = [
63
+ (0.95, 'maximum', GREEN + BOLD, '◆◆◆◆'),
64
+ (0.90, 'high', GREEN, '◆◆◆○'),
65
+ (0.80, 'medium', YELLOW, '◆◆○○'),
66
+ (0.00, 'low', RED, '◆○○○'),
67
+ ]
68
+
69
+ CHECKPOINT_FREQUENCY = 3 # matches llm-interaction.json default
70
+
71
+
72
+ # ── General helpers ──────────────────────────────────────────────────────────
73
+
30
74
  def ctx_color(pct):
31
- if pct < 70:
75
+ """Color based on context usage. 80% = Claude's auto-compact threshold."""
76
+ if pct < 60:
32
77
  return GREEN
33
- if pct < 90:
78
+ if pct < 80:
34
79
  return YELLOW
35
80
  return RED
36
81
 
@@ -42,13 +87,113 @@ def progress_bar(pct, width=8):
42
87
 
43
88
 
44
89
  def format_tokens(n):
90
+ if n >= 1_000_000:
91
+ return f"{n / 1_000_000:.1f}m"
45
92
  if n >= 1000:
46
93
  return f"{n // 1000}k"
47
94
  return str(n)
48
95
 
49
96
 
97
+ # ── MORPH feature helpers ────────────────────────────────────────────────────
98
+
99
+ def calculate_trust(checkpoints):
100
+ """Return (level_str, color, badge, pass_rate) from checkpoint array."""
101
+ if not checkpoints:
102
+ return 'low', RED, '○○○○', 0.0
103
+ total = len(checkpoints)
104
+ passed = sum(1 for c in checkpoints if c.get('passed'))
105
+ rate = passed / total
106
+ for threshold, level, color, badge in TRUST_LEVELS:
107
+ if rate >= threshold:
108
+ return level, color, badge, rate
109
+ return 'low', RED, '○○○○', rate
110
+
111
+
112
+ def get_phase_minimap(phase):
113
+ """Return colored dot strip + phase abbrev, e.g. '●►○○○ design'."""
114
+ pos = PHASE_POSITIONS.get(phase)
115
+ if pos is None:
116
+ return None
117
+ dots = ''
118
+ for i in range(1, PIPELINE_TOTAL + 1):
119
+ if i < pos:
120
+ dots += f"{GREEN}●{R}"
121
+ elif i == pos:
122
+ dots += f"{CYAN}►{R}"
123
+ else:
124
+ dots += f"{GRAY}○{R}"
125
+ abbrev = PHASE_ABBREV.get(phase, phase)
126
+ return f"{dots} {CYAN}{abbrev}{R}"
127
+
128
+
129
+ def get_checkpoint_countdown(tasks_done):
130
+ """Tasks remaining until next checkpoint fires (frequency=3)."""
131
+ if tasks_done <= 0:
132
+ return None
133
+ remaining = CHECKPOINT_FREQUENCY - (tasks_done % CHECKPOINT_FREQUENCY)
134
+ return 0 if remaining == CHECKPOINT_FREQUENCY else remaining
135
+
136
+
137
+ def get_next_gate(phase, approval_gates):
138
+ """Return the upcoming gate for this phase if not yet triggered in state."""
139
+ gate_id = PHASE_GATES.get(phase)
140
+ if not gate_id:
141
+ return None
142
+ # If the gate already appears in approvalGates (approved or pending),
143
+ # it's either done or already shown as pending — don't duplicate.
144
+ if gate_id in (approval_gates or {}):
145
+ return None
146
+ return gate_id
147
+
148
+
149
+ def get_all_active_features(cwd):
150
+ """Return list of all in_progress features with enriched MORPH metadata."""
151
+ state_path = Path(cwd) / '.morph' / 'state.json'
152
+ if not state_path.exists():
153
+ return []
154
+ try:
155
+ state = json.loads(state_path.read_text())
156
+ features = state.get('features', {})
157
+ result = []
158
+ for name, feat in features.items():
159
+ if feat.get('status') != 'in_progress':
160
+ continue
161
+ phase = feat.get('phase', '?')
162
+ tasks = feat.get('tasks', {})
163
+ done = tasks.get('completed', 0)
164
+ total = tasks.get('total', 0)
165
+ gates = feat.get('approvalGates', {})
166
+ checkpts = feat.get('checkpoints', [])
167
+
168
+ pending = [g for g, v in gates.items() if not v.get('approved')]
169
+ trust_lvl, trust_color, trust_badge, trust_rate = calculate_trust(checkpts)
170
+ countdown = get_checkpoint_countdown(done)
171
+ next_gate = get_next_gate(phase, gates)
172
+ minimap = get_phase_minimap(phase)
173
+
174
+ result.append({
175
+ 'name': name,
176
+ 'phase': phase,
177
+ 'tasks_done': done,
178
+ 'tasks_total': total,
179
+ 'pending': pending[0] if pending else None,
180
+ 'trust_level': trust_lvl,
181
+ 'trust_color': trust_color,
182
+ 'trust_badge': trust_badge,
183
+ 'trust_rate': trust_rate,
184
+ 'countdown': countdown,
185
+ 'next_gate': next_gate,
186
+ 'minimap': minimap,
187
+ })
188
+ return result
189
+ except Exception:
190
+ return []
191
+
192
+
193
+ # ── Git helpers ───────────────────────────────────────────────────────────────
194
+
50
195
  def get_git_info(cwd):
51
- """Get git branch and file stats. Uses a 5s file cache to avoid lag."""
196
+ """Get git branch and diff stats. Uses a 5s file cache to avoid lag."""
52
197
  try:
53
198
  cache_file = Path(cwd) / '.morph' / '.git-cache'
54
199
  try:
@@ -64,31 +209,32 @@ def get_git_info(cwd):
64
209
  cwd=cwd, stderr=subprocess.DEVNULL, timeout=2
65
210
  ).decode().strip()
66
211
 
67
- status_out = subprocess.check_output(
68
- ['git', 'status', '--porcelain'],
69
- cwd=cwd, stderr=subprocess.DEVNULL, timeout=2
70
- ).decode()
71
-
72
- staged = sum(1 for l in status_out.splitlines() if l and l[0] in 'MADRC')
73
- modified = sum(1 for l in status_out.splitlines() if l and l[1] in 'MD')
74
- untracked = sum(1 for l in status_out.splitlines() if l.startswith('??'))
212
+ # Diff stats: insertions/deletions from staged + unstaged changes
213
+ ins, dels = 0, 0
214
+ for cmd in [['diff', '--shortstat'], ['diff', '--cached', '--shortstat']]:
215
+ try:
216
+ out = subprocess.check_output(
217
+ ['git'] + cmd, cwd=cwd, stderr=subprocess.DEVNULL, timeout=2
218
+ ).decode()
219
+ m = re.search(r'(\d+) insertion', out)
220
+ if m:
221
+ ins += int(m.group(1))
222
+ m = re.search(r'(\d+) deletion', out)
223
+ if m:
224
+ dels += int(m.group(1))
225
+ except Exception:
226
+ pass
75
227
 
76
228
  parts = [f"{BLUE} {branch}{R}"]
77
- if staged:
78
- parts.append(f"{GREEN}+{staged}{R}")
79
- if modified:
80
- parts.append(f"{YELLOW}~{modified}{R}")
81
- if untracked:
82
- parts.append(f"{GRAY}?{untracked}{R}")
229
+ if ins or dels:
230
+ parts.append(f"{GREEN}+{ins}{R}{GRAY},{R}{RED}-{dels}{R}")
83
231
 
84
232
  result = ' '.join(parts)
85
-
86
233
  try:
87
234
  cache_file.parent.mkdir(parents=True, exist_ok=True)
88
235
  cache_file.write_text(result)
89
236
  except Exception:
90
237
  pass
91
-
92
238
  return result
93
239
  except Exception:
94
240
  return ""
@@ -97,71 +243,164 @@ def get_git_info(cwd):
97
243
  def get_worktree_info(cwd):
98
244
  """Detect if running in a git worktree (not the main worktree)."""
99
245
  try:
100
- worktrees_out = subprocess.check_output(
246
+ out = subprocess.check_output(
101
247
  ['git', 'worktree', 'list', '--porcelain'],
102
248
  cwd=cwd, stderr=subprocess.DEVNULL, timeout=2
103
249
  ).decode()
104
-
105
- entries = []
106
- current = {}
107
- for line in worktrees_out.splitlines():
250
+ entries, current = [], {}
251
+ for line in out.splitlines():
108
252
  if line.startswith('worktree '):
109
253
  if current:
110
254
  entries.append(current)
111
255
  current = {'path': line.split(' ', 1)[1]}
112
256
  elif line.startswith('branch '):
113
257
  current['branch'] = line.split(' ', 1)[1]
114
-
115
258
  if current:
116
259
  entries.append(current)
117
-
118
260
  if len(entries) > 1:
119
- cwd_resolved = str(Path(cwd).resolve())
261
+ cwd_r = str(Path(cwd).resolve())
120
262
  for entry in entries[1:]:
121
- entry_path = str(Path(entry.get('path', '')).resolve())
122
- if cwd_resolved == entry_path:
123
- branch_ref = entry.get('branch', '')
124
- branch = branch_ref.replace('refs/heads/', '')
263
+ if str(Path(entry.get('path', '')).resolve()) == cwd_r:
264
+ branch = entry.get('branch', '').replace('refs/heads/', '')
125
265
  return f"{MAGENTA}worktree:{branch}{R}"
126
266
  except Exception:
127
267
  pass
128
268
  return ""
129
269
 
130
270
 
131
- def get_morph_info(cwd):
132
- """Read active feature info from .morph/state.json."""
133
- state_path = Path(cwd) / '.morph' / 'state.json'
134
- if not state_path.exists():
271
+ # ── Transcript / JSONL helpers ────────────────────────────────────────────────
272
+
273
+ def read_transcript_jsonl(path):
274
+ """Read and parse JSONL transcript file. Returns list of parsed entries."""
275
+ entries = []
276
+ try:
277
+ with open(path, 'r', encoding='utf-8') as f:
278
+ for line in f:
279
+ line = line.strip()
280
+ if not line:
281
+ continue
282
+ try:
283
+ entries.append(json.loads(line))
284
+ except Exception:
285
+ pass
286
+ except Exception:
287
+ pass
288
+ return entries
289
+
290
+
291
+ def get_token_metrics(entries):
292
+ """Sum token usage from all non-sidechain JSONL entries."""
293
+ total_input, total_output, total_cached = 0, 0, 0
294
+ for entry in entries:
295
+ if entry.get('isSidechain') or entry.get('isApiErrorMessage'):
296
+ continue
297
+ usage = (entry.get('message') or {}).get('usage') or {}
298
+ if not usage:
299
+ continue
300
+ total_input += usage.get('input_tokens', 0)
301
+ total_output += usage.get('output_tokens', 0)
302
+ total_cached += (
303
+ usage.get('cache_creation_input_tokens', 0) +
304
+ usage.get('cache_read_input_tokens', 0)
305
+ )
306
+ return {'input': total_input, 'output': total_output, 'cached': total_cached}
307
+
308
+
309
+ def parse_timestamp(ts):
310
+ """Parse ISO 8601 timestamp to Unix float. Returns None on error."""
311
+ try:
312
+ return datetime.fromisoformat(ts.replace('Z', '+00:00')).timestamp()
313
+ except Exception:
135
314
  return None
315
+
316
+
317
+ def get_session_duration(entries):
318
+ """Elapsed time since first message. Returns 'Xhr Ym' or None."""
319
+ now = time.time()
320
+ for entry in entries:
321
+ ts = entry.get('timestamp')
322
+ if not ts:
323
+ continue
324
+ t = parse_timestamp(ts)
325
+ if t is None:
326
+ continue
327
+ elapsed_s = now - t
328
+ hours = int(elapsed_s // 3600)
329
+ minutes = int((elapsed_s % 3600) // 60)
330
+ if hours == 0:
331
+ return f"{minutes}m"
332
+ elif minutes == 0:
333
+ return f"{hours}hr"
334
+ else:
335
+ return f"{hours}hr {minutes}m"
336
+ return None
337
+
338
+
339
+ def get_session_name(entries):
340
+ """Find the most recent /rename title. Returns string or None."""
341
+ for entry in reversed(entries):
342
+ if entry.get('type') == 'custom-title' and entry.get('customTitle'):
343
+ return entry['customTitle']
344
+ return None
345
+
346
+
347
+ def get_block_start(transcript_path, entries):
348
+ """Find start of current 5-hour billing block. Cached per transcript."""
349
+ h = hashlib.sha256(transcript_path.encode()).hexdigest()[:16]
350
+ cache_dir = Path.home() / '.cache' / 'morph-spec'
351
+ cache_file = cache_dir / f'block-{h}.json'
352
+ now = time.time()
353
+ block_s = 5 * 3600
354
+
136
355
  try:
137
- state = json.loads(state_path.read_text())
138
- features = state.get('features', {})
139
- active = [
140
- (name, feat)
141
- for name, feat in features.items()
142
- if feat.get('status') == 'in_progress'
143
- ]
144
- if not active:
145
- return None
146
-
147
- name, feat = active[0]
148
- phase = feat.get('phase', '?')
149
- tasks = feat.get('tasks', {})
150
- done = tasks.get('completed', 0)
151
- total = tasks.get('total', 0)
152
- gates = feat.get('approvalGates', {})
153
- pending = [g for g, v in gates.items() if not v.get('approved')]
154
-
155
- return {
156
- 'name': name,
157
- 'phase': phase,
158
- 'tasks_done': done,
159
- 'tasks_total': total,
160
- 'pending_approval': pending[0] if pending else None,
161
- }
356
+ if cache_file.exists():
357
+ cached = json.loads(cache_file.read_text())
358
+ start = cached.get('block_start')
359
+ if start and (now - start) < block_s:
360
+ return start
162
361
  except Exception:
362
+ pass
363
+
364
+ start = None
365
+ for entry in entries:
366
+ ts = entry.get('timestamp')
367
+ if not ts:
368
+ continue
369
+ t = parse_timestamp(ts)
370
+ if t is None:
371
+ continue
372
+ if (now - t) <= block_s:
373
+ start = t
374
+ break
375
+
376
+ if start is None:
163
377
  return None
164
378
 
379
+ try:
380
+ cache_dir.mkdir(parents=True, exist_ok=True)
381
+ cache_file.write_text(json.dumps({'block_start': start}))
382
+ except Exception:
383
+ pass
384
+ return start
385
+
386
+
387
+ def format_block_timer(block_start):
388
+ """Format block timer as 'bar Xhr Ym' relative to 5-hour window."""
389
+ now = time.time()
390
+ elapsed_s = max(0.0, now - block_start)
391
+ pct = min(elapsed_s / (5 * 3600) * 100, 100)
392
+ hours = int(elapsed_s // 3600)
393
+ minutes = int((elapsed_s % 3600) // 60)
394
+ if hours == 0:
395
+ time_str = f"{minutes}m"
396
+ elif minutes == 0:
397
+ time_str = f"{hours}hr"
398
+ else:
399
+ time_str = f"{hours}hr {minutes}m"
400
+ return f"{progress_bar(pct, 6)} {time_str}"
401
+
402
+
403
+ # ── Main ──────────────────────────────────────────────────────────────────────
165
404
 
166
405
  def main():
167
406
  try:
@@ -172,43 +411,103 @@ def main():
172
411
  except Exception:
173
412
  sys.exit(0)
174
413
 
175
- cwd = data.get('cwd', os.getcwd())
176
-
177
- # === LINE 1: Morph status (only if morph project has active feature) ===
178
- morph = get_morph_info(cwd)
179
- if morph:
180
- parts1 = [
181
- f"{CYAN}{BOLD}{morph['name']}{R}",
182
- f"{MAGENTA}phase:{morph['phase']}{R}",
183
- ]
184
- if morph['tasks_total'] > 0:
185
- pct = morph['tasks_done'] / morph['tasks_total'] * 100
414
+ cwd = data.get('cwd', os.getcwd())
415
+ transcript_path = data.get('transcript_path')
416
+
417
+ # Read JSONL transcript once — shared by session clock, block timer,
418
+ # token metrics, and session name.
419
+ entries = read_transcript_jsonl(transcript_path) if transcript_path else []
420
+
421
+ # ── MORPH feature lines (one line per active feature) ────────────────────
422
+ features = get_all_active_features(cwd)
423
+ for feat in features:
424
+ parts = [f"{CYAN}{BOLD}{feat['name']}{R}"]
425
+
426
+ # Phase pipeline mini-map: ●●►○○ design
427
+ if feat['minimap']:
428
+ parts.append(feat['minimap'])
429
+
430
+ # Task progress bar
431
+ if feat['tasks_total'] > 0:
432
+ pct = feat['tasks_done'] / feat['tasks_total'] * 100
186
433
  bar = progress_bar(pct, 6)
187
- parts1.append(f"{GREEN}{bar} {morph['tasks_done']}/{morph['tasks_total']}{R}")
188
- if morph['pending_approval']:
189
- parts1.append(f"{YELLOW}⏳ {morph['pending_approval']} pending{R}")
190
- print(' | '.join(parts1))
434
+ parts.append(f"{GREEN}{bar} {feat['tasks_done']}/{feat['tasks_total']}{R}")
191
435
 
192
- # === LINE 2: Session info (always shown) ===
436
+ # Checkpoint countdown: how many tasks until next validation fires
437
+ if feat['countdown'] is not None:
438
+ if feat['countdown'] == 0:
439
+ parts.append(f"{GREEN}ckpt!{R}") # just hit checkpoint
440
+ elif feat['countdown'] == 1:
441
+ parts.append(f"{YELLOW}ckpt:1{R}") # 1 task away — heads up
442
+ else:
443
+ parts.append(f"{GRAY}ckpt:{feat['countdown']}{R}")
444
+
445
+ # Trust level badge: ◆◆◆○ high
446
+ tc = feat['trust_color']
447
+ parts.append(f"{tc}{feat['trust_badge']}{R}")
448
+
449
+ # Pending approval gate (blocking — already triggered, not yet approved)
450
+ if feat['pending']:
451
+ parts.append(f"{YELLOW}⏳ {feat['pending']} pending{R}")
452
+
453
+ # Upcoming gate (not yet triggered — reminds what comes at end of phase)
454
+ if feat['next_gate']:
455
+ parts.append(f"{GRAY}→gate:{feat['next_gate']}{R}")
456
+
457
+ print(' | '.join(parts))
458
+
459
+ # ── Session info line (always shown) ─────────────────────────────────────
193
460
  parts2 = []
194
461
 
462
+ # Session name (set via /rename)
463
+ if entries:
464
+ session_name = get_session_name(entries)
465
+ if session_name:
466
+ parts2.append(f"{CYAN}{BOLD}📌 {session_name}{R}")
467
+
195
468
  # Model
196
- model = data.get('model', {})
469
+ model = data.get('model', {})
197
470
  model_name = model.get('display_name', model.get('id', ''))
198
471
  if model_name:
199
472
  short = model_name.replace('Claude ', '').replace(' (claude.ai)', '')
200
473
  parts2.append(f"{WHITE}{BOLD}🤖 {short}{R}")
201
474
 
202
- # Context window
475
+ # Session clock (elapsed time since first message)
476
+ if entries:
477
+ duration = get_session_duration(entries)
478
+ if duration:
479
+ parts2.append(f"{YELLOW}⏱ {duration}{R}")
480
+
481
+ # Block timer (progress through current 5-hour billing window)
482
+ if entries and transcript_path:
483
+ block_start = get_block_start(transcript_path, entries)
484
+ if block_start is not None:
485
+ parts2.append(f"{YELLOW}blk:{format_block_timer(block_start)}{R}")
486
+
487
+ # Context window (60% = yellow, 80% = red/auto-compact threshold)
203
488
  ctx = data.get('context_window', {})
204
489
  if ctx:
205
- used_pct = ctx.get('used_percentage', 0)
206
- cur = ctx.get('current_usage', 0)
490
+ used_pct = ctx.get('used_percentage', 0)
491
+ cur = ctx.get('current_usage', 0)
207
492
  total_ctx = ctx.get('context_window_size', 0)
208
- color = ctx_color(used_pct)
209
- bar = progress_bar(used_pct, 8)
210
- toks = f"{format_tokens(cur)}/{format_tokens(total_ctx)}"
211
- parts2.append(f"{color}{bar} {used_pct:.0f}% ({toks} tok){R}")
493
+ color = ctx_color(used_pct)
494
+ bar = progress_bar(used_pct, 8)
495
+ toks = f"{format_tokens(cur)}/{format_tokens(total_ctx)}"
496
+ suffix = f" {RED}~cmpct{R}" if used_pct >= 80 else ""
497
+ parts2.append(f"{color}{bar} {used_pct:.0f}%{R} ({toks}){suffix}")
498
+
499
+ # Token breakdown from JSONL (session totals: input / output / cached)
500
+ if entries:
501
+ tok = get_token_metrics(entries)
502
+ tok_parts = []
503
+ if tok['input']:
504
+ tok_parts.append(f"in:{format_tokens(tok['input'])}")
505
+ if tok['output']:
506
+ tok_parts.append(f"out:{format_tokens(tok['output'])}")
507
+ if tok['cached']:
508
+ tok_parts.append(f"↩{format_tokens(tok['cached'])}")
509
+ if tok_parts:
510
+ parts2.append(f"{GRAY}{' '.join(tok_parts)}{R}")
212
511
 
213
512
  # Cost
214
513
  cost = data.get('cost', {})
@@ -221,7 +520,7 @@ def main():
221
520
  if agent.get('name'):
222
521
  parts2.append(f"{BLUE}agent:{agent['name']}{R}")
223
522
 
224
- # Git info (cached, non-blocking)
523
+ # Git info (branch + diff stats, 5s cached)
225
524
  git = get_git_info(cwd)
226
525
  if git:
227
526
  parts2.append(git)