@shepai/cli 1.129.0-pr413.57e1c33 → 1.129.1-pr408.36c273f

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 (178) hide show
  1. package/dist/packages/core/src/application/use-cases/repositories/add-repository.use-case.d.ts.map +1 -1
  2. package/dist/packages/core/src/application/use-cases/repositories/add-repository.use-case.js +5 -2
  3. package/dist/packages/core/src/infrastructure/repositories/sqlite-repository.repository.js +2 -2
  4. package/dist/packages/core/src/infrastructure/services/agents/common/agent-validator.service.js +1 -1
  5. package/dist/packages/core/src/infrastructure/services/agents/common/executors/cursor-executor.service.d.ts +5 -5
  6. package/dist/packages/core/src/infrastructure/services/agents/common/executors/cursor-executor.service.js +8 -8
  7. package/dist/packages/core/src/infrastructure/services/deployment/deployment.service.d.ts.map +1 -1
  8. package/dist/packages/core/src/infrastructure/services/deployment/deployment.service.js +6 -20
  9. package/dist/src/presentation/web/app/(dashboard)/get-graph-data.d.ts +8 -0
  10. package/dist/src/presentation/web/app/(dashboard)/get-graph-data.d.ts.map +1 -1
  11. package/dist/src/presentation/web/app/(dashboard)/get-graph-data.js +75 -22
  12. package/dist/src/presentation/web/app/actions/check-agent-auth.js +1 -1
  13. package/dist/src/presentation/web/app/build-graph-nodes.d.ts +2 -0
  14. package/dist/src/presentation/web/app/build-graph-nodes.d.ts.map +1 -1
  15. package/dist/src/presentation/web/app/build-graph-nodes.js +2 -0
  16. package/dist/src/presentation/web/components/common/feature-node/feature-node.d.ts.map +1 -1
  17. package/dist/src/presentation/web/components/common/feature-node/feature-node.js +3 -3
  18. package/dist/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.d.ts.map +1 -1
  19. package/dist/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.js +1 -1
  20. package/dist/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.stories.d.ts +6 -0
  21. package/dist/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.stories.d.ts.map +1 -1
  22. package/dist/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.stories.js +96 -1
  23. package/dist/src/presentation/web/components/common/product-decisions-summary/product-decisions-summary.d.ts.map +1 -1
  24. package/dist/src/presentation/web/components/common/product-decisions-summary/product-decisions-summary.js +1 -1
  25. package/dist/src/presentation/web/components/common/repository-node/repository-node-config.d.ts +2 -0
  26. package/dist/src/presentation/web/components/common/repository-node/repository-node-config.d.ts.map +1 -1
  27. package/dist/src/presentation/web/components/common/repository-node/repository-node.d.ts.map +1 -1
  28. package/dist/src/presentation/web/components/common/repository-node/repository-node.js +7 -3
  29. package/dist/src/presentation/web/components/common/repository-node/repository-node.stories.d.ts +2 -0
  30. package/dist/src/presentation/web/components/common/repository-node/repository-node.stories.d.ts.map +1 -1
  31. package/dist/src/presentation/web/components/common/repository-node/repository-node.stories.js +14 -0
  32. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts.map +1 -1
  33. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js +1 -0
  34. package/dist/src/presentation/web/lib/derive-graph.d.ts.map +1 -1
  35. package/dist/src/presentation/web/lib/derive-graph.js +5 -2
  36. package/dist/src/presentation/web/lib/layout-with-dagre.js +2 -2
  37. package/dist/tsconfig.build.tsbuildinfo +1 -1
  38. package/package.json +1 -1
  39. package/web/.next/BUILD_ID +1 -1
  40. package/web/.next/build-manifest.json +2 -2
  41. package/web/.next/fallback-build-manifest.json +2 -2
  42. package/web/.next/prerender-manifest.json +3 -3
  43. package/web/.next/required-server-files.js +2 -2
  44. package/web/.next/required-server-files.json +2 -2
  45. package/web/.next/server/app/(dashboard)/@drawer/adopt/page/server-reference-manifest.json +24 -24
  46. package/web/.next/server/app/(dashboard)/@drawer/adopt/page.js.nft.json +1 -1
  47. package/web/.next/server/app/(dashboard)/@drawer/adopt/page_client-reference-manifest.js +1 -1
  48. package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +24 -24
  49. package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  50. package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  51. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page/server-reference-manifest.json +30 -30
  52. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  53. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  54. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +30 -30
  55. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  56. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  57. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +22 -22
  58. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  59. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  60. package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +24 -24
  61. package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  62. package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  63. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page/server-reference-manifest.json +30 -30
  64. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  65. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  66. package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +30 -30
  67. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  68. package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  69. package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +22 -22
  70. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  71. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  72. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +22 -22
  73. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  74. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  75. package/web/.next/server/app/_global-error.html +2 -2
  76. package/web/.next/server/app/_global-error.rsc +1 -1
  77. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  78. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  79. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  80. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  81. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  82. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +3 -3
  83. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  84. package/web/.next/server/app/settings/page/server-reference-manifest.json +8 -8
  85. package/web/.next/server/app/settings/page.js.nft.json +1 -1
  86. package/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  87. package/web/.next/server/app/skills/page/server-reference-manifest.json +8 -8
  88. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  89. package/web/.next/server/app/tools/page/server-reference-manifest.json +8 -8
  90. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  91. package/web/.next/server/app/version/page/server-reference-manifest.json +3 -3
  92. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  93. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_370c43b1.js +1 -1
  94. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_370c43b1.js.map +1 -1
  95. package/web/.next/server/chunks/[root-of-the-server]__a402b567._.js +1 -1
  96. package/web/.next/server/chunks/ssr/744ca_web__next-internal_server_app_(dashboard)_@drawer_adopt_page_actions_4e741df2.js +1 -1
  97. package/web/.next/server/chunks/ssr/744ca_web__next-internal_server_app_(dashboard)_@drawer_adopt_page_actions_4e741df2.js.map +1 -1
  98. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
  99. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
  100. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_026fdffa.js +1 -1
  101. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_026fdffa.js.map +1 -1
  102. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_8b91e197.js +1 -1
  103. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_8b91e197.js.map +1 -1
  104. package/web/.next/server/chunks/ssr/[root-of-the-server]__0e842eba._.js +1 -1
  105. package/web/.next/server/chunks/ssr/[root-of-the-server]__0e842eba._.js.map +1 -1
  106. package/web/.next/server/chunks/ssr/[root-of-the-server]__2f886f77._.js +2 -2
  107. package/web/.next/server/chunks/ssr/[root-of-the-server]__357d99f9._.js +1 -1
  108. package/web/.next/server/chunks/ssr/[root-of-the-server]__43f51aa6._.js +1 -1
  109. package/web/.next/server/chunks/ssr/[root-of-the-server]__43f51aa6._.js.map +1 -1
  110. package/web/.next/server/chunks/ssr/[root-of-the-server]__60684242._.js +2 -2
  111. package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.js +2 -2
  112. package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.js.map +1 -1
  113. package/web/.next/server/chunks/ssr/[root-of-the-server]__755cfe02._.js +1 -1
  114. package/web/.next/server/chunks/ssr/[root-of-the-server]__755cfe02._.js.map +1 -1
  115. package/web/.next/server/chunks/ssr/[root-of-the-server]__815546bd._.js +1 -1
  116. package/web/.next/server/chunks/ssr/[root-of-the-server]__815546bd._.js.map +1 -1
  117. package/web/.next/server/chunks/ssr/[root-of-the-server]__a628fb91._.js +1 -1
  118. package/web/.next/server/chunks/ssr/[root-of-the-server]__a628fb91._.js.map +1 -1
  119. package/web/.next/server/chunks/ssr/[root-of-the-server]__bfba58e6._.js +1 -1
  120. package/web/.next/server/chunks/ssr/[root-of-the-server]__bfba58e6._.js.map +1 -1
  121. package/web/.next/server/chunks/ssr/[root-of-the-server]__c3ab8422._.js +1 -1
  122. package/web/.next/server/chunks/ssr/[root-of-the-server]__c3ab8422._.js.map +1 -1
  123. package/web/.next/server/chunks/ssr/[root-of-the-server]__f4a463ee._.js +2 -2
  124. package/web/.next/server/chunks/ssr/[root-of-the-server]__f4a463ee._.js.map +1 -1
  125. package/web/.next/server/chunks/ssr/_02ec1aea._.js +1 -1
  126. package/web/.next/server/chunks/ssr/_02ec1aea._.js.map +1 -1
  127. package/web/.next/server/chunks/ssr/_7dca1882._.js +1 -1
  128. package/web/.next/server/chunks/ssr/_7dca1882._.js.map +1 -1
  129. package/web/.next/server/chunks/ssr/_a9f57758._.js +1 -1
  130. package/web/.next/server/chunks/ssr/{_4c579535._.js → _c577275e._.js} +2 -2
  131. package/web/.next/server/chunks/ssr/{_4c579535._.js.map → _c577275e._.js.map} +1 -1
  132. package/web/.next/server/chunks/ssr/_d8575088._.js +1 -1
  133. package/web/.next/server/chunks/ssr/_d8575088._.js.map +1 -1
  134. package/web/.next/server/chunks/ssr/_f39a1adb._.js +1 -1
  135. package/web/.next/server/chunks/ssr/_f39a1adb._.js.map +1 -1
  136. package/web/.next/server/chunks/ssr/{_ea5daaaf._.js → _fab5571d._.js} +2 -2
  137. package/web/.next/server/chunks/ssr/{_ea5daaaf._.js.map → _fab5571d._.js.map} +1 -1
  138. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
  139. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js.map +1 -1
  140. package/web/.next/server/chunks/ssr/src_presentation_web_8e7fe0d1._.js +2 -2
  141. package/web/.next/server/chunks/ssr/src_presentation_web_8e7fe0d1._.js.map +1 -1
  142. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_ca051134.js +1 -1
  143. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_ca051134.js.map +1 -1
  144. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1b176e3c.js +1 -1
  145. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1b176e3c.js.map +1 -1
  146. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_bd9f0dda.js +1 -1
  147. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_bd9f0dda.js.map +1 -1
  148. package/web/.next/server/chunks/ssr/{src_presentation_web_8401cef2._.js → src_presentation_web_a64706b8._.js} +2 -2
  149. package/web/.next/server/chunks/ssr/{src_presentation_web_8401cef2._.js.map → src_presentation_web_a64706b8._.js.map} +1 -1
  150. package/web/.next/server/chunks/ssr/src_presentation_web_b9023017._.js +2 -2
  151. package/web/.next/server/chunks/ssr/src_presentation_web_b9023017._.js.map +1 -1
  152. package/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js +1 -1
  153. package/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js.map +1 -1
  154. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  155. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +1 -1
  156. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  157. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
  158. package/web/.next/server/pages/500.html +2 -2
  159. package/web/.next/server/server-reference-manifest.js +1 -1
  160. package/web/.next/server/server-reference-manifest.json +38 -38
  161. package/web/.next/static/chunks/153699224addc51e.js +2 -0
  162. package/web/.next/static/chunks/{2c0f72e13873aa11.js → 4ee771feb075f293.js} +1 -1
  163. package/web/.next/static/chunks/{e46b2039c3259c91.js → 5b223ee4211e9f97.js} +1 -1
  164. package/web/.next/static/chunks/{f0f36e5435461097.js → 622d45c8fcd8c71a.js} +1 -1
  165. package/web/.next/static/chunks/{36544f2ce44008e9.js → 6515a36a0fa239b1.js} +1 -1
  166. package/web/.next/static/chunks/{5ee76a706c272a48.js → 6eb57a04da8dd9ec.js} +1 -1
  167. package/web/.next/static/chunks/{963c38281082498c.js → 718a86f43c105bf6.js} +1 -1
  168. package/web/.next/static/chunks/{b4211828df7f8b84.js → 96e8f2ffe7c7dc0e.js} +1 -1
  169. package/web/.next/static/chunks/{4fa84a327da62d41.js → c0f289d181410faf.js} +1 -1
  170. package/web/.next/static/chunks/{e9b5b99f78a82ff2.js → c134a4de481e7510.js} +1 -1
  171. package/web/.next/static/chunks/{e6b6017a4788e869.js → d1bf55cbdce4ca71.js} +1 -1
  172. package/web/.next/static/chunks/da484d44874006a2.css +1 -0
  173. package/web/.next/static/chunks/{8435f506faaeab86.js → e23741487ff1c417.js} +1 -1
  174. package/web/.next/static/chunks/98dfbecc5ee30ce1.css +0 -1
  175. package/web/.next/static/chunks/fbe09387689edb43.js +0 -2
  176. /package/web/.next/static/{2FNRt2NFwO7rgcja0AV1h → jGtdRopyVmCvF4SZRRryd}/_buildManifest.js +0 -0
  177. /package/web/.next/static/{2FNRt2NFwO7rgcja0AV1h → jGtdRopyVmCvF4SZRRryd}/_clientMiddlewareManifest.json +0 -0
  178. /package/web/.next/static/{2FNRt2NFwO7rgcja0AV1h → jGtdRopyVmCvF4SZRRryd}/_ssgManifest.js +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"add-repository.use-case.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/application/use-cases/repositories/add-repository.use-case.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AACtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oEAAoE,CAAC;AAEhH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAcD,qBACa,oBAAoB;IAG7B,OAAO,CAAC,QAAQ,CAAC,cAAc;gBAAd,cAAc,EAAE,qBAAqB;IAGlD,OAAO,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,CAAC;CA8B9D"}
1
+ {"version":3,"file":"add-repository.use-case.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/application/use-cases/repositories/add-repository.use-case.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AACtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oEAAoE,CAAC;AAEhH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAcD,qBACa,oBAAoB;IAG7B,OAAO,CAAC,QAAQ,CAAC,cAAc;gBAAd,cAAc,EAAE,qBAAqB;IAGlD,OAAO,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,CAAC;CAiC9D"}
@@ -41,11 +41,14 @@ let AddRepositoryUseCase = class AddRepositoryUseCase {
41
41
  if (existing) {
42
42
  return existing;
43
43
  }
44
- // Check for soft-deleted repository — restore it instead of creating a duplicate
44
+ // Check for soft-deleted repository — restore it instead of creating a duplicate.
45
+ // Reset createdAt so the restored repo sorts as "newest" on the canvas
46
+ // (otherwise the old createdAt would place it above repos added later).
45
47
  const deleted = await this.repositoryRepo.findByPathIncludingDeleted(normalizedPath);
46
48
  if (deleted) {
49
+ const now = new Date();
47
50
  await this.repositoryRepo.restore(deleted.id);
48
- return { ...deleted, deletedAt: undefined, updatedAt: new Date() };
51
+ return { ...deleted, deletedAt: undefined, createdAt: now, updatedAt: now };
49
52
  }
50
53
  const now = new Date();
51
54
  const name = input.name ?? normalizedPath.split('/').pop() ?? normalizedPath;
@@ -94,8 +94,8 @@ let SQLiteRepositoryRepository = SQLiteRepositoryRepository_1 = class SQLiteRepo
94
94
  }
95
95
  async restore(id) {
96
96
  const now = Date.now();
97
- const stmt = this.db.prepare('UPDATE repositories SET deleted_at = NULL, updated_at = ? WHERE id = ?');
98
- stmt.run(now, id);
97
+ const stmt = this.db.prepare('UPDATE repositories SET deleted_at = NULL, created_at = ?, updated_at = ? WHERE id = ?');
98
+ stmt.run(now, now, id);
99
99
  }
100
100
  static normalizeRemoteUrl(url) {
101
101
  return url.replace(/\.git$/, '').toLowerCase();
@@ -26,7 +26,7 @@ import { injectable, inject } from 'tsyringe';
26
26
  */
27
27
  const AGENT_BINARY_MAP = {
28
28
  'claude-code': 'claude',
29
- cursor: 'cursor-agent',
29
+ cursor: 'agent',
30
30
  'gemini-cli': 'gemini',
31
31
  };
32
32
  /**
@@ -2,7 +2,7 @@
2
2
  * Cursor Executor Service
3
3
  *
4
4
  * Infrastructure implementation of IAgentExecutor for the Cursor agent.
5
- * Executes prompts via the `cursor-agent` CLI subprocess with JSON and stream-json
5
+ * Executes prompts via the `agent` CLI subprocess with JSON and stream-json
6
6
  * output formats.
7
7
  *
8
8
  * Uses constructor dependency injection for the spawn function
@@ -13,7 +13,7 @@ import type { IAgentExecutor, AgentExecutionOptions, AgentExecutionResult, Agent
13
13
  import type { SpawnFunction } from '../types.js';
14
14
  /**
15
15
  * Executor service for Cursor agent.
16
- * Uses subprocess spawning to interact with the `cursor-agent` CLI.
16
+ * Uses subprocess spawning to interact with the `agent` CLI.
17
17
  */
18
18
  export declare class CursorExecutorService implements IAgentExecutor {
19
19
  private readonly spawn;
@@ -35,13 +35,13 @@ export declare class CursorExecutorService implements IAgentExecutor {
35
35
  /**
36
36
  * Spawn the agent process, handling Windows specially via PowerShell.
37
37
  *
38
- * On Windows, cursor CLI ships as `cursor-agent.cmd` which requires `shell: true`,
38
+ * On Windows, cursor CLI ships as `agent.cmd` which requires `shell: true`,
39
39
  * but cmd.exe mangles long `-p` arguments (8191-char limit + special chars).
40
- * Solution: write prompt to a temp file, invoke cursor-agent via PowerShell which
40
+ * Solution: write prompt to a temp file, invoke agent via PowerShell which
41
41
  * reads the file and passes the content as `-p`. PowerShell handles long
42
42
  * strings natively (32K limit) and doesn't mangle arguments.
43
43
  *
44
- * On Linux/macOS, spawn `cursor-agent` directly — no shell needed.
44
+ * On Linux/macOS, spawn `agent` directly — no shell needed.
45
45
  */
46
46
  private spawnAgent;
47
47
  private parseStreamLine;
@@ -2,7 +2,7 @@
2
2
  * Cursor Executor Service
3
3
  *
4
4
  * Infrastructure implementation of IAgentExecutor for the Cursor agent.
5
- * Executes prompts via the `cursor-agent` CLI subprocess with JSON and stream-json
5
+ * Executes prompts via the `agent` CLI subprocess with JSON and stream-json
6
6
  * output formats.
7
7
  *
8
8
  * Uses constructor dependency injection for the spawn function
@@ -32,7 +32,7 @@ function toCursorModelName(model) {
32
32
  const SUPPORTED_FEATURES = new Set(['session-resume', 'streaming']);
33
33
  /**
34
34
  * Executor service for Cursor agent.
35
- * Uses subprocess spawning to interact with the `cursor-agent` CLI.
35
+ * Uses subprocess spawning to interact with the `agent` CLI.
36
36
  */
37
37
  export class CursorExecutorService {
38
38
  spawn;
@@ -312,13 +312,13 @@ export class CursorExecutorService {
312
312
  /**
313
313
  * Spawn the agent process, handling Windows specially via PowerShell.
314
314
  *
315
- * On Windows, cursor CLI ships as `cursor-agent.cmd` which requires `shell: true`,
315
+ * On Windows, cursor CLI ships as `agent.cmd` which requires `shell: true`,
316
316
  * but cmd.exe mangles long `-p` arguments (8191-char limit + special chars).
317
- * Solution: write prompt to a temp file, invoke cursor-agent via PowerShell which
317
+ * Solution: write prompt to a temp file, invoke agent via PowerShell which
318
318
  * reads the file and passes the content as `-p`. PowerShell handles long
319
319
  * strings natively (32K limit) and doesn't mangle arguments.
320
320
  *
321
- * On Linux/macOS, spawn `cursor-agent` directly — no shell needed.
321
+ * On Linux/macOS, spawn `agent` directly — no shell needed.
322
322
  */
323
323
  spawnAgent(prompt, args, options) {
324
324
  const { CLAUDECODE: _, ...cleanEnv } = process.env;
@@ -330,7 +330,7 @@ export class CursorExecutorService {
330
330
  // Build the agent args WITHOUT -p and the prompt (they go via temp file)
331
331
  const agentFlags = args.filter((a) => a !== '-p' && a !== prompt).join(' ');
332
332
  const safePath = tmpFile.replace(/'/g, "''");
333
- const psCmd = `$p = Get-Content -Raw '${safePath}'; & cursor-agent ${agentFlags} -p $p`;
333
+ const psCmd = `$p = Get-Content -Raw '${safePath}'; & agent ${agentFlags} -p $p`;
334
334
  this.log(`Windows PowerShell mode: wrote ${prompt.length} chars to ${tmpFile}`);
335
335
  this.log(`PS command: ${psCmd.replace(prompt, `<${prompt.length} chars>`)}`);
336
336
  const proc = this.spawn('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', psCmd], {
@@ -351,9 +351,9 @@ export class CursorExecutorService {
351
351
  };
352
352
  if (cwd)
353
353
  spawnOpts.cwd = cwd;
354
- this.log(`Spawning: cursor-agent ${args.map((a) => (a.length > 80 ? `${a.slice(0, 77)}...` : a)).join(' ')}`);
354
+ this.log(`Spawning: agent ${args.map((a) => (a.length > 80 ? `${a.slice(0, 77)}...` : a)).join(' ')}`);
355
355
  this.log(`Spawn cwd: ${cwd ?? '(inherited)'}`);
356
- const proc = this.spawn('cursor-agent', args, spawnOpts);
356
+ const proc = this.spawn('agent', args, spawnOpts);
357
357
  this.log(`Subprocess PID: ${proc.pid ?? 'undefined (spawn may have failed)'}`);
358
358
  if (proc.stdin)
359
359
  proc.stdin.end();
@@ -1 +1 @@
1
- {"version":3,"file":"deployment.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/deployment/deployment.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,KAAK,EAAmC,MAAM,oBAAoB,CAAC;AAE5E,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAK3C,OAAO,KAAK,EACV,kBAAkB,EAClB,gBAAgB,EAChB,QAAQ,EACT,MAAM,qEAAqE,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAiCzD,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,eAAe,EAAE,OAAO,eAAe,CAAC;IACxC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,MAAM,KAAK,IAAI,CAAC;IAC7D,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;CACnC;AA0CD,qBAAa,iBAAkB,YAAW,kBAAkB;IAC1D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsC;IAClE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAwB;IAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,EAAE,CAAkC;gBAEhC,IAAI,GAAE,OAAO,CAAC,qBAAqB,CAAM;IAIrD;;OAEG;IACH,WAAW,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI;IAIxC;;;OAGG;IACH,UAAU,IAAI,IAAI;IA4ClB;;;OAGG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,SAAe,GAAG,IAAI;IAsF5E;;;;OAIG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI;IA4CpD;;OAEG;IACG,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0D3C;;OAEG;IACH,OAAO,IAAI,IAAI;IAef;;OAEG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,EAAE,GAAG,IAAI;IAM5C;;OAEG;IACH,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAG,IAAI;IAI1D;;OAEG;IACH,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAG,IAAI;IAM3D,OAAO,CAAC,QAAQ;IAuBhB,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,QAAQ;IAShB,OAAO,CAAC,MAAM;IAed,OAAO,CAAC,WAAW;IAQnB;;;OAGG;IACH,OAAO,CAAC,oBAAoB;YA4Dd,aAAa;IAW3B,OAAO,CAAC,WAAW;CASpB"}
1
+ {"version":3,"file":"deployment.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/deployment/deployment.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAE9D,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAO3C,OAAO,KAAK,EACV,kBAAkB,EAClB,gBAAgB,EAChB,QAAQ,EACT,MAAM,qEAAqE,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAgCzD,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,eAAe,EAAE,OAAO,eAAe,CAAC;IACxC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,MAAM,KAAK,IAAI,CAAC;IAC7D,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;CACnC;AA6BD,qBAAa,iBAAkB,YAAW,kBAAkB;IAC1D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsC;IAClE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAwB;IAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,EAAE,CAAkC;gBAEhC,IAAI,GAAE,OAAO,CAAC,qBAAqB,CAAM;IAIrD;;OAEG;IACH,WAAW,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI;IAIxC;;;OAGG;IACH,UAAU,IAAI,IAAI;IA4ClB;;;OAGG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,SAAe,GAAG,IAAI;IAqF5E;;;;OAIG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI;IA4CpD;;OAEG;IACG,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0D3C;;OAEG;IACH,OAAO,IAAI,IAAI;IAef;;OAEG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,EAAE,GAAG,IAAI;IAM5C;;OAEG;IACH,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAG,IAAI;IAI1D;;OAEG;IACH,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAG,IAAI;IAM3D,OAAO,CAAC,QAAQ;IAuBhB,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,QAAQ;IAShB,OAAO,CAAC,MAAM;IAed,OAAO,CAAC,WAAW;IAQnB;;;OAGG;IACH,OAAO,CAAC,oBAAoB;YA4Dd,aAAa;IAW3B,OAAO,CAAC,WAAW;CASpB"}
@@ -9,17 +9,18 @@
9
9
  * On startup, call `recoverAll()` to reconcile the DB with actual
10
10
  * process state (clean up dead PIDs, re-adopt live ones).
11
11
  */
12
- import { spawn, execFileSync } from 'node:child_process';
12
+ import { spawn } from 'node:child_process';
13
13
  import { EventEmitter } from 'node:events';
14
- // On Unix we use process.kill(-pid) to send signals to the process GROUP
15
- // (detached: true puts the child in its own group via setsid()).
16
- // On Windows, negative PIDs are not supported use taskkill /T for tree kill.
14
+ // NOTE: We intentionally do NOT use tree-kill here. tree-kill traverses
15
+ // /proc to find child processes which can accidentally kill parent processes
16
+ // (including the Shep web UI). Instead, we use process.kill(-pid) to send
17
+ // signals to the process GROUP (since we spawn with detached: true, the
18
+ // child gets its own process group via setsid()).
17
19
  import { DeploymentState } from '../../../domain/generated/output.js';
18
20
  import { detectDevScript } from './detect-dev-script.js';
19
21
  import { createDeploymentLogger } from './deployment-logger.js';
20
22
  import { parsePort } from './parse-port.js';
21
23
  import { LogRingBuffer } from './log-ring-buffer.js';
22
- import { IS_WINDOWS } from '../../platform.js';
23
24
  const log = createDeploymentLogger('[DeploymentService]');
24
25
  const POLL_INTERVAL_MS = 200;
25
26
  const MAX_WAIT_MS = 5000;
@@ -27,20 +28,6 @@ const defaultDeps = {
27
28
  spawn,
28
29
  detectDevScript,
29
30
  kill: (pid, signal) => {
30
- if (IS_WINDOWS) {
31
- // On Windows, negative PIDs are not supported. Use taskkill /T to
32
- // kill the entire process tree (shell + child dev server).
33
- try {
34
- execFileSync('taskkill', ['/F', '/T', '/PID', String(pid)], {
35
- stdio: 'ignore',
36
- windowsHide: true,
37
- });
38
- }
39
- catch {
40
- // Process already dead
41
- }
42
- return;
43
- }
44
31
  try {
45
32
  // Kill the entire process group (negative PID) — safe because
46
33
  // detached: true puts the child in its own group via setsid().
@@ -153,7 +140,6 @@ export class DeploymentService {
153
140
  cwd: targetPath,
154
141
  detached: true,
155
142
  stdio: ['ignore', 'pipe', 'pipe'],
156
- ...(IS_WINDOWS ? { windowsHide: true } : {}),
157
143
  });
158
144
  if (!child.pid) {
159
145
  log.error('spawn() returned no PID — process failed to start');
@@ -6,6 +6,14 @@ export interface RepoGitInfo {
6
6
  committer: string;
7
7
  behindCount: number | null;
8
8
  }
9
+ export type GitInfoResult = {
10
+ status: 'loading';
11
+ } | {
12
+ status: 'ready';
13
+ data: RepoGitInfo;
14
+ } | {
15
+ status: 'not-a-repo';
16
+ };
9
17
  export declare function getGraphData(): Promise<{
10
18
  nodes: CanvasNodeType[];
11
19
  edges: Edge[];
@@ -1 +1 @@
1
- {"version":3,"file":"get-graph-data.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/(dashboard)/get-graph-data.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAE5E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAI1C,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAqDD,wBAAsB,YAAY,IAAI,OAAO,CAAC;IAAE,KAAK,EAAE,cAAc,EAAE,CAAC;IAAC,KAAK,EAAE,IAAI,EAAE,CAAA;CAAE,CAAC,CAyDxF"}
1
+ {"version":3,"file":"get-graph-data.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/(dashboard)/get-graph-data.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAE5E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAI1C,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAwFD,MAAM,MAAM,aAAa,GACrB;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GACrB;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,WAAW,CAAA;CAAE,GACtC;IAAE,MAAM,EAAE,YAAY,CAAA;CAAE,CAAC;AA6B7B,wBAAsB,YAAY,IAAI,OAAO,CAAC;IAAE,KAAK,EAAE,cAAc,EAAE,CAAC;IAAC,KAAK,EAAE,IAAI,EAAE,CAAA;CAAE,CAAC,CA6DxF"}
@@ -6,6 +6,11 @@ import { getSettings } from '../../../../../packages/core/src/infrastructure/ser
6
6
  import { layoutWithDagre, CANVAS_LAYOUT_DEFAULTS } from '../../lib/layout-with-dagre.js';
7
7
  import { buildGraphNodes } from '../build-graph-nodes.js';
8
8
  const execFileAsync = promisify(execFileCb);
9
+ // Cache git info per repo — refreshed every GIT_INFO_TTL_MS via background-style check.
10
+ // getGraphData() always reads from cache (zero git calls), and only spawns git
11
+ // processes when the cache entry is older than the TTL.
12
+ const GIT_INFO_TTL_MS = 30_000;
13
+ const gitInfoCache = new Map();
9
14
  async function gitCommand(cwd, args) {
10
15
  try {
11
16
  const opts = IS_WINDOWS ? { cwd, windowsHide: true } : { cwd };
@@ -16,22 +21,38 @@ async function gitCommand(cwd, args) {
16
21
  return null;
17
22
  }
18
23
  }
19
- async function fetchRepoGitInfo(repo) {
24
+ const FIELD_SEP = '\x1f'; // ASCII Unit Separator — safe in git output and child_process
25
+ /** Refresh git info for a single repo (spawns git processes). */
26
+ async function refreshRepoGitInfo(repo) {
20
27
  try {
21
- const [currentBranch, commitMessage, committer] = await Promise.all([
28
+ const headSha = await gitCommand(repo.path, ['rev-parse', 'HEAD']);
29
+ if (!headSha) {
30
+ // Not a git repo (or bare/broken) — cache this so the UI can show it
31
+ gitInfoCache.set(repo.path, { kind: 'not-a-repo', checkedAt: Date.now() });
32
+ return;
33
+ }
34
+ const cached = gitInfoCache.get(repo.path);
35
+ if (cached?.kind === 'repo' && cached.sha === headSha) {
36
+ // HEAD unchanged — just bump checkedAt
37
+ cached.checkedAt = Date.now();
38
+ return;
39
+ }
40
+ // HEAD changed — fetch branch + subject & author in parallel
41
+ const [currentBranch, logLine] = await Promise.all([
22
42
  gitCommand(repo.path, ['symbolic-ref', '--short', 'HEAD']),
23
- gitCommand(repo.path, ['log', '-1', '--format=%s']),
24
- gitCommand(repo.path, ['log', '-1', '--format=%an']),
43
+ gitCommand(repo.path, ['log', '-1', `--format=%s${FIELD_SEP}%an`]),
25
44
  ]);
26
45
  if (!currentBranch)
27
- return null;
28
- // Try to determine the default branch to compute behind count
46
+ return;
47
+ const [commitMessage, committer] = (logLine ?? '').split(FIELD_SEP);
48
+ // Behind count
29
49
  let behindCount = null;
30
- const defaultBranch = await gitCommand(repo.path, [
50
+ const defaultBranchRef = await gitCommand(repo.path, [
31
51
  'symbolic-ref',
32
52
  '--short',
33
53
  'refs/remotes/origin/HEAD',
34
- ]).then((ref) => ref?.replace('origin/', '') ?? null);
54
+ ]);
55
+ const defaultBranch = defaultBranchRef?.replace('origin/', '') ?? null;
35
56
  if (defaultBranch && currentBranch !== defaultBranch) {
36
57
  const behind = await gitCommand(repo.path, [
37
58
  'rev-list',
@@ -42,32 +63,63 @@ async function fetchRepoGitInfo(repo) {
42
63
  if (isNaN(behindCount))
43
64
  behindCount = null;
44
65
  }
45
- else if (!defaultBranch || currentBranch === defaultBranch) {
66
+ else {
46
67
  behindCount = 0;
47
68
  }
48
- return {
49
- branch: currentBranch,
50
- commitMessage: commitMessage ?? 'unknown',
51
- committer: committer ?? 'unknown',
52
- behindCount,
53
- };
69
+ gitInfoCache.set(repo.path, {
70
+ kind: 'repo',
71
+ sha: headSha,
72
+ checkedAt: Date.now(),
73
+ data: {
74
+ branch: currentBranch,
75
+ commitMessage: commitMessage ?? 'unknown',
76
+ committer: committer ?? 'unknown',
77
+ behindCount,
78
+ },
79
+ });
54
80
  }
55
81
  catch {
56
- return null;
82
+ // ignore — stale cache is better than no data
57
83
  }
58
84
  }
85
+ /**
86
+ * Return cached git info for a repo. If the cache is stale (> TTL),
87
+ * kick off a fire-and-forget refresh so the NEXT call gets fresh data.
88
+ * Never blocks getGraphData() with git subprocess calls.
89
+ */
90
+ function getRepoGitInfo(repo) {
91
+ const cached = gitInfoCache.get(repo.path);
92
+ const now = Date.now();
93
+ if (!cached) {
94
+ // First time — trigger async refresh, return loading for this call
95
+ void refreshRepoGitInfo(repo);
96
+ return { status: 'loading' };
97
+ }
98
+ if (now - cached.checkedAt >= GIT_INFO_TTL_MS) {
99
+ // Stale — trigger async refresh, return current data immediately
100
+ void refreshRepoGitInfo(repo);
101
+ }
102
+ if (cached.kind === 'not-a-repo') {
103
+ return { status: 'not-a-repo' };
104
+ }
105
+ return { status: 'ready', data: cached.data };
106
+ }
59
107
  export async function getGraphData() {
60
108
  const listFeatures = resolve('ListFeaturesUseCase');
61
109
  const listRepos = resolve('ListRepositoriesUseCase');
62
110
  const agentRunRepo = resolve('IAgentRunRepository');
63
111
  const [features, repositories] = await Promise.all([listFeatures.execute(), listRepos.execute()]);
64
- // Fetch git info (branch, commit, behind count) for each repository
112
+ // Read git info from cache (zero git calls). Stale entries trigger
113
+ // a fire-and-forget background refresh for the next poll cycle.
65
114
  const repoGitInfoMap = new Map();
66
- await Promise.all(repositories.map(async (repo) => {
67
- const info = await fetchRepoGitInfo(repo);
68
- if (info)
69
- repoGitInfoMap.set(repo.path, info);
70
- }));
115
+ const repoGitStatusMap = new Map();
116
+ for (const repo of repositories) {
117
+ const result = getRepoGitInfo(repo);
118
+ repoGitStatusMap.set(repo.path, result.status);
119
+ if (result.status === 'ready') {
120
+ repoGitInfoMap.set(repo.path, result.data);
121
+ }
122
+ }
71
123
  // PR/CI status is kept fresh by PrSyncWatcher (30s background poll).
72
124
  // No live GitHub calls here — use cached DB values for fast response.
73
125
  const featuresWithRuns = await Promise.all(features.map(async (feature) => {
@@ -80,6 +132,7 @@ export async function getGraphData() {
80
132
  commitEvidence: workflow.commitEvidence,
81
133
  ciWatchEnabled: workflow.ciWatchEnabled,
82
134
  repoGitInfo: repoGitInfoMap,
135
+ repoGitStatus: repoGitStatusMap,
83
136
  });
84
137
  // Enrich feature nodes with deployment status
85
138
  let deploymentService = null;
@@ -21,7 +21,7 @@ const AGENT_TOOL_MAP = {
21
21
  };
22
22
  const AGENT_BINARY_MAP = {
23
23
  'claude-code': 'claude',
24
- cursor: 'cursor-agent',
24
+ cursor: 'agent',
25
25
  'gemini-cli': 'gemini',
26
26
  };
27
27
  /**
@@ -27,6 +27,8 @@ export interface BuildGraphNodesOptions {
27
27
  committer: string;
28
28
  behindCount: number | null;
29
29
  }>;
30
+ /** Git info resolution status keyed by repository path */
31
+ repoGitStatus?: Map<string, 'loading' | 'ready' | 'not-a-repo'>;
30
32
  }
31
33
  export declare function buildGraphNodes(repositories: Repository[], featuresWithRuns: FeatureWithRun[], options?: BuildGraphNodesOptions): {
32
34
  nodes: CanvasNodeType[];
@@ -1 +1 @@
1
- {"version":3,"file":"build-graph-nodes.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/app/build-graph-nodes.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AAS1F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAU1C,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,QAAQ,GAAG,IAAI,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,sBAAsB;IACrC,uEAAuE;IACvE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,6EAA6E;IAC7E,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,qEAAqE;IACrE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,0FAA0F;IAC1F,WAAW,CAAC,EAAE,GAAG,CACf,MAAM,EACN;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CACzF,CAAC;CACH;AAED,wBAAgB,eAAe,CAC7B,YAAY,EAAE,UAAU,EAAE,EAC1B,gBAAgB,EAAE,cAAc,EAAE,EAClC,OAAO,CAAC,EAAE,sBAAsB,GAC/B;IAAE,KAAK,EAAE,cAAc,EAAE,CAAC;IAAC,KAAK,EAAE,IAAI,EAAE,CAAA;CAAE,CAkG5C"}
1
+ {"version":3,"file":"build-graph-nodes.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/app/build-graph-nodes.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AAS1F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAU1C,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,QAAQ,GAAG,IAAI,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,sBAAsB;IACrC,uEAAuE;IACvE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,6EAA6E;IAC7E,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,qEAAqE;IACrE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,0FAA0F;IAC1F,WAAW,CAAC,EAAE,GAAG,CACf,MAAM,EACN;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CACzF,CAAC;IACF,0DAA0D;IAC1D,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,GAAG,YAAY,CAAC,CAAC;CACjE;AAED,wBAAgB,eAAe,CAC7B,YAAY,EAAE,UAAU,EAAE,EAC1B,gBAAgB,EAAE,cAAc,EAAE,EAClC,OAAO,CAAC,EAAE,sBAAsB,GAC/B;IAAE,KAAK,EAAE,cAAc,EAAE,CAAC;IAAC,KAAK,EAAE,IAAI,EAAE,CAAA;CAAE,CAoG5C"}
@@ -32,6 +32,7 @@ export function buildGraphNodes(repositories, featuresWithRuns, options) {
32
32
  coveredPaths.add(normalizedRepoPath);
33
33
  const repoNodeId = `repo-${repo.id}`;
34
34
  const gitInfo = options?.repoGitInfo?.get(repo.path);
35
+ const gitInfoStatus = options?.repoGitStatus?.get(repo.path) ?? 'loading';
35
36
  nodes.push({
36
37
  id: repoNodeId,
37
38
  type: 'repositoryNode',
@@ -41,6 +42,7 @@ export function buildGraphNodes(repositories, featuresWithRuns, options) {
41
42
  repositoryPath: normalizedRepoPath,
42
43
  id: repo.id,
43
44
  createdAt: repo.createdAt instanceof Date ? repo.createdAt.getTime() : Number(repo.createdAt),
45
+ gitInfoStatus,
44
46
  ...(gitInfo && {
45
47
  branch: gitInfo.branch,
46
48
  commitMessage: gitInfo.commitMessage,
@@ -1 +1 @@
1
- {"version":3,"file":"feature-node.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AA6DnE,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,2CAkWA"}
1
+ {"version":3,"file":"feature-node.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AA6DnE,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,2CAqWA"}
@@ -70,7 +70,7 @@ export function FeatureNode({ data, selected, }) {
70
70
  const config = featureNodeStateConfig[data.state];
71
71
  const Icon = config.icon;
72
72
  const [confirmOpen, setConfirmOpen] = useState(false);
73
- return (_jsxs("div", { className: "animate-in fade-in group relative duration-300", children: [data.showHandles ? (_jsx(Handle, { type: "target", position: Position.Left, isConnectable: false, className: "opacity-0!" })) : null, data.onDelete && data.featureId && data.state !== 'deleting' ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "absolute top-1/2 -left-10 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100", onPointerDown: (e) => e.stopPropagation(), children: _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { "aria-label": "Delete feature", "data-testid": "feature-node-delete-button", onClick: (e) => {
73
+ return (_jsxs("div", { className: "animate-in fade-in group relative duration-300", children: [data.showHandles ? (_jsx(Handle, { type: "target", position: Position.Left, isConnectable: false, className: "opacity-0!", style: { top: 70 } })) : null, data.onDelete && data.featureId && data.state !== 'deleting' ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "absolute top-1/2 -left-10 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100", onPointerDown: (e) => e.stopPropagation(), children: _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { "aria-label": "Delete feature", "data-testid": "feature-node-delete-button", onClick: (e) => {
74
74
  e.stopPropagation();
75
75
  setConfirmOpen(true);
76
76
  }, className: "bg-card text-muted-foreground hover:border-destructive hover:text-destructive flex h-7 w-7 cursor-pointer items-center justify-center rounded-full border shadow-sm transition-colors", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }) }), _jsx(TooltipContent, { children: "Delete feature" })] }) }) }), _jsx(DeleteFeatureDialog, { open: confirmOpen, onOpenChange: setConfirmOpen, onConfirm: (cleanup, cascadeDelete, closePr) => {
@@ -109,8 +109,8 @@ export function FeatureNode({ data, selected, }) {
109
109
  }, className: "nodrag flex shrink-0 cursor-pointer items-center gap-1 rounded-full bg-red-100 px-2.5 py-1 text-[11px] font-medium text-red-700 transition-colors hover:bg-red-200", children: [_jsx(RotateCcw, { className: "h-3 w-3" }), "Retry"] })) : null, data.state === 'pending' && data.onStart ? (_jsxs("button", { type: "button", "aria-label": "Start feature", "data-testid": "feature-node-start-button", onClick: (e) => {
110
110
  e.stopPropagation();
111
111
  data.onStart(data.featureId);
112
- }, className: "nodrag flex shrink-0 cursor-pointer items-center gap-1 rounded-full bg-slate-100 px-2.5 py-1 text-[11px] font-medium text-slate-700 transition-colors hover:bg-slate-200", children: [_jsx(Play, { className: "h-3 w-3" }), "Start"] })) : null] })] })) })] }), data.onAction && data.state !== 'deleting' ? (_jsx(Handle, { type: "source", position: Position.Right, className: "h-0! w-0! border-0! bg-transparent!", children: _jsx("button", { type: "button", "aria-label": "Add", "data-testid": "feature-node-action-button", onClick: (e) => {
112
+ }, className: "nodrag flex shrink-0 cursor-pointer items-center gap-1 rounded-full bg-slate-100 px-2.5 py-1 text-[11px] font-medium text-slate-700 transition-colors hover:bg-slate-200", children: [_jsx(Play, { className: "h-3 w-3" }), "Start"] })) : null] })] })) })] }), data.onAction && data.state !== 'deleting' ? (_jsx(Handle, { type: "source", position: Position.Right, className: "h-0! w-0! border-0! bg-transparent!", style: { top: 70 }, children: _jsx("button", { type: "button", "aria-label": "Add", "data-testid": "feature-node-action-button", onClick: (e) => {
113
113
  e.stopPropagation();
114
114
  data.onAction?.();
115
- }, className: "nodrag absolute top-1/2 left-1/2 flex h-6 w-6 -translate-x-1/2 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full bg-blue-500 text-white opacity-0 shadow-md transition-opacity group-hover:opacity-100 hover:bg-blue-600", children: _jsx(Plus, { className: "h-3.5 w-3.5" }) }) })) : data.showHandles ? (_jsx(Handle, { type: "source", position: Position.Right, isConnectable: false, className: "opacity-0!" })) : null] }));
115
+ }, className: "nodrag absolute top-1/2 left-1/2 flex h-6 w-6 -translate-x-1/2 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full bg-blue-500 text-white opacity-0 shadow-md transition-opacity group-hover:opacity-100 hover:bg-blue-600", children: _jsx(Plus, { className: "h-3.5 w-3.5" }) }) })) : data.showHandles ? (_jsx(Handle, { type: "source", position: Position.Right, isConnectable: false, className: "opacity-0!", style: { top: 70 } })) : null] }));
116
116
  }
@@ -1 +1 @@
1
- {"version":3,"file":"prd-questionnaire.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAExE,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,UAAU,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,YAAoB,EACpB,WAAmB,EACnB,UAAkB,EAClB,SAAS,EACT,iBAAiB,GAClB,EAAE,qBAAqB,kDAkLvB"}
1
+ {"version":3,"file":"prd-questionnaire.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAExE,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,UAAU,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,YAAoB,EACpB,WAAmB,EACnB,UAAkB,EAClB,SAAS,EACT,iBAAiB,GAClB,EAAE,qBAAqB,kDAuLvB"}
@@ -33,7 +33,7 @@ export function PrdQuestionnaire({ data, selections, onSelect, onApprove, onReje
33
33
  } }, q.id))) })] }), _jsx("div", { className: "space-y-2", children: currentQuestion.options.map((opt, optIdx) => {
34
34
  const selected = selections[currentQuestion.id] === opt.id;
35
35
  const letter = String.fromCharCode(65 + optIdx);
36
- return (_jsx("button", { type: "button", className: cn('border-border w-full rounded-md border px-3 py-3 text-left text-xs transition-all', 'hover:border-primary/70 hover:bg-primary/5 group', selected && 'border-primary bg-primary/5', opt.isNew && 'animate-option-highlight'), disabled: isProcessing, onClick: () => handleSelect(currentQuestion.id, opt.id), children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsxs("span", { className: "text-muted-foreground mt-0.5 font-mono text-xs", children: [letter, "."] }), _jsxs("div", { className: "flex-1", children: [_jsxs("div", { className: "mb-0.5 flex items-center gap-2", children: [_jsx("span", { className: "text-foreground text-xs font-semibold", children: opt.label }), opt.recommended ? (_jsx(Badge, { className: "px-1.5 py-0 text-[10px]", children: "AI Recommended" })) : null, opt.isNew ? (_jsx(Badge, { className: "border-transparent bg-emerald-600 px-1.5 py-0 text-[10px] text-white hover:bg-emerald-600/80", children: "New" })) : null] }), _jsx("div", { className: "text-muted-foreground text-xs leading-snug", children: opt.rationale })] })] }) }, opt.id));
36
+ return (_jsx("button", { type: "button", className: cn('border-border w-full overflow-hidden rounded-md border px-3 py-3 text-left text-xs transition-all', 'hover:border-primary/70 hover:bg-primary/5 group', selected && 'border-primary bg-primary/5', opt.isNew && 'animate-option-highlight'), disabled: isProcessing, onClick: () => handleSelect(currentQuestion.id, opt.id), children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsxs("span", { className: "text-muted-foreground mt-0.5 font-mono text-xs", children: [letter, "."] }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-foreground mb-0.5 text-xs font-semibold wrap-break-word", children: opt.label }), _jsx("div", { className: "text-muted-foreground text-xs leading-snug", children: opt.rationale })] }), opt.recommended || opt.isNew ? (_jsx("div", { className: "shrink-0 pt-0.5", children: opt.recommended ? (_jsx(Badge, { className: "px-1.5 py-0 text-[10px] whitespace-nowrap", children: "AI Recommended" })) : (_jsx(Badge, { className: "border-transparent bg-emerald-600 px-1.5 py-0 text-[10px] whitespace-nowrap text-white hover:bg-emerald-600/80", children: "New" })) })) : null] }) }, opt.id));
37
37
  }) })] }), _jsxs("div", { className: "flex items-center justify-between pt-2", children: [_jsxs(Button, { type: "button", variant: "ghost", size: "sm", disabled: isFirstStep || isProcessing, onClick: () => {
38
38
  navigateSound.play();
39
39
  setCurrentStep((s) => s - 1);
@@ -17,4 +17,10 @@ export declare const MinimalData: Story;
17
17
  export declare const WithRejectButton: Story;
18
18
  /** Rejecting state — reject button disabled with spinner while reject action is in flight. */
19
19
  export declare const RejectingState: Story;
20
+ /** Real-world LLM provider question — reproduces exact label text from the reported bug. Badge must stay on one line. */
21
+ export declare const LlmProviderQuestion: Story;
22
+ /** Pathological overflow — label with no natural word break (repeating token). Verifies button does not overflow panel. */
23
+ export declare const RepeatingLabelOverflow: Story;
24
+ /** Long labels — verifies that AI Recommended badge stays on one line even with very long option labels. Uses 640px width to match real panel width. */
25
+ export declare const LongLabels: Story;
20
26
  //# sourceMappingURL=prd-questionnaire.stories.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prd-questionnaire.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAiKvD,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,gBAAgB,CAkBvC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAU/C,sJAAsJ;AACtJ,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,2EAA2E;AAC3E,eAAO,MAAM,cAAc,EAAE,KAW5B,CAAC;AAEF,uEAAuE;AACvE,eAAO,MAAM,WAAW,EAAE,KAczB,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,QAAQ,EAAE,KAStB,CAAC;AAEF,uDAAuD;AACvD,eAAO,MAAM,WAAW,EAAE,KA8BzB,CAAC;AAaF,4DAA4D;AAC5D,eAAO,MAAM,gBAAgB,EAAE,KAQ9B,CAAC;AAEF,8FAA8F;AAC9F,eAAO,MAAM,cAAc,EAAE,KAO5B,CAAC"}
1
+ {"version":3,"file":"prd-questionnaire.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAiKvD,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,gBAAgB,CAkBvC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAU/C,sJAAsJ;AACtJ,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,2EAA2E;AAC3E,eAAO,MAAM,cAAc,EAAE,KAW5B,CAAC;AAEF,uEAAuE;AACvE,eAAO,MAAM,WAAW,EAAE,KAczB,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,QAAQ,EAAE,KAStB,CAAC;AAEF,uDAAuD;AACvD,eAAO,MAAM,WAAW,EAAE,KA8BzB,CAAC;AAaF,4DAA4D;AAC5D,eAAO,MAAM,gBAAgB,EAAE,KAQ9B,CAAC;AAEF,8FAA8F;AAC9F,eAAO,MAAM,cAAc,EAAE,KAO5B,CAAC;AAEF,yHAAyH;AACzH,eAAO,MAAM,mBAAmB,EAAE,KAuCjC,CAAC;AAEF,2HAA2H;AAC3H,eAAO,MAAM,sBAAsB,EAAE,KAiCpC,CAAC;AAEF,wJAAwJ;AACxJ,eAAO,MAAM,UAAU,EAAE,KAyCxB,CAAC"}
@@ -154,7 +154,7 @@ const meta = {
154
154
  onApprove: { action: 'onApprove' },
155
155
  },
156
156
  decorators: [
157
- (Story) => (_jsx("div", { style: { height: '600px', width: '400px', border: '1px solid var(--color-border)' }, children: _jsx(Story, {}) })),
157
+ (Story) => (_jsx("div", { style: { height: '600px', width: '760px', border: '1px solid var(--color-border)' }, children: _jsx(Story, {}) })),
158
158
  ],
159
159
  };
160
160
  export default meta;
@@ -248,3 +248,98 @@ export const RejectingState = {
248
248
  isRejecting: true,
249
249
  },
250
250
  };
251
+ /** Real-world LLM provider question — reproduces exact label text from the reported bug. Badge must stay on one line. */
252
+ export const LlmProviderQuestion = {
253
+ render: () => (_jsx(InteractiveQuestionnaire, { data: {
254
+ question: 'Review Feature Requirements',
255
+ context: 'Testing badge nowrap with real-world LLM provider labels.',
256
+ questions: [
257
+ {
258
+ id: 'llm-provider',
259
+ question: 'Which LLM provider should be the primary AI engine?',
260
+ type: 'select',
261
+ options: [
262
+ {
263
+ id: 'claude-primary',
264
+ label: 'Claude (Anthropic) as primary for all AI tasks including vision/OCR, OpenAI as fallback LLM',
265
+ rationale: 'Claude is used as the primary engine for all AI tasks: structured reasoning, signal interpretation, opportunity scoring, AND vision/OCR for flyer image extraction.',
266
+ recommended: true,
267
+ },
268
+ {
269
+ id: 'split',
270
+ label: 'Claude (Anthropic) for reasoning, OpenAI Vision for OCR',
271
+ rationale: 'Split responsibilities: Claude for text reasoning tasks, OpenAI Vision API specifically for OCR/image analysis.',
272
+ },
273
+ {
274
+ id: 'single',
275
+ label: 'Single provider only (Claude OR OpenAI for everything)',
276
+ rationale: 'Simplifies implementation — one SDK, one billing relationship, one prompt format.',
277
+ },
278
+ ],
279
+ },
280
+ ],
281
+ finalAction: mockFinalAction,
282
+ } })),
283
+ };
284
+ /** Pathological overflow — label with no natural word break (repeating token). Verifies button does not overflow panel. */
285
+ export const RepeatingLabelOverflow = {
286
+ render: () => (_jsx(InteractiveQuestionnaire, { data: {
287
+ question: 'Where should the dark-mode preference be persisted?',
288
+ context: 'Testing overflow containment with no-break label text.',
289
+ questions: [
290
+ {
291
+ id: 'storage',
292
+ question: 'Where should the dark-mode preference be persisted?',
293
+ type: 'select',
294
+ options: [
295
+ {
296
+ id: 'localstorage',
297
+ label: 'localStorage only localStorage only localStorage only localStorage only localStorage only localStorage only',
298
+ rationale: 'Store the theme preference in localStorage. Zero backend changes, instant read on page load, and no need for API round-trips.',
299
+ recommended: true,
300
+ },
301
+ {
302
+ id: 'sqlite',
303
+ label: 'SQLite settings via backend API',
304
+ rationale: 'Persist the preference in the Shep SQLite settings store alongside other settings. Syncs across devices but requires API changes and a settings migration — out of scope for this feature.',
305
+ },
306
+ ],
307
+ },
308
+ ],
309
+ finalAction: mockFinalAction,
310
+ } })),
311
+ };
312
+ /** Long labels — verifies that AI Recommended badge stays on one line even with very long option labels. Uses 640px width to match real panel width. */
313
+ export const LongLabels = {
314
+ render: () => (_jsx(InteractiveQuestionnaire, { data: {
315
+ question: 'Review Feature Requirements',
316
+ context: 'Testing badge nowrap behavior with long option labels.',
317
+ questions: [
318
+ {
319
+ id: 'long-label',
320
+ question: 'Which deployment strategy should we use for the distributed microservices architecture?',
321
+ type: 'select',
322
+ options: [
323
+ {
324
+ id: 'opt-long-1',
325
+ label: 'Blue-Green Deployment with Automated Canary Analysis and Progressive Rollout',
326
+ rationale: 'Minimizes downtime and risk by running two identical production environments',
327
+ recommended: true,
328
+ },
329
+ {
330
+ id: 'opt-long-2',
331
+ label: 'Rolling Update with Health Check Verification and Automatic Rollback on Failure',
332
+ rationale: 'Gradually replaces instances with zero-downtime deployment',
333
+ },
334
+ {
335
+ id: 'opt-long-3',
336
+ label: 'Feature Flags with Percentage-Based Gradual Rollout to Target User Segments',
337
+ rationale: 'Enables targeted rollout and instant rollback via configuration',
338
+ isNew: true,
339
+ },
340
+ ],
341
+ },
342
+ ],
343
+ finalAction: mockFinalAction,
344
+ } })),
345
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"product-decisions-summary.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/product-decisions-summary/product-decisions-summary.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,4BAA4B,EAE7B,MAAM,oCAAoC,CAAC;AAyB5C,wBAAgB,uBAAuB,CAAC,EAAE,IAAI,EAAE,EAAE,4BAA4B,kDAmB7E"}
1
+ {"version":3,"file":"product-decisions-summary.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/product-decisions-summary/product-decisions-summary.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,4BAA4B,EAE7B,MAAM,oCAAoC,CAAC;AA6B5C,wBAAgB,uBAAuB,CAAC,EAAE,IAAI,EAAE,EAAE,4BAA4B,kDAmB7E"}
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { ClipboardList } from 'lucide-react';
4
4
  import { Badge } from '../../ui/badge.js';
5
5
  function ProductDecisionCard({ item, index }) {
6
- return (_jsx("div", { className: "border-border rounded-lg border", children: _jsx("div", { className: "space-y-2 px-4 py-3", children: _jsxs("div", { className: "flex items-start gap-2.5", children: [_jsx("span", { className: "bg-primary text-primary-foreground flex h-6 w-6 shrink-0 items-center justify-center rounded-full text-xs font-bold", children: index + 1 }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("h3", { className: "text-foreground text-sm leading-tight font-semibold", children: item.question }), _jsxs("div", { className: "mt-1 flex items-center gap-2", children: [_jsx("p", { className: "text-muted-foreground text-xs", children: item.selectedOption }), item.wasRecommended ? (_jsx(Badge, { className: "px-1.5 py-0 text-[10px]", children: "AI Recommended" })) : null] })] })] }) }) }));
6
+ return (_jsx("div", { className: "border-border rounded-lg border", children: _jsx("div", { className: "space-y-2 px-4 py-3", children: _jsxs("div", { className: "flex items-start gap-2.5", children: [_jsx("span", { className: "bg-primary text-primary-foreground flex h-6 w-6 shrink-0 items-center justify-center rounded-full text-xs font-bold", children: index + 1 }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("h3", { className: "text-foreground text-sm leading-tight font-semibold", children: item.question }), _jsxs("div", { className: "mt-1 flex items-center gap-2", children: [_jsx("p", { className: "text-muted-foreground min-w-0 truncate text-xs", children: item.selectedOption }), item.wasRecommended ? (_jsx(Badge, { className: "shrink-0 px-1.5 py-0 text-[10px] whitespace-nowrap", children: "AI Recommended" })) : null] })] })] }) }) }));
7
7
  }
8
8
  export function ProductDecisionsSummary({ data }) {
9
9
  const { questions } = data;