@shepai/cli 1.91.1 → 1.91.2-pr271.a8e0a3b

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 (136) hide show
  1. package/dist/src/presentation/web/app/actions/get-feature-phase-timings.d.ts +1 -0
  2. package/dist/src/presentation/web/app/actions/get-feature-phase-timings.d.ts.map +1 -1
  3. package/dist/src/presentation/web/app/actions/get-feature-phase-timings.js +1 -0
  4. package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.d.ts +29 -0
  5. package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.d.ts.map +1 -1
  6. package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.js +79 -37
  7. package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.stories.d.ts +13 -0
  8. package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.stories.d.ts.map +1 -1
  9. package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.stories.js +213 -0
  10. package/dist/src/presentation/web/components/common/feature-node/feature-node.js +1 -1
  11. package/dist/src/presentation/web/components/common/repository-node/repository-node.js +1 -1
  12. package/dist/tsconfig.build.tsbuildinfo +1 -1
  13. package/package.json +1 -1
  14. package/web/.next/BUILD_ID +1 -1
  15. package/web/.next/build-manifest.json +2 -2
  16. package/web/.next/fallback-build-manifest.json +2 -2
  17. package/web/.next/prerender-manifest.json +3 -3
  18. package/web/.next/required-server-files.js +1 -1
  19. package/web/.next/required-server-files.json +1 -1
  20. package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +18 -18
  21. package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  22. package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  23. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page/server-reference-manifest.json +21 -21
  24. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  25. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  26. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +21 -21
  27. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  28. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  29. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +14 -14
  30. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  31. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  32. package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +18 -18
  33. package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  34. package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  35. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page/server-reference-manifest.json +21 -21
  36. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  37. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  38. package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +21 -21
  39. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  40. package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  41. package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +14 -14
  42. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  43. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  44. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +14 -14
  45. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  46. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  47. package/web/.next/server/app/_global-error.html +2 -2
  48. package/web/.next/server/app/_global-error.rsc +1 -1
  49. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  50. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  51. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  52. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  53. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  54. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  55. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  56. package/web/.next/server/app/settings/page/server-reference-manifest.json +5 -5
  57. package/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  58. package/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
  59. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  60. package/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
  61. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  62. package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  63. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  64. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
  65. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
  66. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_da7eef33.js +1 -1
  67. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_da7eef33.js.map +1 -1
  68. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_0a9251aa.js +1 -1
  69. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_0a9251aa.js.map +1 -1
  70. package/web/.next/server/chunks/ssr/[root-of-the-server]__11033878._.js +1 -1
  71. package/web/.next/server/chunks/ssr/[root-of-the-server]__11033878._.js.map +1 -1
  72. package/web/.next/server/chunks/ssr/[root-of-the-server]__17143953._.js +1 -1
  73. package/web/.next/server/chunks/ssr/[root-of-the-server]__17143953._.js.map +1 -1
  74. package/web/.next/server/chunks/ssr/[root-of-the-server]__357d99f9._.js +1 -1
  75. package/web/.next/server/chunks/ssr/[root-of-the-server]__45aad79d._.js +1 -1
  76. package/web/.next/server/chunks/ssr/[root-of-the-server]__45aad79d._.js.map +1 -1
  77. package/web/.next/server/chunks/ssr/[root-of-the-server]__4be9a9bb._.js +1 -1
  78. package/web/.next/server/chunks/ssr/[root-of-the-server]__5cc4752a._.js +1 -1
  79. package/web/.next/server/chunks/ssr/[root-of-the-server]__5cc4752a._.js.map +1 -1
  80. package/web/.next/server/chunks/ssr/[root-of-the-server]__5d87af2e._.js +1 -1
  81. package/web/.next/server/chunks/ssr/[root-of-the-server]__89e8a5cc._.js +2 -2
  82. package/web/.next/server/chunks/ssr/[root-of-the-server]__9de266fa._.js +1 -1
  83. package/web/.next/server/chunks/ssr/[root-of-the-server]__9de266fa._.js.map +1 -1
  84. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +1 -1
  85. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js.map +1 -1
  86. package/web/.next/server/chunks/ssr/[root-of-the-server]__e630cc47._.js +1 -1
  87. package/web/.next/server/chunks/ssr/[root-of-the-server]__e630cc47._.js.map +1 -1
  88. package/web/.next/server/chunks/ssr/[root-of-the-server]__fdc75809._.js +1 -1
  89. package/web/.next/server/chunks/ssr/[root-of-the-server]__fdc75809._.js.map +1 -1
  90. package/web/.next/server/chunks/ssr/_135edb9c._.js +1 -1
  91. package/web/.next/server/chunks/ssr/_135edb9c._.js.map +1 -1
  92. package/web/.next/server/chunks/ssr/_21c75883._.js +1 -1
  93. package/web/.next/server/chunks/ssr/_21c75883._.js.map +1 -1
  94. package/web/.next/server/chunks/ssr/{_7399abd4._.js → _33abfe62._.js} +2 -2
  95. package/web/.next/server/chunks/ssr/{_7399abd4._.js.map → _33abfe62._.js.map} +1 -1
  96. package/web/.next/server/chunks/ssr/_6d26a08f._.js +1 -1
  97. package/web/.next/server/chunks/ssr/_8dacd1e7._.js +1 -1
  98. package/web/.next/server/chunks/ssr/_8dacd1e7._.js.map +1 -1
  99. package/web/.next/server/chunks/ssr/_cc77a143._.js +1 -1
  100. package/web/.next/server/chunks/ssr/_cc77a143._.js.map +1 -1
  101. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
  102. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js.map +1 -1
  103. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_021a05f2.js +1 -1
  104. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_021a05f2.js.map +1 -1
  105. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js +1 -1
  106. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js.map +1 -1
  107. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js +1 -1
  108. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js.map +1 -1
  109. package/web/.next/server/chunks/ssr/src_presentation_web_c4348b8a._.js +2 -2
  110. package/web/.next/server/chunks/ssr/src_presentation_web_c4348b8a._.js.map +1 -1
  111. package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js +1 -1
  112. package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js.map +1 -1
  113. package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.js +1 -1
  114. package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.js.map +1 -1
  115. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  116. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
  117. package/web/.next/server/chunks/ssr/src_presentation_web_e4510d10._.js +2 -2
  118. package/web/.next/server/chunks/ssr/src_presentation_web_e4510d10._.js.map +1 -1
  119. package/web/.next/server/pages/500.html +2 -2
  120. package/web/.next/server/server-reference-manifest.js +1 -1
  121. package/web/.next/server/server-reference-manifest.json +28 -28
  122. package/web/.next/static/chunks/{20f30bbb00f25104.js → 09144b0b13be0b40.js} +1 -1
  123. package/web/.next/static/chunks/{c860f17b796cef6e.js → 1dedaadce86fde03.js} +1 -1
  124. package/web/.next/static/chunks/{c2a2b922a7600c25.js → 318a0e046b0f95f5.js} +1 -1
  125. package/web/.next/static/chunks/{6fdf128512e1a1d0.js → 4e57abbfab95085f.js} +1 -1
  126. package/web/.next/static/chunks/{94479ee90d31f787.js → 5f3a20f1611d5ea3.js} +1 -1
  127. package/web/.next/static/chunks/{ebb1dc33f54e6dcb.js → a65724da790fb232.js} +1 -1
  128. package/web/.next/static/chunks/{0da86d7f410075ae.js → aeaaf802ea1f5182.js} +1 -1
  129. package/web/.next/static/chunks/c54c75d5aa78cdfb.css +2 -0
  130. package/web/.next/static/chunks/{35417237d41c9071.js → c709e93f88d99d99.js} +2 -2
  131. package/web/.next/static/chunks/{b1a0ac8408169e01.js → f793a67775dbba14.js} +1 -1
  132. package/web/.next/static/chunks/{00b44eadfb6e812e.js → fd9756b6b7c2ae9d.js} +1 -1
  133. package/web/.next/static/chunks/2a09571d38368a18.css +0 -2
  134. /package/web/.next/static/{NrUtZm2SMl-XP01PvR2Y_ → 0FO2SUJynXKUH2bAn1Chm}/_buildManifest.js +0 -0
  135. /package/web/.next/static/{NrUtZm2SMl-XP01PvR2Y_ → 0FO2SUJynXKUH2bAn1Chm}/_clientMiddlewareManifest.json +0 -0
  136. /package/web/.next/static/{NrUtZm2SMl-XP01PvR2Y_ → 0FO2SUJynXKUH2bAn1Chm}/_ssgManifest.js +0 -0
@@ -11,6 +11,7 @@ export interface RejectionFeedbackData {
11
11
  iteration: number;
12
12
  message: string;
13
13
  phase?: string;
14
+ timestamp?: string;
14
15
  attachments?: string[];
15
16
  }
16
17
  type GetPhaseTimingsResult = {
@@ -1 +1 @@
1
- {"version":3,"file":"get-feature-phase-timings.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/actions/get-feature-phase-timings.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,KAAK,qBAAqB,GACtB;IAAE,OAAO,EAAE,eAAe,EAAE,CAAC;IAAC,iBAAiB,EAAE,qBAAqB,EAAE,CAAA;CAAE,GAC1E;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtB,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA2B9F"}
1
+ {"version":3,"file":"get-feature-phase-timings.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/actions/get-feature-phase-timings.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,KAAK,qBAAqB,GACtB;IAAE,OAAO,EAAE,eAAe,EAAE,CAAC;IAAC,iBAAiB,EAAE,qBAAqB,EAAE,CAAA;CAAE,GAC1E;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtB,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA2B9F"}
@@ -42,6 +42,7 @@ async function readRejectionFeedback(featureId) {
42
42
  iteration: Number(entry.iteration ?? 1),
43
43
  message: String(entry.message ?? ''),
44
44
  phase: entry.phase ? String(entry.phase) : undefined,
45
+ timestamp: entry.timestamp ? String(entry.timestamp) : undefined,
45
46
  attachments: Array.isArray(entry.attachments)
46
47
  ? entry.attachments.map(String)
47
48
  : undefined,
@@ -5,5 +5,34 @@ export interface ActivityTabProps {
5
5
  error: string | null;
6
6
  rejectionFeedback?: RejectionFeedbackData[];
7
7
  }
8
+ /**
9
+ * A single iteration in the lifecycle timeline.
10
+ *
11
+ * An iteration represents one cycle of work:
12
+ * started/resumed → phase work → rejected/completed/failed
13
+ *
14
+ * Each rejected iteration has associated rejection feedback.
15
+ */
16
+ export interface TimelineIteration {
17
+ /** 1-based iteration number */
18
+ number: number;
19
+ /** The timing events in this iteration */
20
+ timings: PhaseTimingData[];
21
+ /** If this iteration ended with rejection, the feedback message */
22
+ rejectionMessage?: string;
23
+ /** If this iteration ended with rejection, any attachments */
24
+ rejectionAttachments?: string[];
25
+ }
26
+ /**
27
+ * Build a lifecycle-aware timeline from flat timing events and rejection feedback.
28
+ *
29
+ * Splits the timing stream at run:rejected boundaries to produce iterations.
30
+ * Each rejection feedback entry is matched to its iteration by index.
31
+ *
32
+ * When there are more feedback entries than run:rejected events in the timings,
33
+ * synthetic run:rejected + run:resumed events are appended to ensure every
34
+ * feedback entry is represented.
35
+ */
36
+ export declare function buildLifecycleTimeline(timings: PhaseTimingData[], feedback?: RejectionFeedbackData[]): TimelineIteration[];
8
37
  export declare function ActivityTab({ timings, loading, error, rejectionFeedback }: ActivityTabProps): import("react/jsx-runtime").JSX.Element;
9
38
  //# sourceMappingURL=activity-tab.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"activity-tab.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/activity-tab.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,eAAe,EACf,qBAAqB,EACtB,MAAM,yCAAyC,CAAC;AAGjD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,eAAe,EAAE,GAAG,IAAI,CAAC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,iBAAiB,CAAC,EAAE,qBAAqB,EAAE,CAAC;CAC7C;AA4ED,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,gBAAgB,2CA8D3F"}
1
+ {"version":3,"file":"activity-tab.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/activity-tab.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,eAAe,EACf,qBAAqB,EACtB,MAAM,yCAAyC,CAAC;AAGjD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,eAAe,EAAE,GAAG,IAAI,CAAC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,iBAAiB,CAAC,EAAE,qBAAqB,EAAE,CAAC;CAC7C;AAiCD;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,mEAAmE;IACnE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,8DAA8D;IAC9D,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,eAAe,EAAE,EAC1B,QAAQ,CAAC,EAAE,qBAAqB,EAAE,GACjC,iBAAiB,EAAE,CAgErB;AAMD,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,gBAAgB,2CAyD3F"}
@@ -25,38 +25,76 @@ function isLifecycleEvent(phase) {
25
25
  function formatDuration(ms) {
26
26
  return `${(ms / 1000).toFixed(1)}s`;
27
27
  }
28
- function groupTimingsByRun(timings) {
29
- const groups = [];
30
- for (const t of timings) {
31
- const last = groups[groups.length - 1];
32
- if (last?.runId === t.agentRunId) {
33
- last.timings.push(t);
34
- }
35
- else {
36
- groups.push({ runId: t.agentRunId, timings: [t] });
37
- }
38
- }
39
- return groups;
40
- }
41
28
  /**
42
- * Map each run:rejected timing entry to its rejection feedback message and attachments.
43
- * Matches by sequential order: 1st rejected event → 1st feedback entry, etc.
29
+ * Build a lifecycle-aware timeline from flat timing events and rejection feedback.
30
+ *
31
+ * Splits the timing stream at run:rejected boundaries to produce iterations.
32
+ * Each rejection feedback entry is matched to its iteration by index.
33
+ *
34
+ * When there are more feedback entries than run:rejected events in the timings,
35
+ * synthetic run:rejected + run:resumed events are appended to ensure every
36
+ * feedback entry is represented.
44
37
  */
45
- function buildRejectionMessageMap(timings, feedback) {
46
- const map = new Map();
47
- if (!feedback?.length)
48
- return map;
49
- let rejectionIdx = 0;
50
- for (const t of timings) {
51
- if (t.phase === 'run:rejected' && rejectionIdx < feedback.length) {
52
- const key = `${t.agentRunId}-${t.phase}-${t.startedAt}`;
53
- const entry = feedback[rejectionIdx];
54
- map.set(key, { message: entry.message, attachments: entry.attachments });
55
- rejectionIdx++;
38
+ export function buildLifecycleTimeline(timings, feedback) {
39
+ if (!timings.length)
40
+ return [];
41
+ // Count existing run:rejected events
42
+ const existingRejectionCount = timings.filter((t) => t.phase === 'run:rejected').length;
43
+ const feedbackCount = feedback?.length ?? 0;
44
+ // Augment timings with synthetic rejection/resumed cycles for unmatched feedback
45
+ let augmented = timings;
46
+ if (feedbackCount > existingRejectionCount) {
47
+ augmented = [...timings];
48
+ // Use last timing's agentRunId as template
49
+ const templateRunId = timings[timings.length - 1].agentRunId;
50
+ for (let i = existingRejectionCount; i < feedbackCount; i++) {
51
+ const fb = feedback[i];
52
+ // Add run:resumed before work (except for the first synthetic, which continues from existing)
53
+ if (i > existingRejectionCount) {
54
+ augmented.push({
55
+ agentRunId: templateRunId,
56
+ phase: 'run:resumed',
57
+ startedAt: fb.timestamp ?? `synthetic-resumed-${i}`,
58
+ });
59
+ }
60
+ // Add run:rejected
61
+ augmented.push({
62
+ agentRunId: templateRunId,
63
+ phase: 'run:rejected',
64
+ startedAt: fb.timestamp ?? `synthetic-${i}`,
65
+ });
56
66
  }
57
67
  }
58
- return map;
68
+ // Split into iterations at run:rejected boundaries
69
+ const iterations = [];
70
+ let currentEvents = [];
71
+ let rejectionIndex = 0;
72
+ for (const t of augmented) {
73
+ currentEvents.push(t);
74
+ if (t.phase === 'run:rejected') {
75
+ const fb = feedback?.[rejectionIndex];
76
+ iterations.push({
77
+ number: iterations.length + 1,
78
+ timings: currentEvents,
79
+ rejectionMessage: fb?.message,
80
+ rejectionAttachments: fb?.attachments,
81
+ });
82
+ currentEvents = [];
83
+ rejectionIndex++;
84
+ }
85
+ }
86
+ // Remaining events after last rejection (or all events if no rejections)
87
+ if (currentEvents.length > 0) {
88
+ iterations.push({
89
+ number: iterations.length + 1,
90
+ timings: currentEvents,
91
+ });
92
+ }
93
+ return iterations;
59
94
  }
95
+ /* ---------------------------------------------------------------------------
96
+ * Component
97
+ * ------------------------------------------------------------------------- */
60
98
  export function ActivityTab({ timings, loading, error, rejectionFeedback }) {
61
99
  if (loading) {
62
100
  return (_jsx("div", { "data-testid": "activity-tab-loading", className: "flex items-center justify-center p-8", children: _jsx(Loader2, { className: "text-muted-foreground h-6 w-6 animate-spin" }) }));
@@ -69,24 +107,28 @@ export function ActivityTab({ timings, loading, error, rejectionFeedback }) {
69
107
  }
70
108
  const nodeTimings = timings.filter((t) => !isLifecycleEvent(t.phase));
71
109
  const maxDurationMs = Math.max(...nodeTimings.map((t) => t.durationMs ?? 0), ...nodeTimings.map((t) => t.approvalWaitMs ?? 0), 0);
72
- // Build a map from rejection index to feedback message
73
- const rejectionMessages = buildRejectionMessageMap(timings, rejectionFeedback);
74
- const groups = groupTimingsByRun(timings);
75
- const multiRun = groups.length > 1;
110
+ const iterations = buildLifecycleTimeline(timings, rejectionFeedback);
111
+ const hasMultipleIterations = iterations.length > 1;
76
112
  const totalExecMs = nodeTimings.reduce((sum, t) => sum + (t.durationMs ?? 0), 0);
77
113
  const totalWaitMs = nodeTimings.reduce((sum, t) => sum + (t.approvalWaitMs ?? 0), 0);
78
- return (_jsxs("div", { className: "flex flex-col gap-4 p-4", children: [_jsx("div", { "data-testid": "activity-timings", className: "flex flex-col gap-3", children: groups.map((group, groupIdx) => (_jsx(RunGroup, { group: group, groupIdx: groupIdx, multiRun: multiRun, maxDurationMs: maxDurationMs, rejectionMessages: rejectionMessages }, group.runId))) }), totalExecMs > 0 ? (_jsx(SummaryTotals, { totalExecMs: totalExecMs, totalWaitMs: totalWaitMs })) : null] }));
114
+ return (_jsxs("div", { className: "flex flex-col gap-4 p-4", children: [_jsx("div", { "data-testid": "activity-timings", className: "flex flex-col gap-3", children: iterations.map((iteration) => (_jsx(IterationGroup, { iteration: iteration, showHeader: hasMultipleIterations, maxDurationMs: maxDurationMs }, iteration.number))) }), totalExecMs > 0 ? (_jsx(SummaryTotals, { totalExecMs: totalExecMs, totalWaitMs: totalWaitMs })) : null] }));
79
115
  }
80
- function RunGroup({ group, groupIdx, multiRun, maxDurationMs, rejectionMessages, }) {
81
- return (_jsxs("div", { className: "flex flex-col gap-1.5", children: [multiRun ? (_jsxs("div", { className: "text-muted-foreground text-xs font-medium", children: ["Run #", groupIdx + 1] })) : null, group.timings.map((t) => {
116
+ /* ---------------------------------------------------------------------------
117
+ * Iteration group one cycle of work
118
+ * ------------------------------------------------------------------------- */
119
+ function IterationGroup({ iteration, showHeader, maxDurationMs, }) {
120
+ return (_jsxs("div", { "data-testid": `iteration-${iteration.number}`, className: "flex flex-col gap-1.5", children: [showHeader ? (_jsxs("div", { className: "text-muted-foreground text-xs font-medium", children: ["Iteration ", iteration.number] })) : null, iteration.timings.map((t) => {
82
121
  if (isLifecycleEvent(t.phase)) {
83
- const key = `${t.agentRunId}-${t.phase}-${t.startedAt}`;
84
- const rejection = t.phase === 'run:rejected' ? rejectionMessages.get(key) : undefined;
85
- return (_jsx(LifecycleEventRow, { timing: t, message: rejection?.message, attachments: rejection?.attachments }, key));
122
+ // For run:rejected, the message comes from the iteration, not a separate map
123
+ const isRejection = t.phase === 'run:rejected';
124
+ return (_jsx(LifecycleEventRow, { timing: t, message: isRejection ? iteration.rejectionMessage : undefined, attachments: isRejection ? iteration.rejectionAttachments : undefined }, `${t.agentRunId}-${t.phase}-${t.startedAt}`));
86
125
  }
87
126
  return (_jsx(NodeTimingRow, { timing: t, maxDurationMs: maxDurationMs }, `${t.agentRunId}-${t.phase}-${t.startedAt}`));
88
127
  })] }));
89
128
  }
129
+ /* ---------------------------------------------------------------------------
130
+ * Row components
131
+ * ------------------------------------------------------------------------- */
90
132
  function LifecycleEventRow({ timing, message, attachments, }) {
91
133
  const event = LIFECYCLE_EVENTS[timing.phase];
92
134
  if (!event)
@@ -17,6 +17,19 @@ export declare const Empty: Story;
17
17
  export declare const Error: Story;
18
18
  /** Rejection — shows rejected lifecycle event with user feedback text. */
19
19
  export declare const WithRejection: Story;
20
+ /**
21
+ * Multiple rejection/resume cycles — realistic lifecycle showing:
22
+ * Iteration 1: work → rejected("rebase on main")
23
+ * Iteration 2: resumed → work → rejected("rebase on main")
24
+ * Iteration 3: rejected("add support for evidence agent...") — synthetic
25
+ * Iteration 4: rejected("screenshots broken...") — synthetic
26
+ */
27
+ export declare const MultipleRejections: Story;
20
28
  /** Approval wait indicators — phases with approval wait sub-rows. */
21
29
  export declare const WithApprovalWaits: Story;
30
+ /**
31
+ * Full lifecycle — realistic multi-iteration rejection/resume cycle.
32
+ * Shows the full pattern: start → work → rejected → resumed → work → rejected → resumed → completed
33
+ */
34
+ export declare const FullLifecycle: Story;
22
35
  //# sourceMappingURL=activity-tab.stories.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"activity-tab.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/activity-tab.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG7C,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,WAAW,CAclC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,WAAW,CAAC,CAAC;AAmK1C,2EAA2E;AAC3E,eAAO,MAAM,OAAO,EAAE,KAMrB,CAAC;AAEF,uDAAuD;AACvD,eAAO,MAAM,YAAY,EAAE,KAM1B,CAAC;AAEF,kEAAkE;AAClE,eAAO,MAAM,YAAY,EAAE,KAM1B,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,OAAO,EAAE,KAMrB,CAAC;AAEF,8CAA8C;AAC9C,eAAO,MAAM,KAAK,EAAE,KAMnB,CAAC;AAEF,0CAA0C;AAC1C,eAAO,MAAM,KAAK,EAAE,KAMnB,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,aAAa,EAAE,KAsE3B,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,EAAE,KAgC/B,CAAC"}
1
+ {"version":3,"file":"activity-tab.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/activity-tab.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG7C,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,WAAW,CAclC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,WAAW,CAAC,CAAC;AAmK1C,2EAA2E;AAC3E,eAAO,MAAM,OAAO,EAAE,KAMrB,CAAC;AAEF,uDAAuD;AACvD,eAAO,MAAM,YAAY,EAAE,KAM1B,CAAC;AAEF,kEAAkE;AAClE,eAAO,MAAM,YAAY,EAAE,KAM1B,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,OAAO,EAAE,KAMrB,CAAC;AAEF,8CAA8C;AAC9C,eAAO,MAAM,KAAK,EAAE,KAMnB,CAAC;AAEF,0CAA0C;AAC1C,eAAO,MAAM,KAAK,EAAE,KAMnB,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,aAAa,EAAE,KAsE3B,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAwEhC,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,EAAE,KAgC/B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,KAiI3B,CAAC"}
@@ -288,6 +288,85 @@ export const WithRejection = {
288
288
  error: null,
289
289
  },
290
290
  };
291
+ /**
292
+ * Multiple rejection/resume cycles — realistic lifecycle showing:
293
+ * Iteration 1: work → rejected("rebase on main")
294
+ * Iteration 2: resumed → work → rejected("rebase on main")
295
+ * Iteration 3: rejected("add support for evidence agent...") — synthetic
296
+ * Iteration 4: rejected("screenshots broken...") — synthetic
297
+ */
298
+ export const MultipleRejections = {
299
+ args: {
300
+ timings: [
301
+ {
302
+ agentRunId: 'run-1',
303
+ phase: 'run:started',
304
+ startedAt: '2025-01-15T10:00:00Z',
305
+ durationMs: 0,
306
+ },
307
+ {
308
+ agentRunId: 'run-1',
309
+ phase: 'plan',
310
+ startedAt: '2025-01-15T10:00:01Z',
311
+ completedAt: '2025-01-15T10:05:00Z',
312
+ durationMs: 300000,
313
+ },
314
+ {
315
+ agentRunId: 'run-1',
316
+ phase: 'run:rejected',
317
+ startedAt: '2025-01-15T10:05:00Z',
318
+ durationMs: 0,
319
+ },
320
+ {
321
+ agentRunId: 'run-1',
322
+ phase: 'run:resumed',
323
+ startedAt: '2025-01-15T10:10:00Z',
324
+ durationMs: 0,
325
+ },
326
+ {
327
+ agentRunId: 'run-1',
328
+ phase: 'merge',
329
+ startedAt: '2025-01-15T10:10:01Z',
330
+ completedAt: '2025-01-15T10:15:00Z',
331
+ durationMs: 299000,
332
+ },
333
+ {
334
+ agentRunId: 'run-1',
335
+ phase: 'run:rejected',
336
+ startedAt: '2025-01-15T10:15:00Z',
337
+ durationMs: 0,
338
+ },
339
+ ],
340
+ rejectionFeedback: [
341
+ {
342
+ iteration: 1,
343
+ message: 'rebase on main',
344
+ phase: 'plan',
345
+ timestamp: '2025-01-15T10:05:00Z',
346
+ },
347
+ {
348
+ iteration: 2,
349
+ message: 'rebase on main',
350
+ phase: 'merge',
351
+ timestamp: '2025-01-15T10:15:00Z',
352
+ },
353
+ {
354
+ iteration: 3,
355
+ message: 'add support for evidence agent into fast mode as well',
356
+ phase: 'merge',
357
+ timestamp: '2025-01-15T10:20:00Z',
358
+ },
359
+ {
360
+ iteration: 4,
361
+ message: "I've test and created a feature but the screenshots seems to be broken https://github.com/shep-ai/cli/pull/258 fix",
362
+ phase: 'merge',
363
+ timestamp: '2025-01-15T10:25:00Z',
364
+ },
365
+ ],
366
+ loading: false,
367
+ error: null,
368
+ },
369
+ };
291
370
  /** Approval wait indicators — phases with approval wait sub-rows. */
292
371
  export const WithApprovalWaits = {
293
372
  args: {
@@ -322,3 +401,137 @@ export const WithApprovalWaits = {
322
401
  error: null,
323
402
  },
324
403
  };
404
+ /**
405
+ * Full lifecycle — realistic multi-iteration rejection/resume cycle.
406
+ * Shows the full pattern: start → work → rejected → resumed → work → rejected → resumed → completed
407
+ */
408
+ export const FullLifecycle = {
409
+ args: {
410
+ timings: [
411
+ // Iteration 1
412
+ {
413
+ agentRunId: 'run-1',
414
+ phase: 'run:started',
415
+ startedAt: '2025-01-15T10:00:00Z',
416
+ durationMs: 0,
417
+ },
418
+ {
419
+ agentRunId: 'run-1',
420
+ phase: 'analyze',
421
+ startedAt: '2025-01-15T10:00:01Z',
422
+ completedAt: '2025-01-15T10:00:15Z',
423
+ durationMs: 14000,
424
+ },
425
+ {
426
+ agentRunId: 'run-1',
427
+ phase: 'requirements',
428
+ startedAt: '2025-01-15T10:00:15Z',
429
+ completedAt: '2025-01-15T10:02:00Z',
430
+ durationMs: 105000,
431
+ waitingApprovalAt: '2025-01-15T10:01:30Z',
432
+ approvalWaitMs: 20000,
433
+ },
434
+ {
435
+ agentRunId: 'run-1',
436
+ phase: 'plan',
437
+ startedAt: '2025-01-15T10:02:00Z',
438
+ completedAt: '2025-01-15T10:04:00Z',
439
+ durationMs: 120000,
440
+ },
441
+ {
442
+ agentRunId: 'run-1',
443
+ phase: 'implement',
444
+ startedAt: '2025-01-15T10:04:00Z',
445
+ completedAt: '2025-01-15T10:14:00Z',
446
+ durationMs: 600000,
447
+ },
448
+ {
449
+ agentRunId: 'run-1',
450
+ phase: 'merge',
451
+ startedAt: '2025-01-15T10:14:00Z',
452
+ completedAt: '2025-01-15T10:15:00Z',
453
+ durationMs: 60000,
454
+ waitingApprovalAt: '2025-01-15T10:14:30Z',
455
+ approvalWaitMs: 25000,
456
+ },
457
+ {
458
+ agentRunId: 'run-1',
459
+ phase: 'run:rejected',
460
+ startedAt: '2025-01-15T10:15:00Z',
461
+ durationMs: 0,
462
+ },
463
+ // Iteration 2
464
+ {
465
+ agentRunId: 'run-1',
466
+ phase: 'run:resumed',
467
+ startedAt: '2025-01-15T10:20:00Z',
468
+ durationMs: 0,
469
+ },
470
+ {
471
+ agentRunId: 'run-1',
472
+ phase: 'implement',
473
+ startedAt: '2025-01-15T10:20:01Z',
474
+ completedAt: '2025-01-15T10:28:00Z',
475
+ durationMs: 479000,
476
+ },
477
+ {
478
+ agentRunId: 'run-1',
479
+ phase: 'merge',
480
+ startedAt: '2025-01-15T10:28:00Z',
481
+ completedAt: '2025-01-15T10:29:00Z',
482
+ durationMs: 60000,
483
+ waitingApprovalAt: '2025-01-15T10:28:30Z',
484
+ approvalWaitMs: 20000,
485
+ },
486
+ {
487
+ agentRunId: 'run-1',
488
+ phase: 'run:rejected',
489
+ startedAt: '2025-01-15T10:29:00Z',
490
+ durationMs: 0,
491
+ },
492
+ // Iteration 3
493
+ {
494
+ agentRunId: 'run-1',
495
+ phase: 'run:resumed',
496
+ startedAt: '2025-01-15T10:35:00Z',
497
+ durationMs: 0,
498
+ },
499
+ {
500
+ agentRunId: 'run-1',
501
+ phase: 'implement',
502
+ startedAt: '2025-01-15T10:35:01Z',
503
+ completedAt: '2025-01-15T10:40:00Z',
504
+ durationMs: 299000,
505
+ },
506
+ {
507
+ agentRunId: 'run-1',
508
+ phase: 'merge',
509
+ startedAt: '2025-01-15T10:40:00Z',
510
+ completedAt: '2025-01-15T10:41:00Z',
511
+ durationMs: 60000,
512
+ },
513
+ {
514
+ agentRunId: 'run-1',
515
+ phase: 'run:completed',
516
+ startedAt: '2025-01-15T10:41:00Z',
517
+ durationMs: 0,
518
+ },
519
+ ],
520
+ rejectionFeedback: [
521
+ {
522
+ iteration: 1,
523
+ message: 'The PR has merge conflicts — rebase on main first',
524
+ phase: 'merge',
525
+ timestamp: '2025-01-15T10:15:00Z',
526
+ },
527
+ {
528
+ iteration: 2,
529
+ message: 'Tests are failing in the CI pipeline — fix the snapshot tests',
530
+ phase: 'merge',
531
+ timestamp: '2025-01-15T10:29:00Z',
532
+ },
533
+ ],
534
+ loading: false,
535
+ error: null,
536
+ },
537
+ };
@@ -73,7 +73,7 @@ export function FeatureNode({ data, selected, }) {
73
73
  }, 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) => {
74
74
  setConfirmOpen(false);
75
75
  data.onDelete?.(data.featureId, cleanup);
76
- }, isDeleting: false, featureName: data.name ?? 'this feature', featureId: data.featureId })] })) : null, _jsxs("div", { "data-testid": "feature-node-card", "aria-busy": data.state === 'creating' ? 'true' : undefined, className: cn('bg-card flex min-h-35 w-72 flex-col rounded-lg border p-3 shadow-sm', selected && 'ring-primary ring-2'), children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { "data-testid": "feature-node-lifecycle-label", className: cn('text-[10px] font-semibold tracking-wider'), children: data.state === 'blocked' ? 'BLOCKED' : lifecycleDisplayLabels[data.lifecycle] }), _jsxs("div", { className: "flex items-center gap-0.5", children: [data.fastMode ? (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { "data-testid": "feature-node-fast-mode-badge", className: "-mt-1 p-1", children: _jsx(Zap, { className: "h-3.5 w-3.5 text-amber-500" }) }) }), _jsx(TooltipContent, { side: "bottom", children: "Fast Mode" })] }) })) : null, data.agentType ? (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", "aria-label": agentTypeLabels[data.agentType] ?? data.agentType, "data-testid": "feature-node-agent-badge", onClick: (e) => {
76
+ }, isDeleting: false, featureName: data.name ?? 'this feature', featureId: data.featureId })] })) : null, _jsxs("div", { "data-testid": "feature-node-card", "aria-busy": data.state === 'creating' ? 'true' : undefined, className: cn('bg-card flex min-h-35 w-72 cursor-pointer flex-col rounded-lg border p-3 shadow-sm', selected && 'ring-primary ring-2'), children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { "data-testid": "feature-node-lifecycle-label", className: cn('text-[10px] font-semibold tracking-wider'), children: data.state === 'blocked' ? 'BLOCKED' : lifecycleDisplayLabels[data.lifecycle] }), _jsxs("div", { className: "flex items-center gap-0.5", children: [data.fastMode ? (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { "data-testid": "feature-node-fast-mode-badge", className: "-mt-1 p-1", children: _jsx(Zap, { className: "h-3.5 w-3.5 text-amber-500" }) }) }), _jsx(TooltipContent, { side: "bottom", children: "Fast Mode" })] }) })) : null, data.agentType ? (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", "aria-label": agentTypeLabels[data.agentType] ?? data.agentType, "data-testid": "feature-node-agent-badge", onClick: (e) => {
77
77
  e.stopPropagation();
78
78
  data.onSettings?.();
79
79
  }, className: cn('nodrag -mt-1 -mr-1 p-1', data.onSettings
@@ -39,7 +39,7 @@ export function RepositoryNode({ data }) {
39
39
  e.stopPropagation();
40
40
  data.onClick?.();
41
41
  }
42
- }, className: "nodrag bg-card flex min-w-[18rem] cursor-default items-center gap-3 rounded-full border px-4 py-3 shadow-sm", children: [_jsx(Github, { className: "text-muted-foreground h-5 w-5 shrink-0" }), _jsx("span", { "data-testid": "repository-node-name", className: "min-w-0 truncate text-sm font-medium", children: data.name }), _jsxs("div", { className: cn('flex shrink-0 items-center gap-2', (data.repositoryPath ?? data.onAdd) && 'ml-auto'), onClick: (e) => e.stopPropagation(), children: [data.repositoryPath ? (_jsxs(_Fragment, { children: [_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "flex items-center", children: _jsx(ActionButton, { label: "Open in IDE", onClick: actions.openInIde, loading: actions.ideLoading, error: !!actions.ideError, icon: Code2, iconOnly: true, variant: "ghost", size: "icon-xs" }) }) }), _jsx(TooltipContent, { children: "Open in IDE" })] }) }), _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "flex items-center", children: _jsx(ActionButton, { label: "Open in Shell", onClick: actions.openInShell, loading: actions.shellLoading, error: !!actions.shellError, icon: Terminal, iconOnly: true, variant: "ghost", size: "icon-xs" }) }) }), _jsx(TooltipContent, { children: "Open in Shell" })] }) }), _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "flex items-center", children: _jsx(ActionButton, { label: "Open Folder", onClick: actions.openFolder, loading: actions.folderLoading, error: !!actions.folderError, icon: FolderOpen, iconOnly: true, variant: "ghost", size: "icon-xs" }) }) }), _jsx(TooltipContent, { children: "Open Folder" })] }) }), featureFlags.envDeploy ? (_jsxs(_Fragment, { children: [_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "flex items-center", children: _jsx(ActionButton, { label: isDeploymentActive ? 'Stop Dev Server' : 'Start Dev Server', onClick: isDeploymentActive ? deployAction.stop : deployAction.deploy, loading: deployAction.deployLoading || deployAction.stopLoading, error: !!deployAction.deployError, icon: isDeploymentActive ? Square : Play, iconOnly: true, variant: "ghost", size: "icon-xs" }) }) }), _jsx(TooltipContent, { children: isDeploymentActive ? 'Stop Dev Server' : 'Start Dev Server' })] }) }), isDeploymentActive ? (_jsx(DeploymentStatusBadge, { status: deployAction.status, url: deployAction.url, targetId: data.repositoryPath })) : null] })) : null] })) : null, data.onAdd ? (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { "aria-label": "Add feature", "data-testid": "repository-node-add-button", onClick: (e) => {
42
+ }, className: "nodrag bg-card flex min-w-[18rem] cursor-pointer items-center gap-3 rounded-full border px-4 py-3 shadow-sm", children: [_jsx(Github, { className: "text-muted-foreground h-5 w-5 shrink-0" }), _jsx("span", { "data-testid": "repository-node-name", className: "min-w-0 truncate text-sm font-medium", children: data.name }), _jsxs("div", { className: cn('flex shrink-0 items-center gap-2', (data.repositoryPath ?? data.onAdd) && 'ml-auto'), onClick: (e) => e.stopPropagation(), children: [data.repositoryPath ? (_jsxs(_Fragment, { children: [_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "flex items-center", children: _jsx(ActionButton, { label: "Open in IDE", onClick: actions.openInIde, loading: actions.ideLoading, error: !!actions.ideError, icon: Code2, iconOnly: true, variant: "ghost", size: "icon-xs" }) }) }), _jsx(TooltipContent, { children: "Open in IDE" })] }) }), _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "flex items-center", children: _jsx(ActionButton, { label: "Open in Shell", onClick: actions.openInShell, loading: actions.shellLoading, error: !!actions.shellError, icon: Terminal, iconOnly: true, variant: "ghost", size: "icon-xs" }) }) }), _jsx(TooltipContent, { children: "Open in Shell" })] }) }), _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "flex items-center", children: _jsx(ActionButton, { label: "Open Folder", onClick: actions.openFolder, loading: actions.folderLoading, error: !!actions.folderError, icon: FolderOpen, iconOnly: true, variant: "ghost", size: "icon-xs" }) }) }), _jsx(TooltipContent, { children: "Open Folder" })] }) }), featureFlags.envDeploy ? (_jsxs(_Fragment, { children: [_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "flex items-center", children: _jsx(ActionButton, { label: isDeploymentActive ? 'Stop Dev Server' : 'Start Dev Server', onClick: isDeploymentActive ? deployAction.stop : deployAction.deploy, loading: deployAction.deployLoading || deployAction.stopLoading, error: !!deployAction.deployError, icon: isDeploymentActive ? Square : Play, iconOnly: true, variant: "ghost", size: "icon-xs" }) }) }), _jsx(TooltipContent, { children: isDeploymentActive ? 'Stop Dev Server' : 'Start Dev Server' })] }) }), isDeploymentActive ? (_jsx(DeploymentStatusBadge, { status: deployAction.status, url: deployAction.url, targetId: data.repositoryPath })) : null] })) : null] })) : null, data.onAdd ? (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { "aria-label": "Add feature", "data-testid": "repository-node-add-button", onClick: (e) => {
43
43
  e.stopPropagation();
44
44
  data.onAdd?.();
45
45
  }, className: "text-muted-foreground hover:bg-accent dark:hover:bg-accent/50 flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center rounded-full transition-colors hover:text-blue-500", children: _jsx(Plus, { className: "h-3.5 w-3.5" }) }) }), _jsx(TooltipContent, { children: "Add feature" })] }) })) : null] })] }), data.onAdd || data.showHandles ? (_jsx(Handle, { type: "source", position: Position.Right, isConnectable: !data.showHandles, className: "opacity-0!" })) : null] }));