@sureshdsk/devflow-mcp 3.0.0 → 3.0.1

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 (504) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +4 -4
  3. package/.next/standalone/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/.next/required-server-files.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +2 -2
  6. package/.next/standalone/.next/server/app/_global-error/page.js +1 -1
  7. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.html +2 -2
  9. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  12. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  13. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  14. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +2 -2
  16. package/.next/standalone/.next/server/app/_not-found/page.js +2 -2
  17. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  18. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  19. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  20. package/.next/standalone/.next/server/app/_not-found.rsc +14 -13
  21. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +14 -13
  22. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  23. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +5 -4
  24. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  25. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  26. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  27. package/.next/standalone/.next/server/app/api/projects/[id]/route.js +2 -2
  28. package/.next/standalone/.next/server/app/api/projects/[id]/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/projects/route.js +2 -2
  30. package/.next/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/specs/[name]/artifacts/[artifactType]/approve/route.js +2 -2
  32. package/.next/standalone/.next/server/app/api/specs/[name]/artifacts/[artifactType]/approve/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/specs/[name]/artifacts/[artifactType]/route.js +2 -2
  34. package/.next/standalone/.next/server/app/api/specs/[name]/artifacts/[artifactType]/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/specs/[name]/promote/route.js +2 -2
  36. package/.next/standalone/.next/server/app/api/specs/[name]/promote/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/api/specs/[name]/route.js +2 -2
  38. package/.next/standalone/.next/server/app/api/specs/[name]/route.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/api/specs/route.js +2 -2
  40. package/.next/standalone/.next/server/app/api/specs/route.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/api/tasks/[id]/route.js +2 -2
  42. package/.next/standalone/.next/server/app/api/tasks/[id]/route.js.nft.json +1 -1
  43. package/.next/standalone/.next/server/app/api/tasks/route.js +2 -2
  44. package/.next/standalone/.next/server/app/api/tasks/route.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/index.html +1 -1
  46. package/.next/standalone/.next/server/app/index.rsc +15 -14
  47. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  48. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -14
  49. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  50. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +5 -4
  51. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  52. package/.next/standalone/.next/server/app/page/build-manifest.json +2 -2
  53. package/.next/standalone/.next/server/app/page.js +2 -2
  54. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  55. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  56. package/.next/standalone/.next/server/app/specs/[name]/page/build-manifest.json +2 -2
  57. package/.next/standalone/.next/server/app/specs/[name]/page.js +2 -2
  58. package/.next/standalone/.next/server/app/specs/[name]/page.js.nft.json +1 -1
  59. package/.next/standalone/.next/server/app/specs/[name]/page_client-reference-manifest.js +1 -1
  60. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0be0eb8b._.js +3 -0
  61. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__a9d478ee._.js → [root-of-the-server]__160c54ca._.js} +2 -2
  62. package/.next/standalone/.next/server/chunks/[root-of-the-server]__17ac51e3._.js +3 -0
  63. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1bb083e9._.js +3 -0
  64. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1dc1e944._.js +3 -0
  65. package/.next/standalone/.next/server/chunks/[root-of-the-server]__22ecfa2c._.js +3 -0
  66. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__13a93eb6._.js → [root-of-the-server]__64710c0d._.js} +2 -2
  67. package/.next/standalone/.next/server/chunks/[root-of-the-server]__a2b7e5f2._.js +3 -0
  68. package/.next/standalone/.next/server/chunks/[root-of-the-server]__cf302bda._.js +3 -0
  69. package/.next/standalone/.next/server/chunks/[root-of-the-server]__d85482cd._.js +3 -0
  70. package/.next/standalone/.next/server/chunks/[root-of-the-server]__dfa21757._.js +3 -0
  71. package/.next/standalone/.next/server/chunks/_3f452afe._.js +2 -2
  72. package/.next/standalone/.next/server/chunks/src_db_index_ts_a71f50f0._.js +3 -0
  73. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__023e2c69._.js +3 -0
  74. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0a1a1435._.js +3 -0
  75. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0c49a387._.js +3 -0
  76. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1be1fc5f._.js +3 -0
  77. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__3a2f2efb._.js +3 -0
  78. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__4693399c._.js +3 -0
  79. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__937c9212._.js → [root-of-the-server]__72a14301._.js} +2 -2
  80. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__8064ca06._.js +3 -0
  81. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__8b786d48._.js +3 -0
  82. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__8c052461._.js +3 -0
  83. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__8d306066._.js +3 -0
  84. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__9532fd59._.js +3 -0
  85. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__a119dd75._.js +3 -0
  86. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__7fb6ef14._.js → [root-of-the-server]__a57b312b._.js} +2 -2
  87. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__a80bd0ea._.js +3 -0
  88. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__abe095b3._.js +3 -0
  89. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__ad3bd783._.js +3 -0
  90. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1e05fe0e._.js → [root-of-the-server]__ae9f05d6._.js} +2 -2
  91. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__c8781469._.js +3 -0
  92. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__c8a25070._.js +3 -0
  93. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__e8c1e595._.js +3 -0
  94. package/.next/standalone/.next/server/chunks/ssr/_0ff3de19._.js +1 -1
  95. package/.next/standalone/.next/server/chunks/ssr/node_modules_01a5cd4b._.js +3 -0
  96. package/.next/standalone/.next/server/chunks/ssr/node_modules_05c6819f._.js +31 -0
  97. package/.next/standalone/.next/server/chunks/ssr/node_modules_06c2c30a._.js +3 -0
  98. package/.next/standalone/.next/server/chunks/ssr/node_modules_09efe605._.js +3 -0
  99. package/.next/standalone/.next/server/chunks/ssr/{node_modules_mermaid_dist_chunks_mermaid_core_ef601841._.js → node_modules_0a439e3b._.js} +2 -2
  100. package/.next/standalone/.next/server/chunks/ssr/{node_modules_3507b41a._.js → node_modules_0e73ad6c._.js} +3 -3
  101. package/.next/standalone/.next/server/chunks/ssr/node_modules_0e7ff06d._.js +3 -0
  102. package/.next/standalone/.next/server/chunks/ssr/node_modules_0f73e25c._.js +3 -0
  103. package/.next/standalone/.next/server/chunks/ssr/node_modules_14cf9cea._.js +3 -0
  104. package/.next/standalone/.next/server/chunks/ssr/node_modules_19801f01._.js +3 -0
  105. package/.next/standalone/.next/server/chunks/ssr/node_modules_2235b8cf._.js +17 -0
  106. package/.next/standalone/.next/server/chunks/ssr/node_modules_23096c6d._.js +3 -0
  107. package/.next/standalone/.next/server/chunks/ssr/node_modules_23db4cf7._.js +17 -0
  108. package/.next/standalone/.next/server/chunks/ssr/node_modules_24381e95._.js +3 -0
  109. package/.next/standalone/.next/server/chunks/ssr/node_modules_24ca0eb6._.js +17 -0
  110. package/.next/standalone/.next/server/chunks/ssr/node_modules_27525793._.js +17 -0
  111. package/.next/standalone/.next/server/chunks/ssr/{node_modules_mermaid_dist_chunks_mermaid_core_4cfa6360._.js → node_modules_27c20382._.js} +2 -2
  112. package/.next/standalone/.next/server/chunks/ssr/node_modules_2c901b92._.js +3 -0
  113. package/.next/standalone/.next/server/chunks/ssr/node_modules_33b818b7._.js +45 -0
  114. package/.next/standalone/.next/server/chunks/ssr/node_modules_345d7791._.js +3 -0
  115. package/.next/standalone/.next/server/chunks/ssr/node_modules_36185c24._.js +45 -0
  116. package/.next/standalone/.next/server/chunks/ssr/node_modules_3ac7ce09._.js +3 -0
  117. package/.next/standalone/.next/server/chunks/ssr/node_modules_41b77cc7._.js +3 -0
  118. package/.next/standalone/.next/server/chunks/ssr/{node_modules_ed317ff6._.js → node_modules_42acd8db._.js} +2 -2
  119. package/.next/standalone/.next/server/chunks/ssr/node_modules_434aa5ec._.js +3 -0
  120. package/.next/standalone/.next/server/chunks/ssr/node_modules_54a9e72d._.js +17 -0
  121. package/.next/standalone/.next/server/chunks/ssr/node_modules_54f1273a._.js +26 -0
  122. package/.next/standalone/.next/server/chunks/ssr/node_modules_58d64491._.js +3 -0
  123. package/.next/standalone/.next/server/chunks/ssr/node_modules_5e012e3d._.js +3 -0
  124. package/.next/standalone/.next/server/chunks/ssr/node_modules_5ea9e4cf._.js +3 -0
  125. package/.next/standalone/.next/server/chunks/ssr/node_modules_5ecf8270._.js +26 -0
  126. package/.next/standalone/.next/server/chunks/ssr/node_modules_611c99b5._.js +1 -1
  127. package/.next/standalone/.next/server/chunks/ssr/{node_modules_d3-scale_src_e02ef77f._.js → node_modules_6629853d._.js} +2 -2
  128. package/.next/standalone/.next/server/chunks/ssr/node_modules_6fe2661c._.js +3 -0
  129. package/.next/standalone/.next/server/chunks/ssr/node_modules_7fe65a84._.js +3 -0
  130. package/.next/standalone/.next/server/chunks/ssr/node_modules_860f971a._.js +3 -0
  131. package/.next/standalone/.next/server/chunks/ssr/node_modules_882f3743._.js +3 -0
  132. package/.next/standalone/.next/server/chunks/ssr/node_modules_8e047938._.js +3 -0
  133. package/.next/standalone/.next/server/chunks/ssr/node_modules_9268cdc4._.js +3 -0
  134. package/.next/standalone/.next/server/chunks/ssr/node_modules_942541c9._.js +3 -0
  135. package/.next/standalone/.next/server/chunks/ssr/node_modules_9d52c0d6._.js +3 -0
  136. package/.next/standalone/.next/server/chunks/ssr/node_modules_@tanstack_query-core_build_modern_21ece5ea._.js +3 -0
  137. package/.next/standalone/.next/server/chunks/ssr/node_modules_ae6841d2._.js +17 -0
  138. package/.next/standalone/.next/server/chunks/ssr/node_modules_ba280bb0._.js +31 -0
  139. package/.next/standalone/.next/server/chunks/ssr/node_modules_bffdb8ec._.js +3 -0
  140. package/.next/standalone/.next/server/chunks/ssr/node_modules_c00f1821._.js +17 -0
  141. package/.next/standalone/.next/server/chunks/ssr/node_modules_c142ba86._.js +3 -0
  142. package/.next/standalone/.next/server/chunks/ssr/node_modules_d1a47cde._.js +17 -0
  143. package/.next/standalone/.next/server/chunks/ssr/node_modules_d9b454fe._.js +3 -0
  144. package/.next/standalone/.next/server/chunks/ssr/node_modules_dbe36b1d._.js +3 -0
  145. package/.next/standalone/.next/server/chunks/ssr/node_modules_ddbe26b7._.js +3 -0
  146. package/.next/standalone/.next/server/chunks/ssr/node_modules_e76b8bf2._.js +3 -0
  147. package/.next/standalone/.next/server/chunks/ssr/node_modules_e945b74d._.js +3 -0
  148. package/.next/standalone/.next/server/chunks/ssr/node_modules_fbd8a2ee._.js +17 -0
  149. package/.next/standalone/.next/server/chunks/ssr/node_modules_katex_dist_katex_mjs_31d456ba._.js +3 -0
  150. package/.next/standalone/.next/server/chunks/ssr/node_modules_katex_dist_katex_mjs_e1edc676._.js +3 -0
  151. package/.next/standalone/.next/server/chunks/ssr/node_modules_lodash-es_33e24dce._.js +3 -0
  152. package/.next/standalone/.next/server/chunks/ssr/node_modules_mermaid_dist_chunks_mermaid_core_chunk-QXUST7PY_mjs_159fba97._.js +1 -1
  153. package/.next/standalone/.next/server/chunks/ssr/node_modules_mermaid_dist_chunks_mermaid_core_chunk-S3R3BYOJ_mjs_dbeeb277._.js +1 -1
  154. package/.next/standalone/.next/server/chunks/ssr/node_modules_mermaid_dist_mermaid_core_mjs_a11916de._.js +9 -0
  155. package/.next/standalone/.next/server/chunks/ssr/src_66a70595._.js +3 -0
  156. package/.next/standalone/.next/server/chunks/ssr/src_components_kanban-board_tsx_7b875e8e._.js +1 -1
  157. package/.next/standalone/.next/server/middleware-build-manifest.js +2 -2
  158. package/.next/standalone/.next/server/pages/404.html +1 -1
  159. package/.next/standalone/.next/server/pages/500.html +2 -2
  160. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  161. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  162. package/.next/standalone/AGENTS.md +13 -7
  163. package/.next/standalone/CONTRIBUTING.md +42 -26
  164. package/.next/standalone/README.md +166 -25
  165. package/.next/standalone/bin/devflow.js +12 -5
  166. package/.next/standalone/bun.lock +78 -24
  167. package/.next/standalone/devflow/project-config.json +4 -0
  168. package/.next/standalone/devflow/schemas/api-first/schema.yaml +39 -0
  169. package/.next/standalone/devflow/schemas/api-first/templates/api-design.md +41 -0
  170. package/.next/standalone/devflow/schemas/api-first/templates/db-design.md +28 -0
  171. package/.next/standalone/devflow/schemas/api-first/templates/final-architecture.md +31 -0
  172. package/.next/standalone/devflow/schemas/api-first/templates/proposal.md +22 -0
  173. package/.next/standalone/devflow/schemas/api-first/templates/tasks.md +16 -0
  174. package/.next/standalone/devflow/specs/autodetect-skill-and-slash-command-installation/.approvals.json +1 -1
  175. package/.next/standalone/devflow/specs/autodetect-skill-and-slash-command-installation/.meta.json +1 -1
  176. package/.next/standalone/devflow/specs/autodetect-skill-and-slash-command-installation/design.md +11 -0
  177. package/.next/standalone/devflow/specs/autodetect-skill-and-slash-command-installation/proposal.md +3 -0
  178. package/.next/standalone/devflow/specs/autodetect-skill-and-slash-command-installation/tasks.md +34 -2
  179. package/.next/standalone/devflow/specs/precommit/.approvals.json +26 -0
  180. package/.next/standalone/devflow/specs/precommit/.meta.json +7 -0
  181. package/.next/standalone/devflow/specs/precommit/design.md +105 -0
  182. package/.next/standalone/devflow/specs/precommit/proposal.md +43 -0
  183. package/.next/standalone/devflow/specs/precommit/specs.md +89 -0
  184. package/.next/standalone/devflow/specs/precommit/tasks.md +283 -0
  185. package/.next/standalone/docs/custom-schemas.md +117 -0
  186. package/.next/standalone/docs/local-dev-guide.md +73 -0
  187. package/.next/standalone/drizzle.config.ts +7 -7
  188. package/.next/standalone/eslint.config.mjs +2 -2
  189. package/.next/standalone/instrumentation.ts +3 -5
  190. package/.next/standalone/next.config.ts +11 -2
  191. package/.next/standalone/node_modules/{sharp → libsql}/node_modules/detect-libc/package.json +4 -8
  192. package/.next/standalone/package-lock.json +5257 -2085
  193. package/.next/standalone/package.json +16 -7
  194. package/.next/standalone/postcss.config.mjs +1 -1
  195. package/.next/standalone/scripts/init-db.js +3 -5
  196. package/.next/standalone/scripts/init-db.test.js +6 -6
  197. package/.next/standalone/scripts/init-schema.js +22 -22
  198. package/.next/standalone/scripts/init-schema.test.js +45 -45
  199. package/.next/standalone/scripts/install-environments.js +27 -8
  200. package/.next/standalone/scripts/install-environments.test.js +19 -10
  201. package/.next/standalone/scripts/install-skills.js +104 -31
  202. package/.next/standalone/scripts/install-skills.test.js +6 -6
  203. package/.next/standalone/server.js +1 -1
  204. package/.next/standalone/src/app/api/agents/route.ts +2 -2
  205. package/.next/standalone/src/app/api/projects/[id]/route.ts +10 -19
  206. package/.next/standalone/src/app/api/projects/route.ts +4 -10
  207. package/.next/standalone/src/app/api/specs/[name]/artifacts/[artifactType]/approve/route.ts +8 -8
  208. package/.next/standalone/src/app/api/specs/[name]/artifacts/[artifactType]/route.ts +14 -14
  209. package/.next/standalone/src/app/api/specs/[name]/promote/route.ts +17 -23
  210. package/.next/standalone/src/app/api/specs/[name]/route.ts +34 -36
  211. package/.next/standalone/src/app/api/specs/route.ts +10 -10
  212. package/.next/standalone/src/app/api/tasks/[id]/route.ts +11 -33
  213. package/.next/standalone/src/app/api/tasks/route.ts +3 -6
  214. package/.next/standalone/src/app/globals.css +3 -3
  215. package/.next/standalone/src/app/layout.tsx +9 -5
  216. package/.next/standalone/src/app/page.tsx +1 -1
  217. package/.next/standalone/src/app/specs/[name]/page.tsx +26 -68
  218. package/.next/standalone/src/components/kanban-board.tsx +78 -157
  219. package/.next/standalone/src/components/kanban-column.tsx +10 -12
  220. package/.next/standalone/src/components/markdown-preview.tsx +36 -19
  221. package/.next/standalone/src/components/providers.tsx +20 -0
  222. package/.next/standalone/src/components/specs/artifact-dag-status.tsx +12 -14
  223. package/.next/standalone/src/components/specs/artifact-editor.tsx +55 -26
  224. package/.next/standalone/src/components/specs/spec-detail.tsx +48 -40
  225. package/.next/standalone/src/components/specs/spec-kanban-column.tsx +49 -68
  226. package/.next/standalone/src/components/specs/spec-modal.tsx +119 -83
  227. package/.next/standalone/src/components/task-card.tsx +34 -38
  228. package/.next/standalone/src/components/task-dialog.tsx +16 -31
  229. package/.next/standalone/src/components/ui/badge.tsx +12 -18
  230. package/.next/standalone/src/components/ui/button.tsx +23 -28
  231. package/.next/standalone/src/components/ui/card.tsx +42 -62
  232. package/.next/standalone/src/components/ui/dialog.tsx +17 -35
  233. package/.next/standalone/src/db/index.ts +10 -10
  234. package/.next/standalone/src/db/schema.ts +37 -37
  235. package/.next/standalone/src/hooks/use-queries.ts +81 -0
  236. package/.next/standalone/src/hooks/use-websocket.ts +74 -0
  237. package/.next/standalone/src/lib/schema.test.ts +37 -37
  238. package/.next/standalone/src/lib/schema.ts +77 -31
  239. package/.next/standalone/src/lib/specs-dir.ts +8 -8
  240. package/.next/standalone/src/lib/specs.ts +98 -92
  241. package/.next/standalone/src/lib/utils.ts +2 -2
  242. package/.next/standalone/src/mcp/server.ts +556 -349
  243. package/.next/standalone/src/mcp/websocket.ts +26 -23
  244. package/.next/standalone/src/schemas/backend-api/templates/proposal.md +6 -4
  245. package/.next/standalone/src/schemas/backend-api/templates/tasks.md +6 -0
  246. package/.next/standalone/src/schemas/data-engineering/templates/proposal.md +6 -4
  247. package/.next/standalone/src/schemas/data-engineering/templates/tasks.md +6 -0
  248. package/.next/standalone/src/schemas/devops-platform/templates/proposal.md +6 -4
  249. package/.next/standalone/src/schemas/devops-platform/templates/tasks.md +6 -0
  250. package/.next/standalone/src/schemas/frontend-product/templates/proposal.md +6 -4
  251. package/.next/standalone/src/schemas/frontend-product/templates/tasks.md +6 -0
  252. package/.next/standalone/src/schemas/spec-driven/templates/proposal.md +6 -4
  253. package/.next/standalone/src/schemas/spec-driven/templates/tasks.md +26 -0
  254. package/.next/standalone/src/websocket/server.ts +20 -18
  255. package/.next/standalone/tsconfig.json +3 -12
  256. package/.next/standalone/tsconfig.tsbuildinfo +1 -1
  257. package/.next/static/chunks/4f23f64d46848f57.js +1 -0
  258. package/.next/static/chunks/53081dcdcad4f31e.js +1 -0
  259. package/.next/static/chunks/{6a3d582315f1c214.js → 701cfcbb41e0ab47.js} +1 -1
  260. package/.next/static/chunks/ac4b6b4d5b7e486e.css +1 -0
  261. package/.next/static/chunks/d0309ea872db5d9f.js +1 -0
  262. package/.next/static/chunks/d4cf8893a018e965.js +1 -0
  263. package/.next/static/chunks/e6c0233269a4bd69.js +13 -0
  264. package/.next/static/chunks/{7e65cfa9841f66a5.js → e7d83ce855afe416.js} +2 -2
  265. package/.next/static/chunks/ee7035560fc9e5e9.js +1 -0
  266. package/.next/static/chunks/{turbopack-f4d3806ab575051d.js → turbopack-8b3b66a22a5e0fb4.js} +2 -2
  267. package/README.md +166 -25
  268. package/bin/devflow.js +12 -5
  269. package/drizzle.config.ts +7 -7
  270. package/next.config.ts +11 -2
  271. package/package.json +16 -7
  272. package/scripts/init-db.js +3 -5
  273. package/scripts/init-db.test.js +6 -6
  274. package/scripts/init-schema.js +22 -22
  275. package/scripts/init-schema.test.js +45 -45
  276. package/scripts/install-environments.js +27 -8
  277. package/scripts/install-environments.test.js +19 -10
  278. package/scripts/install-skills.js +104 -31
  279. package/scripts/install-skills.test.js +6 -6
  280. package/src/app/api/agents/route.ts +2 -2
  281. package/src/app/api/projects/[id]/route.ts +10 -19
  282. package/src/app/api/projects/route.ts +4 -10
  283. package/src/app/api/specs/[name]/artifacts/[artifactType]/approve/route.ts +8 -8
  284. package/src/app/api/specs/[name]/artifacts/[artifactType]/route.ts +14 -14
  285. package/src/app/api/specs/[name]/promote/route.ts +17 -23
  286. package/src/app/api/specs/[name]/route.ts +34 -36
  287. package/src/app/api/specs/route.ts +10 -10
  288. package/src/app/api/tasks/[id]/route.ts +11 -33
  289. package/src/app/api/tasks/route.ts +3 -6
  290. package/src/app/globals.css +3 -3
  291. package/src/app/layout.tsx +9 -5
  292. package/src/app/page.tsx +1 -1
  293. package/src/app/specs/[name]/page.tsx +26 -68
  294. package/src/components/kanban-board.tsx +78 -157
  295. package/src/components/kanban-column.tsx +10 -12
  296. package/src/components/markdown-preview.tsx +36 -19
  297. package/src/components/providers.tsx +20 -0
  298. package/src/components/specs/artifact-dag-status.tsx +12 -14
  299. package/src/components/specs/artifact-editor.tsx +55 -26
  300. package/src/components/specs/spec-detail.tsx +48 -40
  301. package/src/components/specs/spec-kanban-column.tsx +49 -68
  302. package/src/components/specs/spec-modal.tsx +119 -83
  303. package/src/components/task-card.tsx +34 -38
  304. package/src/components/task-dialog.tsx +16 -31
  305. package/src/components/ui/badge.tsx +12 -18
  306. package/src/components/ui/button.tsx +23 -28
  307. package/src/components/ui/card.tsx +42 -62
  308. package/src/components/ui/dialog.tsx +17 -35
  309. package/src/db/index.ts +10 -10
  310. package/src/db/schema.ts +37 -37
  311. package/src/hooks/use-queries.ts +81 -0
  312. package/src/hooks/use-websocket.ts +74 -0
  313. package/src/lib/schema.test.ts +37 -37
  314. package/src/lib/schema.ts +77 -31
  315. package/src/lib/specs-dir.ts +8 -8
  316. package/src/lib/specs.ts +98 -92
  317. package/src/lib/utils.ts +2 -2
  318. package/src/mcp/server.ts +556 -349
  319. package/src/mcp/websocket.ts +26 -23
  320. package/src/schemas/backend-api/templates/proposal.md +6 -4
  321. package/src/schemas/backend-api/templates/tasks.md +6 -0
  322. package/src/schemas/data-engineering/templates/proposal.md +6 -4
  323. package/src/schemas/data-engineering/templates/tasks.md +6 -0
  324. package/src/schemas/devops-platform/templates/proposal.md +6 -4
  325. package/src/schemas/devops-platform/templates/tasks.md +6 -0
  326. package/src/schemas/frontend-product/templates/proposal.md +6 -4
  327. package/src/schemas/frontend-product/templates/tasks.md +6 -0
  328. package/src/schemas/spec-driven/templates/proposal.md +6 -4
  329. package/src/schemas/spec-driven/templates/tasks.md +26 -0
  330. package/src/types/bun-sqlite.d.ts +1 -1
  331. package/src/websocket/server.ts +20 -18
  332. package/tsconfig.json +3 -12
  333. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1be4e4b6._.js +0 -3
  334. package/.next/standalone/.next/server/chunks/[root-of-the-server]__492a4a24._.js +0 -3
  335. package/.next/standalone/.next/server/chunks/[root-of-the-server]__67c59ae1._.js +0 -3
  336. package/.next/standalone/.next/server/chunks/[root-of-the-server]__6cc7aecd._.js +0 -3
  337. package/.next/standalone/.next/server/chunks/[root-of-the-server]__83a77b07._.js +0 -3
  338. package/.next/standalone/.next/server/chunks/[root-of-the-server]__88d91cb5._.js +0 -3
  339. package/.next/standalone/.next/server/chunks/[root-of-the-server]__a9e095e8._.js +0 -3
  340. package/.next/standalone/.next/server/chunks/[root-of-the-server]__b367d327._.js +0 -3
  341. package/.next/standalone/.next/server/chunks/[root-of-the-server]__eb413bbc._.js +0 -3
  342. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0b8c072a._.js +0 -3
  343. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12b2191f._.js +0 -3
  344. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__15316462._.js +0 -3
  345. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__19c9c409._.js +0 -3
  346. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__19e5e22f._.js +0 -3
  347. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__25b5ccb3._.js +0 -3
  348. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__2f78c3da._.js +0 -3
  349. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__3160b3f4._.js +0 -9
  350. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__319559be._.js +0 -3
  351. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__34f7cbcb._.js +0 -31
  352. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__3abab94b._.js +0 -3
  353. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__3b3d8930._.js +0 -3
  354. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__4250799b._.js +0 -3
  355. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__4c29cbc3._.js +0 -26
  356. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__4e0bb6eb._.js +0 -9
  357. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__5678d2e3._.js +0 -3
  358. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__673445e0._.js +0 -31
  359. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__934de06b._.js +0 -3
  360. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__9b6c6f09._.js +0 -3
  361. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__a1b83a98._.js +0 -3
  362. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__b00a15c5._.js +0 -3
  363. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__b8567be3._.js +0 -26
  364. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__ce64536e._.js +0 -45
  365. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__e96d33d2._.js +0 -45
  366. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__fd7ec054._.js +0 -3
  367. package/.next/standalone/.next/server/chunks/ssr/node_modules_16a3f9e7._.js +0 -17
  368. package/.next/standalone/.next/server/chunks/ssr/node_modules_45bce0e1._.js +0 -3
  369. package/.next/standalone/.next/server/chunks/ssr/node_modules_5e5a372d._.js +0 -17
  370. package/.next/standalone/.next/server/chunks/ssr/node_modules_6ece6f1e._.js +0 -3
  371. package/.next/standalone/.next/server/chunks/ssr/node_modules_d173e749._.js +0 -3
  372. package/.next/standalone/.next/server/chunks/ssr/node_modules_d3-shape_src_arc_fb1ac087.js +0 -3
  373. package/.next/standalone/.next/server/chunks/ssr/node_modules_mermaid_dist_chunks_mermaid_core_1f73a830._.js +0 -3
  374. package/.next/standalone/.next/server/chunks/ssr/node_modules_mermaid_dist_chunks_mermaid_core_663ac803._.js +0 -3
  375. package/.next/standalone/.next/server/chunks/ssr/node_modules_mermaid_dist_chunks_mermaid_core_chunk-FMBD7UC4_mjs_4f485529._.js +0 -17
  376. package/.next/standalone/.next/server/chunks/ssr/node_modules_mermaid_dist_chunks_mermaid_core_chunk-TZMSLE5B_mjs_8436a62a._.js +0 -3
  377. package/.next/standalone/.next/server/chunks/ssr/node_modules_mermaid_dist_chunks_mermaid_core_f78d2dc4._.js +0 -3
  378. package/.next/standalone/.next/server/chunks/ssr/src_app_layout_tsx_cc8184fa._.js +0 -3
  379. package/.next/standalone/devflow/specs/add-default-schema-template-selection/.approvals.json +0 -26
  380. package/.next/standalone/devflow/specs/add-default-schema-template-selection/.meta.json +0 -8
  381. package/.next/standalone/devflow/specs/add-default-schema-template-selection/design.md +0 -126
  382. package/.next/standalone/devflow/specs/add-default-schema-template-selection/proposal.md +0 -65
  383. package/.next/standalone/devflow/specs/add-default-schema-template-selection/specs.md +0 -107
  384. package/.next/standalone/devflow/specs/add-default-schema-template-selection/tasks.md +0 -235
  385. package/.next/standalone/devflow/specs/test-no-project/.approvals.json +0 -17
  386. package/.next/standalone/devflow/specs/test-no-project/.meta.json +0 -6
  387. package/.next/standalone/devflow/specs/ui-cleanup/.approvals.json +0 -29
  388. package/.next/standalone/devflow/specs/ui-cleanup/.meta.json +0 -6
  389. package/.next/standalone/devflow/specs/ui-cleanup/design.md +0 -49
  390. package/.next/standalone/devflow/specs/ui-cleanup/proposal.md +0 -34
  391. package/.next/standalone/devflow/specs/ui-cleanup/specs.md +0 -55
  392. package/.next/standalone/devflow/specs/ui-cleanup/tasks.md +0 -28
  393. package/.next/standalone/node_modules/@img/colour/color.cjs +0 -1594
  394. package/.next/standalone/node_modules/@img/colour/index.cjs +0 -1
  395. package/.next/standalone/node_modules/@img/colour/package.json +0 -45
  396. package/.next/standalone/node_modules/@img/sharp-darwin-arm64/lib/sharp-darwin-arm64.node +0 -0
  397. package/.next/standalone/node_modules/@img/sharp-darwin-arm64/package.json +0 -40
  398. package/.next/standalone/node_modules/@img/sharp-libvips-darwin-arm64/README.md +0 -46
  399. package/.next/standalone/node_modules/@img/sharp-libvips-darwin-arm64/lib/glib-2.0/include/glibconfig.h +0 -220
  400. package/.next/standalone/node_modules/@img/sharp-libvips-darwin-arm64/lib/index.js +0 -1
  401. package/.next/standalone/node_modules/@img/sharp-libvips-darwin-arm64/lib/libvips-cpp.8.17.3.dylib +0 -0
  402. package/.next/standalone/node_modules/@img/sharp-libvips-darwin-arm64/package.json +0 -36
  403. package/.next/standalone/node_modules/@img/sharp-libvips-darwin-arm64/versions.json +0 -30
  404. package/.next/standalone/node_modules/buffer-from/index.js +0 -72
  405. package/.next/standalone/node_modules/buffer-from/package.json +0 -19
  406. package/.next/standalone/node_modules/sharp/lib/channel.js +0 -177
  407. package/.next/standalone/node_modules/sharp/lib/colour.js +0 -195
  408. package/.next/standalone/node_modules/sharp/lib/composite.js +0 -212
  409. package/.next/standalone/node_modules/sharp/lib/constructor.js +0 -499
  410. package/.next/standalone/node_modules/sharp/lib/index.js +0 -16
  411. package/.next/standalone/node_modules/sharp/lib/input.js +0 -809
  412. package/.next/standalone/node_modules/sharp/lib/is.js +0 -143
  413. package/.next/standalone/node_modules/sharp/lib/libvips.js +0 -207
  414. package/.next/standalone/node_modules/sharp/lib/operation.js +0 -1016
  415. package/.next/standalone/node_modules/sharp/lib/output.js +0 -1666
  416. package/.next/standalone/node_modules/sharp/lib/resize.js +0 -595
  417. package/.next/standalone/node_modules/sharp/lib/sharp.js +0 -121
  418. package/.next/standalone/node_modules/sharp/lib/utility.js +0 -291
  419. package/.next/standalone/node_modules/sharp/node_modules/detect-libc/lib/detect-libc.js +0 -313
  420. package/.next/standalone/node_modules/sharp/node_modules/detect-libc/lib/elf.js +0 -39
  421. package/.next/standalone/node_modules/sharp/node_modules/detect-libc/lib/filesystem.js +0 -51
  422. package/.next/standalone/node_modules/sharp/node_modules/detect-libc/lib/process.js +0 -24
  423. package/.next/standalone/node_modules/sharp/node_modules/semver/classes/comparator.js +0 -143
  424. package/.next/standalone/node_modules/sharp/node_modules/semver/classes/range.js +0 -557
  425. package/.next/standalone/node_modules/sharp/node_modules/semver/classes/semver.js +0 -333
  426. package/.next/standalone/node_modules/sharp/node_modules/semver/functions/cmp.js +0 -54
  427. package/.next/standalone/node_modules/sharp/node_modules/semver/functions/coerce.js +0 -62
  428. package/.next/standalone/node_modules/sharp/node_modules/semver/functions/compare.js +0 -7
  429. package/.next/standalone/node_modules/sharp/node_modules/semver/functions/eq.js +0 -5
  430. package/.next/standalone/node_modules/sharp/node_modules/semver/functions/gt.js +0 -5
  431. package/.next/standalone/node_modules/sharp/node_modules/semver/functions/gte.js +0 -5
  432. package/.next/standalone/node_modules/sharp/node_modules/semver/functions/lt.js +0 -5
  433. package/.next/standalone/node_modules/sharp/node_modules/semver/functions/lte.js +0 -5
  434. package/.next/standalone/node_modules/sharp/node_modules/semver/functions/neq.js +0 -5
  435. package/.next/standalone/node_modules/sharp/node_modules/semver/functions/parse.js +0 -18
  436. package/.next/standalone/node_modules/sharp/node_modules/semver/functions/satisfies.js +0 -12
  437. package/.next/standalone/node_modules/sharp/node_modules/semver/internal/constants.js +0 -37
  438. package/.next/standalone/node_modules/sharp/node_modules/semver/internal/debug.js +0 -11
  439. package/.next/standalone/node_modules/sharp/node_modules/semver/internal/identifiers.js +0 -29
  440. package/.next/standalone/node_modules/sharp/node_modules/semver/internal/lrucache.js +0 -42
  441. package/.next/standalone/node_modules/sharp/node_modules/semver/internal/parse-options.js +0 -17
  442. package/.next/standalone/node_modules/sharp/node_modules/semver/internal/re.js +0 -223
  443. package/.next/standalone/node_modules/sharp/node_modules/semver/package.json +0 -78
  444. package/.next/standalone/node_modules/sharp/package.json +0 -202
  445. package/.next/standalone/node_modules/source-map/lib/array-set.js +0 -121
  446. package/.next/standalone/node_modules/source-map/lib/base64-vlq.js +0 -140
  447. package/.next/standalone/node_modules/source-map/lib/base64.js +0 -67
  448. package/.next/standalone/node_modules/source-map/lib/binary-search.js +0 -111
  449. package/.next/standalone/node_modules/source-map/lib/mapping-list.js +0 -79
  450. package/.next/standalone/node_modules/source-map/lib/quick-sort.js +0 -114
  451. package/.next/standalone/node_modules/source-map/lib/source-map-consumer.js +0 -1145
  452. package/.next/standalone/node_modules/source-map/lib/source-map-generator.js +0 -425
  453. package/.next/standalone/node_modules/source-map/lib/source-node.js +0 -413
  454. package/.next/standalone/node_modules/source-map/lib/util.js +0 -488
  455. package/.next/standalone/node_modules/source-map/package.json +0 -73
  456. package/.next/standalone/node_modules/source-map/source-map.js +0 -8
  457. package/.next/standalone/node_modules/source-map-support/LICENSE.md +0 -21
  458. package/.next/standalone/node_modules/source-map-support/README.md +0 -284
  459. package/.next/standalone/node_modules/source-map-support/browser-source-map-support.js +0 -114
  460. package/.next/standalone/node_modules/source-map-support/package.json +0 -31
  461. package/.next/standalone/node_modules/source-map-support/register-hook-require.js +0 -1
  462. package/.next/standalone/node_modules/source-map-support/register.js +0 -1
  463. package/.next/standalone/node_modules/source-map-support/source-map-support.js +0 -625
  464. package/.next/standalone/node_modules/typescript/lib/_tsc.js +0 -133818
  465. package/.next/standalone/node_modules/typescript/lib/_tsserver.js +0 -659
  466. package/.next/standalone/node_modules/typescript/lib/_typingsInstaller.js +0 -222
  467. package/.next/standalone/node_modules/typescript/lib/cs/diagnosticMessages.generated.json +0 -2122
  468. package/.next/standalone/node_modules/typescript/lib/de/diagnosticMessages.generated.json +0 -2122
  469. package/.next/standalone/node_modules/typescript/lib/es/diagnosticMessages.generated.json +0 -2122
  470. package/.next/standalone/node_modules/typescript/lib/fr/diagnosticMessages.generated.json +0 -2122
  471. package/.next/standalone/node_modules/typescript/lib/it/diagnosticMessages.generated.json +0 -2122
  472. package/.next/standalone/node_modules/typescript/lib/ja/diagnosticMessages.generated.json +0 -2122
  473. package/.next/standalone/node_modules/typescript/lib/ko/diagnosticMessages.generated.json +0 -2122
  474. package/.next/standalone/node_modules/typescript/lib/pl/diagnosticMessages.generated.json +0 -2122
  475. package/.next/standalone/node_modules/typescript/lib/pt-br/diagnosticMessages.generated.json +0 -2122
  476. package/.next/standalone/node_modules/typescript/lib/ru/diagnosticMessages.generated.json +0 -2122
  477. package/.next/standalone/node_modules/typescript/lib/tr/diagnosticMessages.generated.json +0 -2122
  478. package/.next/standalone/node_modules/typescript/lib/tsc.js +0 -8
  479. package/.next/standalone/node_modules/typescript/lib/tsserver.js +0 -8
  480. package/.next/standalone/node_modules/typescript/lib/tsserverlibrary.js +0 -21
  481. package/.next/standalone/node_modules/typescript/lib/typesMap.json +0 -497
  482. package/.next/standalone/node_modules/typescript/lib/typescript.js +0 -200276
  483. package/.next/standalone/node_modules/typescript/lib/typingsInstaller.js +0 -8
  484. package/.next/standalone/node_modules/typescript/lib/watchGuard.js +0 -53
  485. package/.next/standalone/node_modules/typescript/lib/zh-cn/diagnosticMessages.generated.json +0 -2122
  486. package/.next/standalone/node_modules/typescript/lib/zh-tw/diagnosticMessages.generated.json +0 -2122
  487. package/.next/standalone/node_modules/typescript/package.json +0 -120
  488. package/.next/standalone/src/components/ui/collapsible.tsx +0 -12
  489. package/.next/standalone/src/components/ui/sheet.tsx +0 -130
  490. package/.next/standalone/src/components/ui/tabs.tsx +0 -58
  491. package/.next/static/chunks/0177331b8adf7346.js +0 -7
  492. package/.next/static/chunks/06af74e2f3e207c5.js +0 -7
  493. package/.next/static/chunks/1748d5cd2443723b.css +0 -1
  494. package/.next/static/chunks/7ae917cb100be2b9.js +0 -7
  495. package/.next/static/chunks/7c4b901b394c3a8c.js +0 -7
  496. package/src/components/ui/collapsible.tsx +0 -12
  497. package/src/components/ui/sheet.tsx +0 -130
  498. package/src/components/ui/tabs.tsx +0 -58
  499. /package/.next/standalone/node_modules/{detect-libc → libsql/node_modules/detect-libc}/lib/detect-libc.js +0 -0
  500. /package/.next/standalone/node_modules/{detect-libc → libsql/node_modules/detect-libc}/lib/filesystem.js +0 -0
  501. /package/.next/standalone/node_modules/{detect-libc → libsql/node_modules/detect-libc}/lib/process.js +0 -0
  502. /package/.next/static/{5YFrdIQd1UU2IxLxekrU9 → Ql6qo0GYLL23WRu6ec4N5}/_buildManifest.js +0 -0
  503. /package/.next/static/{5YFrdIQd1UU2IxLxekrU9 → Ql6qo0GYLL23WRu6ec4N5}/_clientMiddlewareManifest.json +0 -0
  504. /package/.next/static/{5YFrdIQd1UU2IxLxekrU9 → Ql6qo0GYLL23WRu6ec4N5}/_ssgManifest.js +0 -0
@@ -1,26 +1,25 @@
1
1
  #!/usr/bin/env node
2
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
- import {
5
- CallToolRequestSchema,
6
- ListToolsRequestSchema,
7
- } from "@modelcontextprotocol/sdk/types.js";
8
- import { getDb, schema } from "../db/index.js";
9
- import { eq, desc, and } from "drizzle-orm";
10
- import { randomUUID } from "crypto";
11
- import { broadcastUpdate } from "./websocket.js";
12
- import path from "path";
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
5
+ import { getDb, schema } from '../db/index.js';
6
+ import { eq, desc, and } from 'drizzle-orm';
7
+ import { randomUUID } from 'crypto';
8
+ import { broadcastUpdate } from './websocket.js';
9
+ import path from 'path';
13
10
 
14
11
  function slugify(str: string): string {
15
12
  return str
16
13
  .toLowerCase()
17
- .replace(/[^a-z0-9]+/g, "-")
18
- .replace(/^-+|-+$/g, "");
14
+ .replace(/[^a-z0-9]+/g, '-')
15
+ .replace(/^-+|-+$/g, '');
19
16
  }
20
17
 
21
18
  function defaultProjectName(): string {
22
19
  return slugify(path.basename(process.cwd()));
23
20
  }
21
+ import { listSchemas, createSchema } from '../lib/schema.js';
22
+ import { getSpecsDir } from '../lib/specs-dir.js';
24
23
  import {
25
24
  listSpecs,
26
25
  getSpec,
@@ -36,18 +35,18 @@ import {
36
35
  getArtifactTemplate,
37
36
  fillTaskSummary,
38
37
  updateTaskBodyInSpec,
39
- } from "../lib/specs.js";
38
+ } from '../lib/specs.js';
40
39
 
41
40
  const server = new Server(
42
41
  {
43
- name: "devflow-mcp",
44
- version: "2.0.0",
42
+ name: 'devflow-mcp',
43
+ version: '2.0.0',
45
44
  },
46
45
  {
47
46
  capabilities: {
48
47
  tools: {},
49
48
  },
50
- }
49
+ },
51
50
  );
52
51
 
53
52
  // Tool definitions
@@ -56,411 +55,511 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
56
55
  tools: [
57
56
  // Project Management
58
57
  {
59
- name: "list_projects",
60
- description: "List all projects",
58
+ name: 'list_projects',
59
+ description: 'List all projects',
61
60
  inputSchema: {
62
- type: "object",
61
+ type: 'object',
63
62
  properties: {
64
63
  status: {
65
- type: "string",
66
- enum: ["active", "archived", "completed"],
67
- description: "Filter by project status (optional)",
64
+ type: 'string',
65
+ enum: ['active', 'archived', 'completed'],
66
+ description: 'Filter by project status (optional)',
68
67
  },
69
68
  },
70
69
  },
71
70
  },
72
71
  {
73
- name: "create_project",
74
- description: "Create a new project. If name is omitted, defaults to the current directory name (slugified).",
72
+ name: 'create_project',
73
+ description:
74
+ 'Create a new project. If name is omitted, defaults to the current directory name (slugified).',
75
75
  inputSchema: {
76
- type: "object",
76
+ type: 'object',
77
77
  properties: {
78
- name: { type: "string", description: "Project name (optional — defaults to current directory name)" },
79
- description: { type: "string", description: "Project description" },
78
+ name: {
79
+ type: 'string',
80
+ description: 'Project name (optional — defaults to current directory name)',
81
+ },
82
+ description: { type: 'string', description: 'Project description' },
80
83
  },
81
84
  },
82
85
  },
83
86
  {
84
- name: "get_project",
85
- description: "Get project details with all tasks",
87
+ name: 'get_project',
88
+ description: 'Get project details with all tasks',
86
89
  inputSchema: {
87
- type: "object",
90
+ type: 'object',
88
91
  properties: {
89
- projectId: { type: "string", description: "Project ID" },
92
+ projectId: { type: 'string', description: 'Project ID' },
90
93
  },
91
- required: ["projectId"],
94
+ required: ['projectId'],
92
95
  },
93
96
  },
94
97
  {
95
- name: "update_project",
96
- description: "Update project details",
98
+ name: 'update_project',
99
+ description: 'Update project details',
97
100
  inputSchema: {
98
- type: "object",
101
+ type: 'object',
99
102
  properties: {
100
- projectId: { type: "string", description: "Project ID" },
101
- name: { type: "string", description: "New project name" },
102
- description: { type: "string", description: "New description" },
103
+ projectId: { type: 'string', description: 'Project ID' },
104
+ name: { type: 'string', description: 'New project name' },
105
+ description: { type: 'string', description: 'New description' },
103
106
  status: {
104
- type: "string",
105
- enum: ["active", "archived", "completed"],
106
- description: "New status",
107
+ type: 'string',
108
+ enum: ['active', 'archived', 'completed'],
109
+ description: 'New status',
107
110
  },
108
111
  },
109
- required: ["projectId"],
112
+ required: ['projectId'],
110
113
  },
111
114
  },
112
115
  {
113
- name: "get_or_create_project",
114
- description: "Get an existing project by name or create it if it doesn't exist. If name is omitted, defaults to the current directory name (slugified). Use the project directory name as a default if no project name is specified.",
116
+ name: 'get_or_create_project',
117
+ description:
118
+ "Get an existing project by name or create it if it doesn't exist. If name is omitted, defaults to the current directory name (slugified). Use the project directory name as a default if no project name is specified.",
115
119
  inputSchema: {
116
- type: "object",
120
+ type: 'object',
117
121
  properties: {
118
- name: { type: "string", description: "Project name (optional — defaults to current directory name)" },
119
- description: { type: "string", description: "Description (used only if creating)" },
122
+ name: {
123
+ type: 'string',
124
+ description: 'Project name (optional — defaults to current directory name)',
125
+ },
126
+ description: { type: 'string', description: 'Description (used only if creating)' },
120
127
  },
121
128
  },
122
129
  },
123
130
  // Task Management
124
131
  {
125
- name: "list_tasks",
126
- description: "List tasks with optional filters",
132
+ name: 'list_tasks',
133
+ description: 'List tasks with optional filters',
127
134
  inputSchema: {
128
- type: "object",
135
+ type: 'object',
129
136
  properties: {
130
- projectId: { type: "string", description: "Filter by project ID" },
131
- specName: { type: "string", description: "Filter by spec name" },
137
+ projectId: { type: 'string', description: 'Filter by project ID' },
138
+ specName: { type: 'string', description: 'Filter by spec name' },
132
139
  status: {
133
- type: "string",
134
- enum: ["backlog", "todo", "in_progress", "interrupted", "done"],
135
- description: "Filter by status",
140
+ type: 'string',
141
+ enum: ['backlog', 'todo', 'in_progress', 'interrupted', 'done'],
142
+ description: 'Filter by status',
136
143
  },
137
- assignedAgent: { type: "string", description: "Filter by assigned agent" },
144
+ assignedAgent: { type: 'string', description: 'Filter by assigned agent' },
138
145
  },
139
146
  },
140
147
  },
141
148
  {
142
- name: "get_task",
143
- description: "Get details of a specific task",
149
+ name: 'get_task',
150
+ description: 'Get details of a specific task',
144
151
  inputSchema: {
145
- type: "object",
152
+ type: 'object',
146
153
  properties: {
147
- taskId: { type: "string", description: "Task ID" },
154
+ taskId: { type: 'string', description: 'Task ID' },
148
155
  },
149
- required: ["taskId"],
156
+ required: ['taskId'],
150
157
  },
151
158
  },
152
159
  {
153
- name: "create_task",
154
- description: "Create a new task. Can auto-create project if projectName is provided instead of projectId.",
160
+ name: 'create_task',
161
+ description:
162
+ 'Create a new task. Can auto-create project if projectName is provided instead of projectId.',
155
163
  inputSchema: {
156
- type: "object",
164
+ type: 'object',
157
165
  properties: {
158
- title: { type: "string", description: "Task title" },
159
- body: { type: "string", description: "Task body (markdown)" },
160
- description: { type: "string", description: "Task description (legacy — auto-composed into body if body not provided)" },
161
- context: { type: "string", description: "Additional context (legacy — auto-composed into body if body not provided)" },
166
+ title: { type: 'string', description: 'Task title' },
167
+ body: { type: 'string', description: 'Task body (markdown)' },
168
+ description: {
169
+ type: 'string',
170
+ description:
171
+ 'Task description (legacy — auto-composed into body if body not provided)',
172
+ },
173
+ context: {
174
+ type: 'string',
175
+ description:
176
+ 'Additional context (legacy — auto-composed into body if body not provided)',
177
+ },
162
178
  priority: {
163
- type: "string",
164
- enum: ["low", "medium", "high", "urgent"],
165
- description: "Task priority",
179
+ type: 'string',
180
+ enum: ['low', 'medium', 'high', 'urgent'],
181
+ description: 'Task priority',
166
182
  },
167
183
  status: {
168
- type: "string",
169
- enum: ["backlog", "todo", "in_progress", "done"],
170
- description: "Initial status (defaults to backlog)",
184
+ type: 'string',
185
+ enum: ['backlog', 'todo', 'in_progress', 'done'],
186
+ description: 'Initial status (defaults to backlog)',
171
187
  },
172
- projectId: { type: "string", description: "Project ID" },
188
+ projectId: { type: 'string', description: 'Project ID' },
173
189
  projectName: {
174
- type: "string",
190
+ type: 'string',
175
191
  description: "Project name — auto-creates the project if it doesn't exist",
176
192
  },
177
- specName: { type: "string", description: "Spec name to link this task to" },
193
+ specName: { type: 'string', description: 'Spec name to link this task to' },
178
194
  },
179
- required: ["title"],
195
+ required: ['title'],
180
196
  },
181
197
  },
182
198
  {
183
- name: "create_tasks_bulk",
184
- description: "Create multiple tasks at once",
199
+ name: 'create_tasks_bulk',
200
+ description: 'Create multiple tasks at once',
185
201
  inputSchema: {
186
- type: "object",
202
+ type: 'object',
187
203
  properties: {
188
- projectId: { type: "string", description: "Project ID for all tasks" },
189
- projectName: { type: "string", description: "Project name (auto-creates if needed)" },
190
- specName: { type: "string", description: "Spec name to link tasks to" },
204
+ projectId: { type: 'string', description: 'Project ID for all tasks' },
205
+ projectName: { type: 'string', description: 'Project name (auto-creates if needed)' },
206
+ specName: { type: 'string', description: 'Spec name to link tasks to' },
191
207
  tasks: {
192
- type: "array",
193
- description: "Array of task objects",
208
+ type: 'array',
209
+ description: 'Array of task objects',
194
210
  items: {
195
- type: "object",
211
+ type: 'object',
196
212
  properties: {
197
- title: { type: "string" },
198
- body: { type: "string", description: "Task body (markdown)" },
199
- description: { type: "string", description: "Legacy — auto-composed into body if body not provided" },
200
- context: { type: "string", description: "Legacy — auto-composed into body if body not provided" },
201
- priority: { type: "string", enum: ["low", "medium", "high", "urgent"] },
202
- status: { type: "string", enum: ["backlog", "todo", "in_progress", "done"] },
213
+ title: { type: 'string' },
214
+ body: { type: 'string', description: 'Task body (markdown)' },
215
+ description: {
216
+ type: 'string',
217
+ description: 'Legacy auto-composed into body if body not provided',
218
+ },
219
+ context: {
220
+ type: 'string',
221
+ description: 'Legacy — auto-composed into body if body not provided',
222
+ },
223
+ priority: { type: 'string', enum: ['low', 'medium', 'high', 'urgent'] },
224
+ status: { type: 'string', enum: ['backlog', 'todo', 'in_progress', 'done'] },
203
225
  },
204
- required: ["title"],
226
+ required: ['title'],
205
227
  },
206
228
  },
207
229
  },
208
- required: ["tasks"],
230
+ required: ['tasks'],
209
231
  },
210
232
  },
211
233
  {
212
- name: "update_task",
234
+ name: 'update_task',
213
235
  description: "Update a task's details or status",
214
236
  inputSchema: {
215
- type: "object",
237
+ type: 'object',
216
238
  properties: {
217
- taskId: { type: "string", description: "Task ID" },
218
- title: { type: "string", description: "New title" },
219
- body: { type: "string", description: "New task body (markdown)" },
239
+ taskId: { type: 'string', description: 'Task ID' },
240
+ title: { type: 'string', description: 'New title' },
241
+ body: { type: 'string', description: 'New task body (markdown)' },
220
242
  status: {
221
- type: "string",
222
- enum: ["backlog", "todo", "in_progress", "interrupted", "done"],
223
- description: "New status",
243
+ type: 'string',
244
+ enum: ['backlog', 'todo', 'in_progress', 'interrupted', 'done'],
245
+ description: 'New status',
224
246
  },
225
247
  priority: {
226
- type: "string",
227
- enum: ["low", "medium", "high", "urgent"],
228
- description: "New priority",
248
+ type: 'string',
249
+ enum: ['low', 'medium', 'high', 'urgent'],
250
+ description: 'New priority',
229
251
  },
230
- specName: { type: "string", description: "Link or re-link to a spec" },
252
+ specName: { type: 'string', description: 'Link or re-link to a spec' },
231
253
  },
232
- required: ["taskId"],
254
+ required: ['taskId'],
233
255
  },
234
256
  },
235
257
  // Agent Workflow
236
258
  {
237
- name: "check_in",
238
- description: "Agent checks in to a task (sets status to in_progress, assigns agent)",
259
+ name: 'check_in',
260
+ description: 'Agent checks in to a task (sets status to in_progress, assigns agent)',
239
261
  inputSchema: {
240
- type: "object",
262
+ type: 'object',
241
263
  properties: {
242
- taskId: { type: "string", description: "Task ID to check in to" },
243
- agentName: { type: "string", description: "Name/identifier of the agent" },
244
- executionPlan: { type: "string", description: "Agent's plan for completing the task" },
264
+ taskId: { type: 'string', description: 'Task ID to check in to' },
265
+ agentName: { type: 'string', description: 'Name/identifier of the agent' },
266
+ executionPlan: { type: 'string', description: "Agent's plan for completing the task" },
245
267
  },
246
- required: ["taskId", "agentName"],
268
+ required: ['taskId', 'agentName'],
247
269
  },
248
270
  },
249
271
  {
250
- name: "check_out",
251
- description: "Agent checks out of a task (sets status to done)",
272
+ name: 'check_out',
273
+ description: 'Agent checks out of a task (sets status to done)',
252
274
  inputSchema: {
253
- type: "object",
275
+ type: 'object',
254
276
  properties: {
255
- taskId: { type: "string", description: "Task ID to check out of" },
256
- agentName: { type: "string", description: "Name/identifier of the agent" },
277
+ taskId: { type: 'string', description: 'Task ID to check out of' },
278
+ agentName: { type: 'string', description: 'Name/identifier of the agent' },
257
279
  taskSummary: {
258
- type: "object",
259
- description: "Structured summary of what was accomplished",
280
+ type: 'object',
281
+ description: 'Structured summary of what was accomplished',
260
282
  properties: {
261
- whatWasDone: { type: "string", description: "What was accomplished" },
262
- filesChanged: { type: "string", description: "Files changed (optional)" },
263
- issuesEncountered: { type: "string", description: "Issues encountered (optional)" },
264
- followUps: { type: "string", description: "Follow-up items (optional)" },
283
+ whatWasDone: { type: 'string', description: 'What was accomplished' },
284
+ filesChanged: { type: 'string', description: 'Files changed (optional)' },
285
+ issuesEncountered: { type: 'string', description: 'Issues encountered (optional)' },
286
+ followUps: { type: 'string', description: 'Follow-up items (optional)' },
265
287
  },
266
- required: ["whatWasDone"],
288
+ required: ['whatWasDone'],
267
289
  },
268
290
  },
269
- required: ["taskId", "agentName"],
291
+ required: ['taskId', 'agentName'],
270
292
  },
271
293
  },
272
294
  {
273
- name: "log_activity",
274
- description: "Log an activity or comment on a task",
295
+ name: 'log_activity',
296
+ description: 'Log an activity or comment on a task',
275
297
  inputSchema: {
276
- type: "object",
298
+ type: 'object',
277
299
  properties: {
278
- taskId: { type: "string", description: "Task ID" },
279
- agentName: { type: "string", description: "Name/identifier of the agent" },
300
+ taskId: { type: 'string', description: 'Task ID' },
301
+ agentName: { type: 'string', description: 'Name/identifier of the agent' },
280
302
  action: {
281
- type: "string",
282
- enum: ["check_in", "check_out", "update", "comment"],
283
- description: "Type of action",
303
+ type: 'string',
304
+ enum: ['check_in', 'check_out', 'update', 'comment'],
305
+ description: 'Type of action',
284
306
  },
285
- details: { type: "string", description: "Activity details or comment" },
307
+ details: { type: 'string', description: 'Activity details or comment' },
286
308
  },
287
- required: ["taskId", "agentName", "action"],
309
+ required: ['taskId', 'agentName', 'action'],
288
310
  },
289
311
  },
290
312
  {
291
- name: "get_activity_log",
292
- description: "Get activity log for a task",
313
+ name: 'get_activity_log',
314
+ description: 'Get activity log for a task',
293
315
  inputSchema: {
294
- type: "object",
316
+ type: 'object',
295
317
  properties: {
296
- taskId: { type: "string", description: "Task ID" },
297
- limit: { type: "number", description: "Max number of entries (default 50)" },
318
+ taskId: { type: 'string', description: 'Task ID' },
319
+ limit: { type: 'number', description: 'Max number of entries (default 50)' },
298
320
  },
299
- required: ["taskId"],
321
+ required: ['taskId'],
300
322
  },
301
323
  },
302
324
  // Spec Management
303
325
  {
304
- name: "list_specs",
305
- description: "List all specs in the devflow/specs directory",
326
+ name: 'list_specs',
327
+ description: 'List all specs in the devflow/specs directory',
306
328
  inputSchema: {
307
- type: "object",
329
+ type: 'object',
308
330
  properties: {
309
- projectId: { type: "string", description: "Filter by project ID (optional)" },
331
+ projectId: { type: 'string', description: 'Filter by project ID (optional)' },
310
332
  },
311
333
  },
312
334
  },
313
335
  {
314
- name: "create_spec",
315
- description: "Create a new spec folder with meta and approvals files. Run list_projects or get_or_create_project first to get a projectId.",
336
+ name: 'create_spec',
337
+ description:
338
+ 'Create a new spec folder with meta and approvals files. Run list_projects or get_or_create_project first to get a projectId.',
316
339
  inputSchema: {
317
- type: "object",
340
+ type: 'object',
318
341
  properties: {
319
342
  name: {
320
- type: "string",
321
- description: "Spec folder name (slug, e.g. add-oauth)",
343
+ type: 'string',
344
+ description: 'Spec folder name (slug, e.g. add-oauth)',
322
345
  },
323
- title: { type: "string", description: "Human-readable title" },
324
- projectId: { type: "string", description: "Project ID to link to — run list_projects or get_or_create_project first" },
325
- description: { type: "string", description: "Optional description" },
346
+ title: { type: 'string', description: 'Human-readable title' },
347
+ projectId: {
348
+ type: 'string',
349
+ description:
350
+ 'Project ID to link to — run list_projects or get_or_create_project first',
351
+ },
352
+ description: { type: 'string', description: 'Optional description' },
326
353
  schema: {
327
- type: "string",
328
- description: "Schema name (defaults to spec-driven)",
354
+ type: 'string',
355
+ description: 'Schema name (defaults to spec-driven)',
329
356
  },
330
357
  },
331
- required: ["name", "title", "projectId"],
358
+ required: ['name', 'title', 'projectId'],
332
359
  },
333
360
  },
334
361
  {
335
- name: "get_spec",
336
- description: "Get full spec details including artifact contents and statuses",
362
+ name: 'get_spec',
363
+ description: 'Get full spec details including artifact contents and statuses',
337
364
  inputSchema: {
338
- type: "object",
365
+ type: 'object',
339
366
  properties: {
340
- specName: { type: "string", description: "Spec folder name" },
367
+ specName: { type: 'string', description: 'Spec folder name' },
341
368
  },
342
- required: ["specName"],
369
+ required: ['specName'],
343
370
  },
344
371
  },
345
372
  {
346
- name: "get_spec_status",
347
- description: "Get the DAG status of all artifacts in a spec",
373
+ name: 'get_spec_status',
374
+ description: 'Get the DAG status of all artifacts in a spec',
348
375
  inputSchema: {
349
- type: "object",
376
+ type: 'object',
350
377
  properties: {
351
- specName: { type: "string", description: "Spec folder name" },
378
+ specName: { type: 'string', description: 'Spec folder name' },
352
379
  },
353
- required: ["specName"],
380
+ required: ['specName'],
354
381
  },
355
382
  },
356
383
  {
357
- name: "get_artifact",
358
- description: "Get the content of a spec artifact",
384
+ name: 'get_artifact',
385
+ description: 'Get the content of a spec artifact',
359
386
  inputSchema: {
360
- type: "object",
387
+ type: 'object',
361
388
  properties: {
362
- specName: { type: "string", description: "Spec folder name" },
389
+ specName: { type: 'string', description: 'Spec folder name' },
363
390
  artifactType: {
364
- type: "string",
365
- description: "Artifact type (e.g. proposal, specs, design, tasks)",
391
+ type: 'string',
392
+ description: 'Artifact type (e.g. proposal, specs, design, tasks)',
366
393
  },
367
394
  },
368
- required: ["specName", "artifactType"],
395
+ required: ['specName', 'artifactType'],
369
396
  },
370
397
  },
371
398
  {
372
- name: "get_artifact_template",
373
- description: "Get the template for a spec artifact to use as a starting point",
399
+ name: 'get_artifact_template',
400
+ description: 'Get the template for a spec artifact to use as a starting point',
374
401
  inputSchema: {
375
- type: "object",
402
+ type: 'object',
376
403
  properties: {
377
- specName: { type: "string", description: "Spec folder name" },
404
+ specName: { type: 'string', description: 'Spec folder name' },
378
405
  artifactType: {
379
- type: "string",
380
- description: "Artifact type (e.g. proposal, specs, design, tasks)",
406
+ type: 'string',
407
+ description: 'Artifact type (e.g. proposal, specs, design, tasks)',
381
408
  },
382
409
  },
383
- required: ["specName", "artifactType"],
410
+ required: ['specName', 'artifactType'],
384
411
  },
385
412
  },
386
413
  {
387
- name: "write_artifact",
388
- description: "Write content to a spec artifact file. Blocked if required predecessors not approved.",
414
+ name: 'write_artifact',
415
+ description:
416
+ 'Write content to a spec artifact file. Blocked if required predecessors not approved.',
389
417
  inputSchema: {
390
- type: "object",
418
+ type: 'object',
391
419
  properties: {
392
- specName: { type: "string", description: "Spec folder name" },
420
+ specName: { type: 'string', description: 'Spec folder name' },
393
421
  artifactType: {
394
- type: "string",
395
- description: "Artifact type (e.g. proposal, specs, design, tasks)",
422
+ type: 'string',
423
+ description: 'Artifact type (e.g. proposal, specs, design, tasks)',
396
424
  },
397
- content: { type: "string", description: "Markdown content to write" },
425
+ content: { type: 'string', description: 'Markdown content to write' },
398
426
  },
399
- required: ["specName", "artifactType", "content"],
427
+ required: ['specName', 'artifactType', 'content'],
400
428
  },
401
429
  },
402
430
  {
403
- name: "approve_artifact",
404
- description: "Approve a spec artifact, allowing downstream artifacts to be written",
431
+ name: 'approve_artifact',
432
+ description: 'Approve a spec artifact, allowing downstream artifacts to be written',
405
433
  inputSchema: {
406
- type: "object",
434
+ type: 'object',
407
435
  properties: {
408
- specName: { type: "string", description: "Spec folder name" },
436
+ specName: { type: 'string', description: 'Spec folder name' },
409
437
  artifactType: {
410
- type: "string",
411
- description: "Artifact type to approve",
438
+ type: 'string',
439
+ description: 'Artifact type to approve',
412
440
  },
413
441
  approvedBy: {
414
- type: "string",
442
+ type: 'string',
415
443
  description: "Who is approving (agent name or 'human')",
416
444
  },
417
445
  },
418
- required: ["specName", "artifactType"],
446
+ required: ['specName', 'artifactType'],
447
+ },
448
+ },
449
+ {
450
+ name: 'draft_artifact',
451
+ description: 'Reset an artifact approval back to draft state',
452
+ inputSchema: {
453
+ type: 'object',
454
+ properties: {
455
+ specName: { type: 'string', description: 'Spec folder name' },
456
+ artifactType: { type: 'string', description: 'Artifact type to reset' },
457
+ },
458
+ required: ['specName', 'artifactType'],
419
459
  },
420
460
  },
421
461
  {
422
- name: "draft_artifact",
423
- description: "Reset an artifact approval back to draft state",
462
+ name: 'validate_spec',
463
+ description: 'Validate a spec for completeness and quality',
424
464
  inputSchema: {
425
- type: "object",
465
+ type: 'object',
426
466
  properties: {
427
- specName: { type: "string", description: "Spec folder name" },
428
- artifactType: { type: "string", description: "Artifact type to reset" },
467
+ specName: { type: 'string', description: 'Spec folder name' },
429
468
  },
430
- required: ["specName", "artifactType"],
469
+ required: ['specName'],
431
470
  },
432
471
  },
433
472
  {
434
- name: "validate_spec",
435
- description: "Validate a spec for completeness and quality",
473
+ name: 'promote_spec',
474
+ description:
475
+ "Promote approved tasks.md to Kanban tasks. The project is read from the spec's metadata — no need to supply projectId.",
436
476
  inputSchema: {
437
- type: "object",
477
+ type: 'object',
438
478
  properties: {
439
- specName: { type: "string", description: "Spec folder name" },
479
+ specName: { type: 'string', description: 'Spec folder name' },
440
480
  },
441
- required: ["specName"],
481
+ required: ['specName'],
482
+ },
483
+ },
484
+ // Schema Management
485
+ {
486
+ name: 'list_schemas',
487
+ description:
488
+ 'List all available schemas (bundled and project-local) with their artifact DAGs',
489
+ inputSchema: {
490
+ type: 'object',
491
+ properties: {},
442
492
  },
443
493
  },
444
494
  {
445
- name: "promote_spec",
446
- description: "Promote approved tasks.md to Kanban tasks. The project is read from the spec's metadata — no need to supply projectId.",
495
+ name: 'create_schema',
496
+ description:
497
+ 'Create a new project-local schema with schema.yaml and template files under devflow/schemas/<name>/',
447
498
  inputSchema: {
448
- type: "object",
499
+ type: 'object',
449
500
  properties: {
450
- specName: { type: "string", description: "Spec folder name" },
501
+ name: {
502
+ type: 'string',
503
+ description: 'Schema name (kebab-case slug, e.g. "ml-pipeline")',
504
+ },
505
+ artifacts: {
506
+ type: 'array',
507
+ description: 'Array of artifact definitions',
508
+ items: {
509
+ type: 'object',
510
+ properties: {
511
+ id: { type: 'string', description: 'Artifact ID (e.g. "proposal")' },
512
+ generates: {
513
+ type: 'string',
514
+ description: 'Output filename (e.g. "proposal.md")',
515
+ },
516
+ description: { type: 'string', description: 'What this artifact covers' },
517
+ template: {
518
+ type: 'string',
519
+ description: 'Template filename (e.g. "proposal.md")',
520
+ },
521
+ requires: {
522
+ type: 'array',
523
+ items: { type: 'string' },
524
+ description: 'IDs of artifacts that must be approved first',
525
+ },
526
+ },
527
+ required: ['id', 'generates', 'description', 'template', 'requires'],
528
+ },
529
+ },
530
+ qualityRules: {
531
+ type: 'object',
532
+ description: 'Optional quality rules',
533
+ properties: {
534
+ requireRfc2119: { type: 'boolean' },
535
+ minScenariosPerRequirement: { type: 'number' },
536
+ },
537
+ },
538
+ apply: {
539
+ type: 'object',
540
+ description: 'Which artifacts must be approved before promoting to tasks',
541
+ properties: {
542
+ requires: { type: 'array', items: { type: 'string' } },
543
+ },
544
+ },
545
+ templates: {
546
+ type: 'object',
547
+ description:
548
+ 'Map of artifactId → markdown template content. Keys should match artifact IDs.',
549
+ },
451
550
  },
452
- required: ["specName"],
551
+ required: ['name', 'artifacts', 'templates'],
453
552
  },
454
553
  },
455
554
  {
456
- name: "archive_spec",
457
- description: "Move a spec to the archive folder",
555
+ name: 'archive_spec',
556
+ description: 'Move a spec to the archive folder',
458
557
  inputSchema: {
459
- type: "object",
558
+ type: 'object',
460
559
  properties: {
461
- specName: { type: "string", description: "Spec folder name to archive" },
560
+ specName: { type: 'string', description: 'Spec folder name to archive' },
462
561
  },
463
- required: ["specName"],
562
+ required: ['specName'],
464
563
  },
465
564
  },
466
565
  ],
@@ -474,7 +573,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
474
573
  try {
475
574
  switch (name) {
476
575
  // ── Project tools ──────────────────────────────────────────────────────
477
- case "list_projects": {
576
+ case 'list_projects': {
478
577
  const db = await getDb();
479
578
  let query = db.select().from(schema.projects);
480
579
  const projects = await query.orderBy(desc(schema.projects.createdAt));
@@ -484,11 +583,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
484
583
  : projects;
485
584
 
486
585
  return {
487
- content: [{ type: "text", text: JSON.stringify(filtered, null, 2) }],
586
+ content: [{ type: 'text', text: JSON.stringify(filtered, null, 2) }],
488
587
  };
489
588
  }
490
589
 
491
- case "create_project": {
590
+ case 'create_project': {
492
591
  const { name: rawName, description } = args as {
493
592
  name?: string;
494
593
  description?: string;
@@ -499,16 +598,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
499
598
  id: randomUUID(),
500
599
  name: projectName,
501
600
  description: description || null,
502
- status: "active" as const,
601
+ status: 'active' as const,
503
602
  };
504
603
  await db.insert(schema.projects).values(newProject);
505
- broadcastUpdate({ type: "project_created", project: newProject });
604
+ broadcastUpdate({ type: 'project_created', project: newProject });
506
605
  return {
507
- content: [{ type: "text", text: JSON.stringify(newProject, null, 2) }],
606
+ content: [{ type: 'text', text: JSON.stringify(newProject, null, 2) }],
508
607
  };
509
608
  }
510
609
 
511
- case "get_project": {
610
+ case 'get_project': {
512
611
  const { projectId } = args as { projectId: string };
513
612
  const db = await getDb();
514
613
  const project = await db
@@ -518,7 +617,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
518
617
  .limit(1);
519
618
 
520
619
  if (project.length === 0) {
521
- return { content: [{ type: "text", text: "Project not found" }], isError: true };
620
+ return { content: [{ type: 'text', text: 'Project not found' }], isError: true };
522
621
  }
523
622
 
524
623
  const tasks = await db
@@ -530,14 +629,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
530
629
  return {
531
630
  content: [
532
631
  {
533
- type: "text",
632
+ type: 'text',
534
633
  text: JSON.stringify({ ...project[0], tasks }, null, 2),
535
634
  },
536
635
  ],
537
636
  };
538
637
  }
539
638
 
540
- case "update_project": {
639
+ case 'update_project': {
541
640
  const { projectId, ...updates } = args as {
542
641
  projectId: string;
543
642
  name?: string;
@@ -556,13 +655,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
556
655
  .where(eq(schema.projects.id, projectId))
557
656
  .limit(1);
558
657
 
559
- broadcastUpdate({ type: "project_updated", project: updated[0] });
658
+ broadcastUpdate({ type: 'project_updated', project: updated[0] });
560
659
  return {
561
- content: [{ type: "text", text: JSON.stringify(updated[0], null, 2) }],
660
+ content: [{ type: 'text', text: JSON.stringify(updated[0], null, 2) }],
562
661
  };
563
662
  }
564
663
 
565
- case "get_or_create_project": {
664
+ case 'get_or_create_project': {
566
665
  const { name: rawName, description } = args as {
567
666
  name?: string;
568
667
  description?: string;
@@ -577,7 +676,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
577
676
 
578
677
  if (existing.length > 0) {
579
678
  return {
580
- content: [{ type: "text", text: JSON.stringify({ ...existing[0], created: false }, null, 2) }],
679
+ content: [
680
+ { type: 'text', text: JSON.stringify({ ...existing[0], created: false }, null, 2) },
681
+ ],
581
682
  };
582
683
  }
583
684
 
@@ -585,17 +686,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
585
686
  id: randomUUID(),
586
687
  name: projectName,
587
688
  description: description || null,
588
- status: "active" as const,
689
+ status: 'active' as const,
589
690
  };
590
691
  await db.insert(schema.projects).values(newProject);
591
- broadcastUpdate({ type: "project_created", project: newProject });
692
+ broadcastUpdate({ type: 'project_created', project: newProject });
592
693
  return {
593
- content: [{ type: "text", text: JSON.stringify({ ...newProject, created: true }, null, 2) }],
694
+ content: [
695
+ { type: 'text', text: JSON.stringify({ ...newProject, created: true }, null, 2) },
696
+ ],
594
697
  };
595
698
  }
596
699
 
597
700
  // ── Task tools ─────────────────────────────────────────────────────────
598
- case "list_tasks": {
701
+ case 'list_tasks': {
599
702
  const { projectId, specName, status, assignedAgent } = args as {
600
703
  projectId?: string;
601
704
  specName?: string;
@@ -603,10 +706,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
603
706
  assignedAgent?: string;
604
707
  };
605
708
  const db = await getDb();
606
- let tasks = await db
607
- .select()
608
- .from(schema.tasks)
609
- .orderBy(schema.tasks.order);
709
+ let tasks = await db.select().from(schema.tasks).orderBy(schema.tasks.order);
610
710
 
611
711
  if (projectId) tasks = tasks.filter((t) => t.projectId === projectId);
612
712
  if (specName) tasks = tasks.filter((t) => t.specName === specName);
@@ -614,11 +714,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
614
714
  if (assignedAgent) tasks = tasks.filter((t) => t.assignedAgent === assignedAgent);
615
715
 
616
716
  return {
617
- content: [{ type: "text", text: JSON.stringify(tasks, null, 2) }],
717
+ content: [{ type: 'text', text: JSON.stringify(tasks, null, 2) }],
618
718
  };
619
719
  }
620
720
 
621
- case "get_task": {
721
+ case 'get_task': {
622
722
  const { taskId } = args as { taskId: string };
623
723
  const db = await getDb();
624
724
  const task = await db
@@ -628,7 +728,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
628
728
  .limit(1);
629
729
 
630
730
  if (task.length === 0) {
631
- return { content: [{ type: "text", text: "Task not found" }], isError: true };
731
+ return { content: [{ type: 'text', text: 'Task not found' }], isError: true };
632
732
  }
633
733
 
634
734
  const activity = await db
@@ -639,12 +739,27 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
639
739
  .limit(10);
640
740
 
641
741
  return {
642
- content: [{ type: "text", text: JSON.stringify({ ...task[0], recentActivity: activity }, null, 2) }],
742
+ content: [
743
+ {
744
+ type: 'text',
745
+ text: JSON.stringify({ ...task[0], recentActivity: activity }, null, 2),
746
+ },
747
+ ],
643
748
  };
644
749
  }
645
750
 
646
- case "create_task": {
647
- const { title, body: bodyParam, description, priority, status, context, projectId, projectName, specName } = args as {
751
+ case 'create_task': {
752
+ const {
753
+ title,
754
+ body: bodyParam,
755
+ description,
756
+ priority,
757
+ status,
758
+ context,
759
+ projectId,
760
+ projectName,
761
+ specName,
762
+ } = args as {
648
763
  title: string;
649
764
  body?: string;
650
765
  description?: string;
@@ -674,11 +789,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
674
789
  id: randomUUID(),
675
790
  name: sluggedName,
676
791
  description: null,
677
- status: "active" as const,
792
+ status: 'active' as const,
678
793
  };
679
794
  await db.insert(schema.projects).values(newProject);
680
795
  targetProjectId = newProject.id;
681
- broadcastUpdate({ type: "project_created", project: newProject });
796
+ broadcastUpdate({ type: 'project_created', project: newProject });
682
797
  }
683
798
  }
684
799
 
@@ -697,21 +812,26 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
697
812
  specName: specName || null,
698
813
  title,
699
814
  body: resolvedBody,
700
- status: (status || "backlog") as "backlog" | "todo" | "in_progress" | "done",
701
- priority: (priority || "medium") as "low" | "medium" | "high" | "urgent",
815
+ status: (status || 'backlog') as 'backlog' | 'todo' | 'in_progress' | 'done',
816
+ priority: (priority || 'medium') as 'low' | 'medium' | 'high' | 'urgent',
702
817
  assignedAgent: null,
703
818
  order: 0,
704
819
  };
705
820
 
706
821
  await db.insert(schema.tasks).values(newTask);
707
- broadcastUpdate({ type: "task_created", task: newTask });
822
+ broadcastUpdate({ type: 'task_created', task: newTask });
708
823
  return {
709
- content: [{ type: "text", text: JSON.stringify(newTask, null, 2) }],
824
+ content: [{ type: 'text', text: JSON.stringify(newTask, null, 2) }],
710
825
  };
711
826
  }
712
827
 
713
- case "create_tasks_bulk": {
714
- const { projectId, projectName, specName, tasks: taskList } = args as {
828
+ case 'create_tasks_bulk': {
829
+ const {
830
+ projectId,
831
+ projectName,
832
+ specName,
833
+ tasks: taskList,
834
+ } = args as {
715
835
  projectId?: string;
716
836
  projectName?: string;
717
837
  specName?: string;
@@ -743,11 +863,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
743
863
  id: randomUUID(),
744
864
  name: sluggedName,
745
865
  description: null,
746
- status: "active" as const,
866
+ status: 'active' as const,
747
867
  };
748
868
  await db.insert(schema.projects).values(newProject);
749
869
  targetProjectId = newProject.id;
750
- broadcastUpdate({ type: "project_created", project: newProject });
870
+ broadcastUpdate({ type: 'project_created', project: newProject });
751
871
  }
752
872
  }
753
873
 
@@ -766,22 +886,34 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
766
886
  specName: specName || null,
767
887
  title: task.title,
768
888
  body: resolvedBody,
769
- status: (task.status || "backlog") as "backlog" | "todo" | "in_progress" | "done",
770
- priority: (task.priority || "medium") as "low" | "medium" | "high" | "urgent",
889
+ status: (task.status || 'backlog') as 'backlog' | 'todo' | 'in_progress' | 'done',
890
+ priority: (task.priority || 'medium') as 'low' | 'medium' | 'high' | 'urgent',
771
891
  assignedAgent: null,
772
892
  order: index,
773
893
  };
774
894
  });
775
895
 
776
896
  await db.insert(schema.tasks).values(newTasks);
777
- broadcastUpdate({ type: "tasks_created", count: newTasks.length });
897
+ broadcastUpdate({ type: 'tasks_created', count: newTasks.length });
778
898
  return {
779
- content: [{ type: "text", text: JSON.stringify({ created: newTasks.length, tasks: newTasks }, null, 2) }],
899
+ content: [
900
+ {
901
+ type: 'text',
902
+ text: JSON.stringify({ created: newTasks.length, tasks: newTasks }, null, 2),
903
+ },
904
+ ],
780
905
  };
781
906
  }
782
907
 
783
- case "update_task": {
784
- const { taskId, title, body: bodyParam, status, priority, specName } = args as {
908
+ case 'update_task': {
909
+ const {
910
+ taskId,
911
+ title,
912
+ body: bodyParam,
913
+ status,
914
+ priority,
915
+ specName,
916
+ } = args as {
785
917
  taskId: string;
786
918
  title?: string;
787
919
  body?: string;
@@ -807,14 +939,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
807
939
  .where(eq(schema.tasks.id, taskId))
808
940
  .limit(1);
809
941
 
810
- broadcastUpdate({ type: "task_updated", task: updated[0] });
942
+ broadcastUpdate({ type: 'task_updated', task: updated[0] });
811
943
  return {
812
- content: [{ type: "text", text: JSON.stringify(updated[0], null, 2) }],
944
+ content: [{ type: 'text', text: JSON.stringify(updated[0], null, 2) }],
813
945
  };
814
946
  }
815
947
 
816
948
  // ── Agent workflow tools ───────────────────────────────────────────────
817
- case "check_in": {
949
+ case 'check_in': {
818
950
  const { taskId, agentName, executionPlan } = args as {
819
951
  taskId: string;
820
952
  agentName: string;
@@ -824,7 +956,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
824
956
 
825
957
  // If executionPlan provided, prepend it to the existing body
826
958
  const taskSet: Record<string, unknown> = {
827
- status: "in_progress",
959
+ status: 'in_progress',
828
960
  assignedAgent: agentName,
829
961
  updatedAt: new Date(),
830
962
  };
@@ -835,22 +967,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
835
967
  .from(schema.tasks)
836
968
  .where(eq(schema.tasks.id, taskId))
837
969
  .limit(1);
838
- const existingBody = existingTask[0]?.body || "";
970
+ const existingBody = existingTask[0]?.body || '';
839
971
  const planSection = `**Execution Plan**\n\n${executionPlan}`;
840
972
  taskSet.body = existingBody ? `${planSection}\n\n${existingBody}` : planSection;
841
973
  }
842
974
 
843
- await db
844
- .update(schema.tasks)
845
- .set(taskSet)
846
- .where(eq(schema.tasks.id, taskId));
975
+ await db.update(schema.tasks).set(taskSet).where(eq(schema.tasks.id, taskId));
847
976
 
848
977
  await db.insert(schema.agentActivity).values({
849
978
  id: randomUUID(),
850
979
  taskId,
851
980
  agentName,
852
- action: "check_in",
853
- details: executionPlan ? `Plan: ${executionPlan}` : "Checked in",
981
+ action: 'check_in',
982
+ details: executionPlan ? `Plan: ${executionPlan}` : 'Checked in',
854
983
  timestamp: new Date(),
855
984
  });
856
985
 
@@ -860,13 +989,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
860
989
  .where(eq(schema.tasks.id, taskId))
861
990
  .limit(1);
862
991
 
863
- broadcastUpdate({ type: "task_updated", task: task[0] });
992
+ broadcastUpdate({ type: 'task_updated', task: task[0] });
864
993
  return {
865
- content: [{ type: "text", text: JSON.stringify(task[0], null, 2) }],
994
+ content: [{ type: 'text', text: JSON.stringify(task[0], null, 2) }],
866
995
  };
867
996
  }
868
997
 
869
- case "check_out": {
998
+ case 'check_out': {
870
999
  const { taskId, agentName, taskSummary } = args as {
871
1000
  taskId: string;
872
1001
  agentName: string;
@@ -880,7 +1009,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
880
1009
  const db = await getDb();
881
1010
 
882
1011
  const taskSet: Record<string, unknown> = {
883
- status: "done",
1012
+ status: 'done',
884
1013
  updatedAt: new Date(),
885
1014
  };
886
1015
 
@@ -891,7 +1020,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
891
1020
  .from(schema.tasks)
892
1021
  .where(eq(schema.tasks.id, taskId))
893
1022
  .limit(1);
894
- const existingBody = existingTask[0]?.body || "";
1023
+ const existingBody = existingTask[0]?.body || '';
895
1024
  const updatedBody = fillTaskSummary(existingBody, taskSummary);
896
1025
  taskSet.body = updatedBody;
897
1026
 
@@ -903,17 +1032,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
903
1032
  }
904
1033
  }
905
1034
 
906
- await db
907
- .update(schema.tasks)
908
- .set(taskSet)
909
- .where(eq(schema.tasks.id, taskId));
1035
+ await db.update(schema.tasks).set(taskSet).where(eq(schema.tasks.id, taskId));
910
1036
 
911
1037
  await db.insert(schema.agentActivity).values({
912
1038
  id: randomUUID(),
913
1039
  taskId,
914
1040
  agentName,
915
- action: "check_out",
916
- details: taskSummary ? taskSummary.whatWasDone : "Checked out",
1041
+ action: 'check_out',
1042
+ details: taskSummary ? taskSummary.whatWasDone : 'Checked out',
917
1043
  timestamp: new Date(),
918
1044
  });
919
1045
 
@@ -923,13 +1049,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
923
1049
  .where(eq(schema.tasks.id, taskId))
924
1050
  .limit(1);
925
1051
 
926
- broadcastUpdate({ type: "task_updated", task: task[0] });
1052
+ broadcastUpdate({ type: 'task_updated', task: task[0] });
927
1053
  return {
928
- content: [{ type: "text", text: JSON.stringify(task[0], null, 2) }],
1054
+ content: [{ type: 'text', text: JSON.stringify(task[0], null, 2) }],
929
1055
  };
930
1056
  }
931
1057
 
932
- case "log_activity": {
1058
+ case 'log_activity': {
933
1059
  const { taskId, agentName, action, details } = args as {
934
1060
  taskId: string;
935
1061
  agentName: string;
@@ -948,13 +1074,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
948
1074
  };
949
1075
 
950
1076
  await db.insert(schema.agentActivity).values(entry);
951
- broadcastUpdate({ type: "activity_logged", taskId });
1077
+ broadcastUpdate({ type: 'activity_logged', taskId });
952
1078
  return {
953
- content: [{ type: "text", text: JSON.stringify(entry, null, 2) }],
1079
+ content: [{ type: 'text', text: JSON.stringify(entry, null, 2) }],
954
1080
  };
955
1081
  }
956
1082
 
957
- case "get_activity_log": {
1083
+ case 'get_activity_log': {
958
1084
  const { taskId, limit } = args as { taskId: string; limit?: number };
959
1085
  const db = await getDb();
960
1086
 
@@ -966,12 +1092,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
966
1092
  .limit(limit || 50);
967
1093
 
968
1094
  return {
969
- content: [{ type: "text", text: JSON.stringify(activity, null, 2) }],
1095
+ content: [{ type: 'text', text: JSON.stringify(activity, null, 2) }],
970
1096
  };
971
1097
  }
972
1098
 
973
1099
  // ── Spec tools ─────────────────────────────────────────────────────────
974
- case "list_specs": {
1100
+ case 'list_specs': {
975
1101
  const { projectId: filterProjectId } = args as { projectId?: string };
976
1102
  let specs;
977
1103
  if (filterProjectId) {
@@ -985,12 +1111,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
985
1111
  specs = await listSpecs();
986
1112
  }
987
1113
  return {
988
- content: [{ type: "text", text: JSON.stringify(specs, null, 2) }],
1114
+ content: [{ type: 'text', text: JSON.stringify(specs, null, 2) }],
989
1115
  };
990
1116
  }
991
1117
 
992
- case "create_spec": {
993
- const { name: specName, title, projectId, description, schema: schemaName } = args as {
1118
+ case 'create_spec': {
1119
+ const {
1120
+ name: specName,
1121
+ title,
1122
+ projectId,
1123
+ description,
1124
+ schema: schemaName,
1125
+ } = args as {
994
1126
  name: string;
995
1127
  title: string;
996
1128
  projectId: string;
@@ -998,48 +1130,58 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
998
1130
  schema?: string;
999
1131
  };
1000
1132
  await createSpec(specName, title, projectId, description, schemaName);
1001
- broadcastUpdate({ type: "spec_created", specName });
1133
+ broadcastUpdate({ type: 'spec_created', specName });
1002
1134
  return {
1003
- content: [{ type: "text", text: JSON.stringify({ name: specName, title, projectId, created: true }, null, 2) }],
1135
+ content: [
1136
+ {
1137
+ type: 'text',
1138
+ text: JSON.stringify({ name: specName, title, projectId, created: true }, null, 2),
1139
+ },
1140
+ ],
1004
1141
  };
1005
1142
  }
1006
1143
 
1007
- case "get_spec": {
1144
+ case 'get_spec': {
1008
1145
  const { specName } = args as { specName: string };
1009
1146
  const spec = await getSpec(specName);
1010
1147
  return {
1011
- content: [{ type: "text", text: JSON.stringify(spec, null, 2) }],
1148
+ content: [{ type: 'text', text: JSON.stringify(spec, null, 2) }],
1012
1149
  };
1013
1150
  }
1014
1151
 
1015
- case "get_spec_status": {
1152
+ case 'get_spec_status': {
1016
1153
  const { specName } = args as { specName: string };
1017
1154
  const statuses = await getSpecStatus(specName);
1018
1155
  return {
1019
- content: [{ type: "text", text: JSON.stringify(statuses, null, 2) }],
1156
+ content: [{ type: 'text', text: JSON.stringify(statuses, null, 2) }],
1020
1157
  };
1021
1158
  }
1022
1159
 
1023
- case "get_artifact": {
1160
+ case 'get_artifact': {
1024
1161
  const { specName, artifactType } = args as { specName: string; artifactType: string };
1025
1162
  const content = await getArtifact(specName, artifactType);
1026
1163
  if (content === null) {
1027
- return { content: [{ type: "text", text: `Artifact "${artifactType}" not found in spec "${specName}"` }], isError: true };
1164
+ return {
1165
+ content: [
1166
+ { type: 'text', text: `Artifact "${artifactType}" not found in spec "${specName}"` },
1167
+ ],
1168
+ isError: true,
1169
+ };
1028
1170
  }
1029
1171
  return {
1030
- content: [{ type: "text", text: content }],
1172
+ content: [{ type: 'text', text: content }],
1031
1173
  };
1032
1174
  }
1033
1175
 
1034
- case "get_artifact_template": {
1176
+ case 'get_artifact_template': {
1035
1177
  const { specName, artifactType } = args as { specName: string; artifactType: string };
1036
1178
  const template = await getArtifactTemplate(specName, artifactType);
1037
1179
  return {
1038
- content: [{ type: "text", text: template }],
1180
+ content: [{ type: 'text', text: template }],
1039
1181
  };
1040
1182
  }
1041
1183
 
1042
- case "write_artifact": {
1184
+ case 'write_artifact': {
1043
1185
  const { specName, artifactType, content } = args as {
1044
1186
  specName: string;
1045
1187
  artifactType: string;
@@ -1049,65 +1191,76 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1049
1191
  // Check blocker gate
1050
1192
  const statuses = await getSpecStatus(specName);
1051
1193
  const status = statuses.find((s) => s.id === artifactType);
1052
- if (status?.state === "blocked") {
1194
+ if (status?.state === 'blocked') {
1053
1195
  return {
1054
- content: [{ type: "text", text: `Cannot write "${artifactType}": required artifacts (${status.requires.join(", ")}) must be approved first` }],
1196
+ content: [
1197
+ {
1198
+ type: 'text',
1199
+ text: `Cannot write "${artifactType}": required artifacts (${status.requires.join(', ')}) must be approved first`,
1200
+ },
1201
+ ],
1055
1202
  isError: true,
1056
1203
  };
1057
1204
  }
1058
1205
 
1059
1206
  await writeArtifact(specName, artifactType, content);
1060
- broadcastUpdate({ type: "artifact_updated", specName, artifactType });
1207
+ broadcastUpdate({ type: 'artifact_updated', specName, artifactType });
1061
1208
  return {
1062
- content: [{ type: "text", text: `Successfully wrote ${artifactType} for spec "${specName}"` }],
1209
+ content: [
1210
+ { type: 'text', text: `Successfully wrote ${artifactType} for spec "${specName}"` },
1211
+ ],
1063
1212
  };
1064
1213
  }
1065
1214
 
1066
- case "approve_artifact": {
1215
+ case 'approve_artifact': {
1067
1216
  const { specName, artifactType, approvedBy } = args as {
1068
1217
  specName: string;
1069
1218
  artifactType: string;
1070
1219
  approvedBy?: string;
1071
1220
  };
1072
- await approveArtifact(specName, artifactType, approvedBy || "agent");
1073
- broadcastUpdate({ type: "artifact_approved", specName, artifactType });
1221
+ await approveArtifact(specName, artifactType, approvedBy || 'agent');
1222
+ broadcastUpdate({ type: 'artifact_approved', specName, artifactType });
1074
1223
  return {
1075
- content: [{ type: "text", text: `Approved "${artifactType}" for spec "${specName}"` }],
1224
+ content: [{ type: 'text', text: `Approved "${artifactType}" for spec "${specName}"` }],
1076
1225
  };
1077
1226
  }
1078
1227
 
1079
- case "draft_artifact": {
1228
+ case 'draft_artifact': {
1080
1229
  const { specName, artifactType } = args as { specName: string; artifactType: string };
1081
1230
  await draftArtifact(specName, artifactType);
1082
- broadcastUpdate({ type: "artifact_drafted", specName, artifactType });
1231
+ broadcastUpdate({ type: 'artifact_drafted', specName, artifactType });
1083
1232
  return {
1084
- content: [{ type: "text", text: `Reset "${artifactType}" to draft for spec "${specName}"` }],
1233
+ content: [
1234
+ { type: 'text', text: `Reset "${artifactType}" to draft for spec "${specName}"` },
1235
+ ],
1085
1236
  };
1086
1237
  }
1087
1238
 
1088
- case "validate_spec": {
1239
+ case 'validate_spec': {
1089
1240
  const { specName } = args as { specName: string };
1090
1241
  const report = await validateSpec(specName);
1091
1242
  return {
1092
- content: [{ type: "text", text: JSON.stringify(report, null, 2) }],
1243
+ content: [{ type: 'text', text: JSON.stringify(report, null, 2) }],
1093
1244
  };
1094
1245
  }
1095
1246
 
1096
- case "promote_spec": {
1247
+ case 'promote_spec': {
1097
1248
  const { specName } = args as { specName: string };
1098
1249
 
1099
1250
  const specDetail = await getSpec(specName);
1100
1251
  const projectId = specDetail.projectId;
1101
1252
  if (!projectId) {
1102
1253
  return {
1103
- content: [{ type: "text", text: 'Spec has no project — re-create it with a projectId' }],
1254
+ content: [
1255
+ { type: 'text', text: 'Spec has no project — re-create it with a projectId' },
1256
+ ],
1104
1257
  isError: true,
1105
1258
  };
1106
1259
  }
1107
1260
 
1108
- if (specDetail.approvals.artifacts["tasks"]?.state !== "approved") {
1261
+ if (specDetail.approvals.artifacts['tasks']?.state !== 'approved') {
1109
1262
  return {
1110
- content: [{ type: "text", text: 'tasks artifact must be approved before promoting' }],
1263
+ content: [{ type: 'text', text: 'tasks artifact must be approved before promoting' }],
1111
1264
  isError: true,
1112
1265
  };
1113
1266
  }
@@ -1115,7 +1268,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1115
1268
  const parsedTasks = await parseTasksArtifact(specName);
1116
1269
  if (parsedTasks.length === 0) {
1117
1270
  return {
1118
- content: [{ type: "text", text: "No tasks found in tasks.md" }],
1271
+ content: [{ type: 'text', text: 'No tasks found in tasks.md' }],
1119
1272
  isError: true,
1120
1273
  };
1121
1274
  }
@@ -1127,31 +1280,85 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1127
1280
  specName,
1128
1281
  title: task.title,
1129
1282
  body: task.body || null,
1130
- status: "backlog" as const,
1131
- priority: (task.priority || "medium") as "low" | "medium" | "high" | "urgent",
1283
+ status: 'backlog' as const,
1284
+ priority: (task.priority || 'medium') as 'low' | 'medium' | 'high' | 'urgent',
1132
1285
  assignedAgent: null,
1133
1286
  order: index,
1134
1287
  }));
1135
1288
 
1136
1289
  await db.insert(schema.tasks).values(newTasks);
1137
- broadcastUpdate({ type: "spec_promoted", specName, taskCount: newTasks.length });
1290
+ broadcastUpdate({ type: 'spec_promoted', specName, taskCount: newTasks.length });
1291
+ return {
1292
+ content: [
1293
+ {
1294
+ type: 'text',
1295
+ text: JSON.stringify({ promoted: newTasks.length, tasks: newTasks }, null, 2),
1296
+ },
1297
+ ],
1298
+ };
1299
+ }
1300
+
1301
+ // ── Schema tools ───────────────────────────────────────────────────────
1302
+ case 'list_schemas': {
1303
+ const entries = await listSchemas(getSpecsDir());
1304
+ const result = entries.map((e) => ({
1305
+ id: e.id,
1306
+ source: e.source,
1307
+ artifacts: e.schema.artifacts.map((a) => ({
1308
+ id: a.id,
1309
+ generates: a.generates,
1310
+ description: a.description,
1311
+ requires: a.requires,
1312
+ })),
1313
+ qualityRules: e.schema.qualityRules,
1314
+ apply: e.schema.apply,
1315
+ }));
1138
1316
  return {
1139
- content: [{ type: "text", text: JSON.stringify({ promoted: newTasks.length, tasks: newTasks }, null, 2) }],
1317
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
1318
+ };
1319
+ }
1320
+
1321
+ case 'create_schema': {
1322
+ const { name, artifacts, qualityRules, apply, templates } = args as {
1323
+ name: string;
1324
+ artifacts: Array<{
1325
+ id: string;
1326
+ generates: string;
1327
+ description: string;
1328
+ template: string;
1329
+ requires: string[];
1330
+ }>;
1331
+ qualityRules?: { requireRfc2119?: boolean; minScenariosPerRequirement?: number };
1332
+ apply?: { requires: string[] };
1333
+ templates: Record<string, string>;
1334
+ };
1335
+ const schemaDir = await createSchema(
1336
+ { name, artifacts, qualityRules, apply, templates },
1337
+ getSpecsDir(),
1338
+ );
1339
+ broadcastUpdate({ type: 'schema_created', name });
1340
+ return {
1341
+ content: [
1342
+ {
1343
+ type: 'text',
1344
+ text: `Created schema "${name}" at ${schemaDir}\n\nArtifacts: ${artifacts.map((a) => a.id).join(' → ')}`,
1345
+ },
1346
+ ],
1140
1347
  };
1141
1348
  }
1142
1349
 
1143
- case "archive_spec": {
1350
+ case 'archive_spec': {
1144
1351
  const { specName } = args as { specName: string };
1145
1352
  await archiveSpec(specName);
1146
- broadcastUpdate({ type: "spec_archived", specName });
1353
+ broadcastUpdate({ type: 'spec_archived', specName });
1147
1354
  return {
1148
- content: [{ type: "text", text: `Archived spec "${specName}"` }],
1355
+ content: [{ type: 'text', text: `Archived spec "${specName}"` }],
1149
1356
  };
1150
1357
  }
1151
1358
 
1152
1359
  default:
1153
1360
  return {
1154
- content: [{ type: "text", text: `Unknown tool: ${name}` }],
1361
+ content: [{ type: 'text', text: `Unknown tool: ${name}` }],
1155
1362
  isError: true,
1156
1363
  };
1157
1364
  }
@@ -1159,7 +1366,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1159
1366
  return {
1160
1367
  content: [
1161
1368
  {
1162
- type: "text",
1369
+ type: 'text',
1163
1370
  text: `Error: ${error instanceof Error ? error.message : String(error)}`,
1164
1371
  },
1165
1372
  ],
@@ -1170,34 +1377,34 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1170
1377
 
1171
1378
  function resolveAgentName(): string {
1172
1379
  // 1. Env-based detection (set by agent host process)
1173
- if (process.env.CLAUDECODE || process.env.CLAUDE_CODE_ENTRYPOINT) return "Claude Code";
1174
- if (process.env.CODEX) return "Codex";
1175
- if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_CHANNEL) return "Cursor";
1176
- if (process.env.WINDSURF_PLUGIN_VERSION) return "Windsurf";
1380
+ if (process.env.CLAUDECODE || process.env.CLAUDE_CODE_ENTRYPOINT) return 'Claude Code';
1381
+ if (process.env.CODEX) return 'Codex';
1382
+ if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_CHANNEL) return 'Cursor';
1383
+ if (process.env.WINDSURF_PLUGIN_VERSION) return 'Windsurf';
1177
1384
  // 2. MCP clientInfo (available after initialize handshake)
1178
1385
  const clientInfo = server.getClientVersion();
1179
1386
  if (clientInfo?.name) {
1180
1387
  const n = clientInfo.name.toLowerCase();
1181
- if (n.includes("claude")) return "Claude Code";
1182
- if (n.includes("codex")) return "Codex";
1183
- if (n.includes("cursor")) return "Cursor";
1184
- if (n.includes("windsurf")) return "Windsurf";
1185
- if (n.includes("copilot")) return "Copilot";
1388
+ if (n.includes('claude')) return 'Claude Code';
1389
+ if (n.includes('codex')) return 'Codex';
1390
+ if (n.includes('cursor')) return 'Cursor';
1391
+ if (n.includes('windsurf')) return 'Windsurf';
1392
+ if (n.includes('copilot')) return 'Copilot';
1186
1393
  return clientInfo.name; // use as-is if unrecognized
1187
1394
  }
1188
- return "Unknown Agent";
1395
+ return 'Unknown Agent';
1189
1396
  }
1190
1397
 
1191
1398
  async function main() {
1192
1399
  const transport = new StdioServerTransport();
1193
1400
  await server.connect(transport);
1194
- console.error("DevFlow MCP server running on stdio");
1401
+ console.error('DevFlow MCP server running on stdio');
1195
1402
 
1196
1403
  // Re-broadcast identify after initialize so clientInfo is available
1197
1404
  // The initial identify (sent on WS open) uses env-based detection;
1198
1405
  // this one fires ~1s later with the MCP clientInfo name as fallback.
1199
1406
  setTimeout(() => {
1200
- broadcastUpdate({ type: "identify", role: "mcp", agent: resolveAgentName() });
1407
+ broadcastUpdate({ type: 'identify', role: 'mcp', agent: resolveAgentName() });
1201
1408
  }, 1000);
1202
1409
  }
1203
1410