@shepai/cli 1.46.0 → 1.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/apis/json-schema/ApprovalGateDefaults.yaml +26 -0
  2. package/apis/json-schema/Feature.yaml +3 -0
  3. package/apis/json-schema/Repository.yaml +16 -0
  4. package/apis/json-schema/Settings.yaml +5 -0
  5. package/apis/json-schema/WorkflowConfig.yaml +4 -20
  6. package/dist/packages/core/src/application/ports/output/services/git-pr-service.interface.d.ts +17 -0
  7. package/dist/packages/core/src/application/ports/output/services/git-pr-service.interface.d.ts.map +1 -1
  8. package/dist/packages/core/src/application/ports/output/services/worktree-service.interface.d.ts +11 -1
  9. package/dist/packages/core/src/application/ports/output/services/worktree-service.interface.d.ts.map +1 -1
  10. package/dist/packages/core/src/application/use-cases/features/create/create-feature.use-case.d.ts +3 -1
  11. package/dist/packages/core/src/application/use-cases/features/create/create-feature.use-case.d.ts.map +1 -1
  12. package/dist/packages/core/src/application/use-cases/features/create/create-feature.use-case.js +11 -4
  13. package/dist/packages/core/src/application/use-cases/features/get-plan-artifact.use-case.d.ts +19 -0
  14. package/dist/packages/core/src/application/use-cases/features/get-plan-artifact.use-case.d.ts.map +1 -0
  15. package/dist/packages/core/src/application/use-cases/features/get-plan-artifact.use-case.js +52 -0
  16. package/dist/packages/core/src/infrastructure/di/container.d.ts.map +1 -1
  17. package/dist/packages/core/src/infrastructure/di/container.js +5 -0
  18. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.d.ts.map +1 -1
  19. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.js +2 -0
  20. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge.node.d.ts +2 -0
  21. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge.node.d.ts.map +1 -1
  22. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge.node.js +10 -3
  23. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/prompts/merge-prompts.d.ts.map +1 -1
  24. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/prompts/merge-prompts.js +15 -7
  25. package/dist/packages/core/src/infrastructure/services/git/git-pr.service.d.ts +2 -0
  26. package/dist/packages/core/src/infrastructure/services/git/git-pr.service.d.ts.map +1 -1
  27. package/dist/packages/core/src/infrastructure/services/git/git-pr.service.js +41 -0
  28. package/dist/packages/core/src/infrastructure/services/git/worktree.service.d.ts +2 -1
  29. package/dist/packages/core/src/infrastructure/services/git/worktree.service.d.ts.map +1 -1
  30. package/dist/packages/core/src/infrastructure/services/git/worktree.service.js +29 -4
  31. package/dist/src/presentation/cli/ui/messages.d.ts.map +1 -1
  32. package/dist/src/presentation/cli/ui/messages.js +1 -7
  33. package/dist/src/presentation/web/app/actions/create-feature.d.ts +2 -0
  34. package/dist/src/presentation/web/app/actions/create-feature.d.ts.map +1 -1
  35. package/dist/src/presentation/web/app/actions/create-feature.js +3 -1
  36. package/dist/src/presentation/web/app/actions/get-merge-review-data.d.ts +7 -0
  37. package/dist/src/presentation/web/app/actions/get-merge-review-data.d.ts.map +1 -0
  38. package/dist/src/presentation/web/app/actions/get-merge-review-data.js +49 -0
  39. package/dist/src/presentation/web/app/actions/get-workflow-defaults.d.ts +11 -0
  40. package/dist/src/presentation/web/app/actions/get-workflow-defaults.d.ts.map +1 -0
  41. package/dist/src/presentation/web/app/actions/get-workflow-defaults.js +15 -0
  42. package/dist/src/presentation/web/app/page.d.ts.map +1 -1
  43. package/dist/src/presentation/web/app/page.js +1 -0
  44. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts +5 -1
  45. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts.map +1 -1
  46. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.js +25 -7
  47. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.d.ts +17 -0
  48. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.d.ts.map +1 -1
  49. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.js +54 -2
  50. package/dist/src/presentation/web/components/common/feature-node/feature-node.d.ts.map +1 -1
  51. package/dist/src/presentation/web/components/common/feature-node/feature-node.js +8 -1
  52. package/dist/src/presentation/web/components/common/feature-node/feature-node.stories.d.ts +1 -0
  53. package/dist/src/presentation/web/components/common/feature-node/feature-node.stories.d.ts.map +1 -1
  54. package/dist/src/presentation/web/components/common/feature-node/feature-node.stories.js +22 -1
  55. package/dist/src/presentation/web/components/common/merge-review/index.d.ts +4 -0
  56. package/dist/src/presentation/web/components/common/merge-review/index.d.ts.map +1 -0
  57. package/dist/src/presentation/web/components/common/merge-review/index.js +2 -0
  58. package/dist/src/presentation/web/components/common/merge-review/merge-review-config.d.ts +75 -0
  59. package/dist/src/presentation/web/components/common/merge-review/merge-review-config.d.ts.map +1 -0
  60. package/dist/src/presentation/web/components/common/merge-review/merge-review-config.js +1 -0
  61. package/dist/src/presentation/web/components/common/merge-review/merge-review-drawer.d.ts +3 -0
  62. package/dist/src/presentation/web/components/common/merge-review/merge-review-drawer.d.ts.map +1 -0
  63. package/dist/src/presentation/web/components/common/merge-review/merge-review-drawer.js +7 -0
  64. package/dist/src/presentation/web/components/common/merge-review/merge-review.d.ts +3 -0
  65. package/dist/src/presentation/web/components/common/merge-review/merge-review.d.ts.map +1 -0
  66. package/dist/src/presentation/web/components/common/merge-review/merge-review.js +36 -0
  67. package/dist/src/presentation/web/components/common/merge-review/merge-review.stories.d.ts +32 -0
  68. package/dist/src/presentation/web/components/common/merge-review/merge-review.stories.d.ts.map +1 -0
  69. package/dist/src/presentation/web/components/common/merge-review/merge-review.stories.js +165 -0
  70. package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map +1 -1
  71. package/dist/src/presentation/web/components/features/control-center/control-center-inner.js +66 -2
  72. package/dist/src/presentation/web/components/features/control-center/control-center.stories.d.ts.map +1 -1
  73. package/dist/src/presentation/web/components/features/control-center/control-center.stories.js +15 -0
  74. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts.map +1 -1
  75. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js +37 -15
  76. package/dist/tsconfig.build.tsbuildinfo +1 -1
  77. package/package.json +1 -1
  78. package/web/.next/BUILD_ID +1 -1
  79. package/web/.next/build-manifest.json +2 -2
  80. package/web/.next/cache/.previewinfo +1 -1
  81. package/web/.next/cache/.rscinfo +1 -1
  82. package/web/.next/cache/.tsbuildinfo +1 -1
  83. package/web/.next/cache/config.json +3 -3
  84. package/web/.next/fallback-build-manifest.json +2 -2
  85. package/web/.next/prerender-manifest.json +3 -3
  86. package/web/.next/required-server-files.js +1 -1
  87. package/web/.next/required-server-files.json +1 -1
  88. package/web/.next/server/app/_global-error.html +2 -2
  89. package/web/.next/server/app/_global-error.rsc +1 -1
  90. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  91. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  92. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  93. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  94. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  95. package/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  96. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  97. package/web/.next/server/app/page/server-reference-manifest.json +52 -22
  98. package/web/.next/server/app/page.js +1 -1
  99. package/web/.next/server/app/page.js.nft.json +1 -1
  100. package/web/.next/server/app/page_client-reference-manifest.js +1 -1
  101. package/web/.next/server/app/version/page.js.nft.json +1 -1
  102. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  103. package/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js +4 -4
  104. package/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js.map +1 -1
  105. package/web/.next/server/chunks/ssr/[root-of-the-server]__19d52794._.js +9 -0
  106. package/web/.next/server/chunks/ssr/[root-of-the-server]__19d52794._.js.map +1 -0
  107. package/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js +1 -1
  108. package/web/.next/{standalone/src/presentation/web/.next/server/chunks/ssr/_3ac3601c._.js → server/chunks/ssr/_1fafa9db._.js} +2 -2
  109. package/web/.next/server/chunks/ssr/_1fafa9db._.js.map +1 -0
  110. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  111. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +1 -1
  112. package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js +1 -1
  113. package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js.map +1 -1
  114. package/web/.next/server/pages/500.html +2 -2
  115. package/web/.next/server/server-reference-manifest.js +1 -1
  116. package/web/.next/server/server-reference-manifest.json +53 -23
  117. package/web/.next/standalone/src/presentation/web/.next/BUILD_ID +1 -1
  118. package/web/.next/standalone/src/presentation/web/.next/build-manifest.json +2 -2
  119. package/web/.next/standalone/src/presentation/web/.next/prerender-manifest.json +3 -3
  120. package/web/.next/standalone/src/presentation/web/.next/required-server-files.json +1 -1
  121. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.html +2 -2
  122. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.rsc +1 -1
  123. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  124. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  125. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  126. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  127. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  128. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  129. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  130. package/web/.next/standalone/src/presentation/web/.next/server/app/page/server-reference-manifest.json +52 -22
  131. package/web/.next/standalone/src/presentation/web/.next/server/app/page.js +1 -1
  132. package/web/.next/standalone/src/presentation/web/.next/server/app/page.js.nft.json +1 -1
  133. package/web/.next/standalone/src/presentation/web/.next/server/app/page_client-reference-manifest.js +1 -1
  134. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page.js.nft.json +1 -1
  135. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  136. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js +4 -4
  137. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__19d52794._.js +9 -0
  138. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js +1 -1
  139. package/web/.next/{server/chunks/ssr/_3ac3601c._.js → standalone/src/presentation/web/.next/server/chunks/ssr/_1fafa9db._.js} +2 -2
  140. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  141. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js +1 -1
  142. package/web/.next/standalone/src/presentation/web/.next/server/pages/500.html +2 -2
  143. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.js +1 -1
  144. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.json +53 -23
  145. package/web/.next/standalone/src/presentation/web/app/actions/create-feature.ts +5 -1
  146. package/web/.next/standalone/src/presentation/web/app/actions/get-merge-review-data.ts +64 -0
  147. package/web/.next/standalone/src/presentation/web/app/actions/get-workflow-defaults.ts +28 -0
  148. package/web/.next/standalone/src/presentation/web/app/page.tsx +1 -0
  149. package/web/.next/standalone/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.tsx +74 -1
  150. package/web/.next/standalone/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.tsx +55 -5
  151. package/web/.next/standalone/src/presentation/web/components/common/feature-node/feature-node.stories.tsx +23 -1
  152. package/web/.next/standalone/src/presentation/web/components/common/feature-node/feature-node.tsx +6 -1
  153. package/web/.next/standalone/src/presentation/web/components/common/merge-review/index.ts +7 -0
  154. package/web/.next/standalone/src/presentation/web/components/common/merge-review/merge-review-config.ts +81 -0
  155. package/web/.next/standalone/src/presentation/web/components/common/merge-review/merge-review-drawer.tsx +34 -0
  156. package/web/.next/standalone/src/presentation/web/components/common/merge-review/merge-review.stories.tsx +240 -0
  157. package/web/.next/standalone/src/presentation/web/components/common/merge-review/merge-review.tsx +252 -0
  158. package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center-inner.tsx +96 -1
  159. package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center.stories.tsx +15 -0
  160. package/web/.next/standalone/src/presentation/web/components/features/control-center/use-control-center-state.ts +47 -17
  161. package/web/.next/standalone/src/presentation/web/server.js +1 -1
  162. package/web/.next/static/chunks/{9f8e71029fb04681.js → 0da7a9860fa6ae05.js} +2 -2
  163. package/web/.next/static/chunks/22f30ce91a3c79c1.js +1 -0
  164. package/web/.next/static/chunks/66378a6c055c4e67.js +10 -0
  165. package/web/.next/static/chunks/8668962d3e682263.css +2 -0
  166. package/web/.next/trace +1 -1
  167. package/web/.next/trace-build +1 -1
  168. package/web/.next/server/chunks/ssr/[root-of-the-server]__4e8ca89c._.js +0 -9
  169. package/web/.next/server/chunks/ssr/[root-of-the-server]__4e8ca89c._.js.map +0 -1
  170. package/web/.next/server/chunks/ssr/_3ac3601c._.js.map +0 -1
  171. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__4e8ca89c._.js +0 -9
  172. package/web/.next/static/chunks/83ff3b6bbee54bce.js +0 -1
  173. package/web/.next/static/chunks/b7b7abe79e05b229.css +0 -2
  174. package/web/.next/static/chunks/ecf44fa8a155c854.js +0 -10
  175. /package/web/.next/static/{nM-uXIiW9k_0Z_GM_4bI4 → 1kZFDMQ0i-Y8dy1KkHlzv}/_buildManifest.js +0 -0
  176. /package/web/.next/static/{nM-uXIiW9k_0Z_GM_4bI4 → 1kZFDMQ0i-Y8dy1KkHlzv}/_clientMiddlewareManifest.json +0 -0
  177. /package/web/.next/static/{nM-uXIiW9k_0Z_GM_4bI4 → 1kZFDMQ0i-Y8dy1KkHlzv}/_ssgManifest.js +0 -0
@@ -6,13 +6,18 @@ import { toast } from 'sonner';
6
6
  import { approveFeature } from '@/app/actions/approve-feature';
7
7
  import { getFeatureArtifact } from '@/app/actions/get-feature-artifact';
8
8
  import { getResearchArtifact } from '@/app/actions/get-research-artifact';
9
+ import { getWorkflowDefaults } from '@/app/actions/get-workflow-defaults';
10
+ import type { WorkflowDefaults } from '@/app/actions/get-workflow-defaults';
11
+ import { getMergeReviewData } from '@/app/actions/get-merge-review-data';
9
12
  import type { TechDecisionsReviewData } from '@/components/common/tech-decisions-review';
13
+ import type { MergeReviewData } from '@/components/common/merge-review';
10
14
  import { FeaturesCanvas } from '@/components/features/features-canvas';
11
15
  import type { CanvasNodeType } from '@/components/features/features-canvas';
12
16
  import { FeatureDrawer, FeatureCreateDrawer } from '@/components/common';
13
17
  import { PrdQuestionnaireDrawer } from '@/components/common/prd-questionnaire';
14
18
  import type { PrdQuestionnaireData } from '@/components/common/prd-questionnaire';
15
19
  import { TechDecisionsDrawer } from '@/components/common/tech-decisions-review';
20
+ import { MergeReviewDrawer } from '@/components/common/merge-review';
16
21
  import { NotificationPermissionBanner } from '@/components/common/notification-permission-banner';
17
22
  import { ControlCenterEmptyState } from './control-center-empty-state';
18
23
  import { useControlCenterState } from './use-control-center-state';
@@ -44,6 +49,16 @@ export function ControlCenterInner({ initialNodes, initialEdges }: ControlCenter
44
49
  closeCreateDrawer,
45
50
  } = useControlCenterState(initialNodes, initialEdges);
46
51
 
52
+ // Workflow defaults for the create-feature drawer
53
+ const [workflowDefaults, setWorkflowDefaults] = useState<WorkflowDefaults | undefined>();
54
+ useEffect(() => {
55
+ getWorkflowDefaults()
56
+ .then(setWorkflowDefaults)
57
+ .catch(() => {
58
+ // Settings unavailable — drawer falls back to all-false defaults
59
+ });
60
+ }, []);
61
+
47
62
  // PRD questionnaire drawer state
48
63
  const [prdSelections, setPrdSelections] = useState<Record<string, string>>({});
49
64
  const [isPrdProcessing, setIsPrdProcessing] = useState(false);
@@ -54,12 +69,19 @@ export function ControlCenterInner({ initialNodes, initialEdges }: ControlCenter
54
69
  const [techDecisionsData, setTechDecisionsData] = useState<TechDecisionsReviewData | null>(null);
55
70
  const [isLoadingTechDecisions, setIsLoadingTechDecisions] = useState(false);
56
71
 
72
+ // Merge review drawer state
73
+ const [mergeReviewData, setMergeReviewData] = useState<MergeReviewData | null>(null);
74
+ const [isLoadingMergeReview, setIsLoadingMergeReview] = useState(false);
75
+
57
76
  const showPrdDrawer =
58
77
  selectedNode?.lifecycle === 'requirements' && selectedNode?.state === 'action-required';
59
78
 
60
79
  const showTechDecisionsDrawer =
61
80
  selectedNode?.lifecycle === 'implementation' && selectedNode?.state === 'action-required';
62
81
 
82
+ const showMergeReviewDrawer =
83
+ selectedNode?.lifecycle === 'review' && selectedNode?.state === 'action-required';
84
+
63
85
  const handlePrdSelect = useCallback((questionId: string, optionId: string) => {
64
86
  setPrdSelections((prev) => ({ ...prev, [questionId]: optionId }));
65
87
  }, []);
@@ -113,6 +135,28 @@ export function ControlCenterInner({ initialNodes, initialEdges }: ControlCenter
113
135
  clearSelection();
114
136
  }, [selectedNode?.featureId, clearSelection]);
115
137
 
138
+ const handleMergeApprove = useCallback(async () => {
139
+ const featureId = selectedNode?.featureId;
140
+ if (!featureId) return;
141
+
142
+ const result = await approveFeature(featureId);
143
+
144
+ if (!result.approved) {
145
+ toast.error(result.error ?? 'Failed to approve merge');
146
+ return;
147
+ }
148
+
149
+ toast.success('Merge approved — agent resuming');
150
+ clearSelection();
151
+ }, [selectedNode?.featureId, clearSelection]);
152
+
153
+ const handleMergeRefine = useCallback(async (_text: string) => {
154
+ setIsLoadingMergeReview(true);
155
+ // TODO: Call API to refine merge
156
+ await new Promise((resolve) => setTimeout(resolve, 1500));
157
+ setIsLoadingMergeReview(false);
158
+ }, []);
159
+
116
160
  // Fetch questionnaire data and reset selections when a different feature is selected
117
161
  const prdFeatureId = showPrdDrawer ? selectedNode?.featureId : null;
118
162
  useEffect(() => {
@@ -185,6 +229,36 @@ export function ControlCenterInner({ initialNodes, initialEdges }: ControlCenter
185
229
  };
186
230
  }, [techDecisionsFeatureId]);
187
231
 
232
+ // Fetch merge review data when a feature is in review + action-required
233
+ const mergeFeatureId = showMergeReviewDrawer ? selectedNode?.featureId : null;
234
+ useEffect(() => {
235
+ setMergeReviewData(null);
236
+
237
+ if (!mergeFeatureId) return;
238
+
239
+ let cancelled = false;
240
+ setIsLoadingMergeReview(true);
241
+ getMergeReviewData(mergeFeatureId)
242
+ .then((result) => {
243
+ if (cancelled) return;
244
+ if ('error' in result) {
245
+ toast.error(result.error);
246
+ return;
247
+ }
248
+ setMergeReviewData(result);
249
+ })
250
+ .catch(() => {
251
+ if (!cancelled) toast.error('Failed to load merge review data');
252
+ })
253
+ .finally(() => {
254
+ if (!cancelled) setIsLoadingMergeReview(false);
255
+ });
256
+
257
+ return () => {
258
+ cancelled = true;
259
+ };
260
+ }, [mergeFeatureId]);
261
+
188
262
  const hasRepositories = nodes.some((n) => n.type === 'repositoryNode');
189
263
 
190
264
  // Listen for global "open create drawer" events from the sidebar
@@ -204,6 +278,7 @@ export function ControlCenterInner({ initialNodes, initialEdges }: ControlCenter
204
278
  onClose={closeCreateDrawer}
205
279
  onSubmit={handleCreateFeatureSubmit}
206
280
  repositoryPath={pendingRepositoryPath}
281
+ workflowDefaults={workflowDefaults}
207
282
  />
208
283
  </>
209
284
  );
@@ -227,7 +302,9 @@ export function ControlCenterInner({ initialNodes, initialEdges }: ControlCenter
227
302
  emptyState={<ControlCenterEmptyState onRepositorySelect={handleAddRepository} />}
228
303
  />
229
304
  <FeatureDrawer
230
- selectedNode={showPrdDrawer || showTechDecisionsDrawer ? null : selectedNode}
305
+ selectedNode={
306
+ showPrdDrawer || showTechDecisionsDrawer || showMergeReviewDrawer ? null : selectedNode
307
+ }
231
308
  onClose={clearSelection}
232
309
  onDelete={handleDeleteFeature}
233
310
  isDeleting={isDeleting}
@@ -269,11 +346,29 @@ export function ControlCenterInner({ initialNodes, initialEdges }: ControlCenter
269
346
  isProcessing={isLoadingTechDecisions}
270
347
  />
271
348
  ) : null}
349
+ {mergeReviewData ? (
350
+ <MergeReviewDrawer
351
+ open={showMergeReviewDrawer}
352
+ onClose={clearSelection}
353
+ featureName={selectedNode?.name ?? ''}
354
+ featureId={selectedNode?.featureId}
355
+ repositoryPath={selectedNode?.repositoryPath}
356
+ branch={selectedNode?.branch}
357
+ specPath={selectedNode?.specPath}
358
+ data={mergeReviewData}
359
+ onApprove={handleMergeApprove}
360
+ onRefine={handleMergeRefine}
361
+ onDelete={handleDeleteFeature}
362
+ isDeleting={isDeleting}
363
+ isProcessing={isLoadingMergeReview}
364
+ />
365
+ ) : null}
272
366
  <FeatureCreateDrawer
273
367
  open={isCreateDrawerOpen}
274
368
  onClose={closeCreateDrawer}
275
369
  onSubmit={handleCreateFeatureSubmit}
276
370
  repositoryPath={pendingRepositoryPath}
371
+ workflowDefaults={workflowDefaults}
277
372
  />
278
373
  </>
279
374
  );
@@ -75,6 +75,21 @@ const featureNodes: FeatureNodeType[] = [
75
75
  branch: 'feat/api-gateway',
76
76
  },
77
77
  },
78
+ {
79
+ id: 'feat-4',
80
+ type: 'featureNode',
81
+ position: { x: 800, y: 345 },
82
+ data: {
83
+ name: 'Merge Review',
84
+ description: 'PR ready for merge approval',
85
+ featureId: '#f4',
86
+ lifecycle: 'review',
87
+ state: 'action-required',
88
+ progress: 90,
89
+ repositoryPath: '/home/user/my-repo',
90
+ branch: 'feat/merge-review',
91
+ },
92
+ },
78
93
  ];
79
94
 
80
95
  const repoNode: RepositoryNodeType = {
@@ -351,8 +351,19 @@ export function useControlCenterState(
351
351
  }
352
352
 
353
353
  setSelectedNode(null);
354
- setNodes((prev) => prev.filter((n) => n.id !== featureId));
355
- setEdges((prev) => prev.filter((e) => e.source !== featureId && e.target !== featureId));
354
+ setNodes((currentNodes) => {
355
+ const remainingNodes = currentNodes.filter((n) => n.id !== featureId);
356
+ const remainingEdges = edges.filter(
357
+ (e) => e.source !== featureId && e.target !== featureId
358
+ );
359
+ const result = layoutWithDagre(remainingNodes, remainingEdges, {
360
+ direction: 'LR',
361
+ ranksep: 200,
362
+ nodesep: 60,
363
+ });
364
+ setEdges(result.edges);
365
+ return result.nodes;
366
+ });
356
367
  toast.success('Feature deleted successfully');
357
368
  router.refresh();
358
369
  } catch {
@@ -361,7 +372,7 @@ export function useControlCenterState(
361
372
  setIsDeleting(false);
362
373
  }
363
374
  },
364
- [router]
375
+ [router, edges]
365
376
  );
366
377
 
367
378
  const handleDeleteRepository = useCallback(
@@ -430,23 +441,23 @@ export function useControlCenterState(
430
441
  .pop() ?? path;
431
442
 
432
443
  // Optimistic UI: add node immediately
444
+ let savedAddRepoY = 0;
433
445
  setNodes((currentNodes) => {
434
446
  const repoNodes = currentNodes.filter((n) => n.type === 'repositoryNode');
435
447
  const addRepoNode = currentNodes.find((n) => n.type === 'addRepositoryNode');
436
448
 
437
- // Place in the repo column, below the last existing repo node
449
+ // Save addRepoNode's original Y for rollback on error
450
+ if (addRepoNode) savedAddRepoY = addRepoNode.position.y;
451
+
452
+ // Place in the repo column, at the addRepoNode's current position
438
453
  const repoX = repoNodes[0]?.position.x ?? addRepoNode?.position.x ?? 50;
439
454
  const repoHeight = 50; // repositoryNode height
440
455
  const gap = 15; // match dagre nodesep
441
456
 
442
- const lastRepoBottomY =
443
- repoNodes.length > 0
444
- ? Math.max(...repoNodes.map((n) => n.position.y)) + repoHeight
445
- : addRepoNode
446
- ? addRepoNode.position.y
447
- : 0;
448
-
449
- const position = { x: repoX, y: lastRepoBottomY + gap };
457
+ const position = {
458
+ x: repoX,
459
+ y: addRepoNode ? addRepoNode.position.y : 0,
460
+ };
450
461
 
451
462
  const newNode = {
452
463
  id: tempId,
@@ -455,8 +466,10 @@ export function useControlCenterState(
455
466
  data: { name: repoName, repositoryPath: path, id: tempId },
456
467
  } as CanvasNodeType;
457
468
 
458
- // Move addRepo button below the new node
459
- const addRepoY = position.y + repoHeight + gap;
469
+ // Shift addRepo button down by exactly one slot
470
+ const addRepoY = addRepoNode
471
+ ? addRepoNode.position.y + repoHeight + gap
472
+ : position.y + repoHeight + gap;
460
473
 
461
474
  return currentNodes
462
475
  .map((n) =>
@@ -469,8 +482,16 @@ export function useControlCenterState(
469
482
  addRepository({ path, name: repoName })
470
483
  .then((result) => {
471
484
  if (result.error) {
472
- // Rollback optimistic node
473
- setNodes((prev) => prev.filter((n) => n.id !== tempId));
485
+ // Rollback optimistic node and restore addRepoNode position
486
+ setNodes((prev) =>
487
+ prev
488
+ .filter((n) => n.id !== tempId)
489
+ .map((n) =>
490
+ n.type === 'addRepositoryNode'
491
+ ? { ...n, position: { ...n.position, y: savedAddRepoY } }
492
+ : n
493
+ )
494
+ );
474
495
  toast.error(result.error);
475
496
  return;
476
497
  }
@@ -501,7 +522,16 @@ export function useControlCenterState(
501
522
  router.refresh();
502
523
  })
503
524
  .catch(() => {
504
- setNodes((prev) => prev.filter((n) => n.id !== tempId));
525
+ // Rollback optimistic node and restore addRepoNode position
526
+ setNodes((prev) =>
527
+ prev
528
+ .filter((n) => n.id !== tempId)
529
+ .map((n) =>
530
+ n.type === 'addRepositoryNode'
531
+ ? { ...n, position: { ...n.position, y: savedAddRepoY } }
532
+ : n
533
+ )
534
+ );
505
535
  toast.error('Failed to add repository');
506
536
  });
507
537
  },
@@ -15,7 +15,7 @@ const currentPort = parseInt(process.env.PORT, 10) || 3000
15
15
  const hostname = process.env.HOSTNAME || '0.0.0.0'
16
16
 
17
17
  let keepAliveTimeout = parseInt(process.env.KEEP_ALIVE_TIMEOUT, 10)
18
- const nextConfig = {"env":{"NEXT_PUBLIC_SHEP_VERSION":"1.45.0","NEXT_PUBLIC_SHEP_PACKAGE_NAME":"@shepai/cli","NEXT_PUBLIC_SHEP_DESCRIPTION":"Autonomous AI Native SDLC Platform - Automate the development cycle from idea to deploy"},"webpack":null,"typescript":{"ignoreBuildErrors":false},"typedRoutes":true,"distDir":"./.next","cleanDistDir":true,"assetPrefix":"","cacheMaxMemorySize":52428800,"configOrigin":"next.config.ts","useFileSystemPublicRoutes":true,"generateEtags":true,"pageExtensions":["tsx","ts","jsx","js"],"poweredByHeader":true,"compress":true,"images":{"deviceSizes":[640,750,828,1080,1200,1920,2048,3840],"imageSizes":[32,48,64,96,128,256,384],"path":"/_next/image","loader":"default","loaderFile":"","domains":[],"disableStaticImages":false,"minimumCacheTTL":14400,"formats":["image/webp"],"maximumRedirects":3,"maximumResponseBody":50000000,"dangerouslyAllowLocalIP":false,"dangerouslyAllowSVG":false,"contentSecurityPolicy":"script-src 'none'; frame-src 'none'; sandbox;","contentDispositionType":"attachment","localPatterns":[{"pathname":"**","search":""}],"remotePatterns":[],"qualities":[75],"unoptimized":false},"devIndicators":{"position":"bottom-left"},"onDemandEntries":{"maxInactiveAge":60000,"pagesBufferLength":5},"basePath":"","sassOptions":{},"trailingSlash":false,"i18n":null,"productionBrowserSourceMaps":false,"excludeDefaultMomentLocales":true,"reactProductionProfiling":false,"reactStrictMode":null,"reactMaxHeadersLength":6000,"httpAgentOptions":{"keepAlive":true},"logging":{},"compiler":{},"expireTime":31536000,"staticPageGenerationTimeout":60,"output":"standalone","modularizeImports":{"@mui/icons-material":{"transform":"@mui/icons-material/{{member}}"},"lodash":{"transform":"lodash/{{member}}"}},"outputFileTracingRoot":"/home/runner/work/cli/cli","cacheComponents":false,"cacheLife":{"default":{"stale":300,"revalidate":900,"expire":4294967294},"seconds":{"stale":30,"revalidate":1,"expire":60},"minutes":{"stale":300,"revalidate":60,"expire":3600},"hours":{"stale":300,"revalidate":3600,"expire":86400},"days":{"stale":300,"revalidate":86400,"expire":604800},"weeks":{"stale":300,"revalidate":604800,"expire":2592000},"max":{"stale":300,"revalidate":2592000,"expire":31536000}},"cacheHandlers":{},"experimental":{"useSkewCookie":false,"cssChunking":true,"multiZoneDraftMode":false,"appNavFailHandling":false,"prerenderEarlyExit":true,"serverMinification":true,"linkNoTouchStart":false,"caseSensitiveRoutes":false,"dynamicOnHover":false,"preloadEntriesOnStart":true,"clientRouterFilter":true,"clientRouterFilterRedirects":false,"fetchCacheKeyPrefix":"","proxyPrefetch":"flexible","optimisticClientCache":true,"manualClientBasePath":false,"cpus":3,"memoryBasedWorkersCount":false,"imgOptConcurrency":null,"imgOptTimeoutInSeconds":7,"imgOptMaxInputPixels":268402689,"imgOptSequentialRead":null,"imgOptSkipMetadata":null,"isrFlushToDisk":true,"workerThreads":false,"optimizeCss":false,"nextScriptWorkers":false,"scrollRestoration":false,"externalDir":false,"disableOptimizedLoading":false,"gzipSize":true,"craCompat":false,"esmExternals":true,"fullySpecified":false,"swcTraceProfiling":false,"forceSwcTransforms":false,"largePageDataBytes":128000,"typedEnv":false,"parallelServerCompiles":false,"parallelServerBuildTraces":false,"ppr":false,"authInterrupts":false,"webpackMemoryOptimizations":false,"optimizeServerReact":true,"viewTransition":false,"removeUncaughtErrorAndRejectionListeners":false,"validateRSCRequestHeaders":false,"staleTimes":{"dynamic":0,"static":300},"reactDebugChannel":false,"serverComponentsHmrCache":true,"staticGenerationMaxConcurrency":8,"staticGenerationMinPagesPerWorker":25,"transitionIndicator":false,"inlineCss":false,"useCache":false,"globalNotFound":false,"browserDebugInfoInTerminal":false,"lockDistDir":true,"isolatedDevBuild":true,"proxyClientMaxBodySize":10485760,"hideLogsAfterAbort":false,"mcpServer":true,"turbopackFileSystemCacheForDev":true,"turbopackFileSystemCacheForBuild":false,"turbopackInferModuleSideEffects":false,"optimizePackageImports":["lucide-react","date-fns","lodash-es","ramda","antd","react-bootstrap","ahooks","@ant-design/icons","@headlessui/react","@headlessui-float/react","@heroicons/react/20/solid","@heroicons/react/24/solid","@heroicons/react/24/outline","@visx/visx","@tremor/react","rxjs","@mui/material","@mui/icons-material","recharts","react-use","effect","@effect/schema","@effect/platform","@effect/platform-node","@effect/platform-browser","@effect/platform-bun","@effect/sql","@effect/sql-mssql","@effect/sql-mysql2","@effect/sql-pg","@effect/sql-sqlite-node","@effect/sql-sqlite-bun","@effect/sql-sqlite-wasm","@effect/sql-sqlite-react-native","@effect/rpc","@effect/rpc-http","@effect/typeclass","@effect/experimental","@effect/opentelemetry","@material-ui/core","@material-ui/icons","@tabler/icons-react","mui-core","react-icons/ai","react-icons/bi","react-icons/bs","react-icons/cg","react-icons/ci","react-icons/di","react-icons/fa","react-icons/fa6","react-icons/fc","react-icons/fi","react-icons/gi","react-icons/go","react-icons/gr","react-icons/hi","react-icons/hi2","react-icons/im","react-icons/io","react-icons/io5","react-icons/lia","react-icons/lib","react-icons/lu","react-icons/md","react-icons/pi","react-icons/ri","react-icons/rx","react-icons/si","react-icons/sl","react-icons/tb","react-icons/tfi","react-icons/ti","react-icons/vsc","react-icons/wi"],"trustHostHeader":false,"isExperimentalCompile":false},"htmlLimitedBots":"[\\w-]+-Google|Google-[\\w-]+|Chrome-Lighthouse|Slurp|DuckDuckBot|baiduspider|yandex|sogou|bitlybot|tumblr|vkShare|quora link preview|redditbot|ia_archiver|Bingbot|BingPreview|applebot|facebookexternalhit|facebookcatalog|Twitterbot|LinkedInBot|Slackbot|Discordbot|WhatsApp|SkypeUriPreview|Yeti|googleweblight","bundlePagesRouterDependencies":false,"configFileName":"next.config.ts","turbopack":{"root":"/home/runner/work/cli/cli"},"serverExternalPackages":["tsyringe","reflect-metadata","better-sqlite3"],"distDirRoot":".next"}
18
+ const nextConfig = {"env":{"NEXT_PUBLIC_SHEP_VERSION":"1.47.0","NEXT_PUBLIC_SHEP_PACKAGE_NAME":"@shepai/cli","NEXT_PUBLIC_SHEP_DESCRIPTION":"Autonomous AI Native SDLC Platform - Automate the development cycle from idea to deploy"},"webpack":null,"typescript":{"ignoreBuildErrors":false},"typedRoutes":true,"distDir":"./.next","cleanDistDir":true,"assetPrefix":"","cacheMaxMemorySize":52428800,"configOrigin":"next.config.ts","useFileSystemPublicRoutes":true,"generateEtags":true,"pageExtensions":["tsx","ts","jsx","js"],"poweredByHeader":true,"compress":true,"images":{"deviceSizes":[640,750,828,1080,1200,1920,2048,3840],"imageSizes":[32,48,64,96,128,256,384],"path":"/_next/image","loader":"default","loaderFile":"","domains":[],"disableStaticImages":false,"minimumCacheTTL":14400,"formats":["image/webp"],"maximumRedirects":3,"maximumResponseBody":50000000,"dangerouslyAllowLocalIP":false,"dangerouslyAllowSVG":false,"contentSecurityPolicy":"script-src 'none'; frame-src 'none'; sandbox;","contentDispositionType":"attachment","localPatterns":[{"pathname":"**","search":""}],"remotePatterns":[],"qualities":[75],"unoptimized":false},"devIndicators":{"position":"bottom-left"},"onDemandEntries":{"maxInactiveAge":60000,"pagesBufferLength":5},"basePath":"","sassOptions":{},"trailingSlash":false,"i18n":null,"productionBrowserSourceMaps":false,"excludeDefaultMomentLocales":true,"reactProductionProfiling":false,"reactStrictMode":null,"reactMaxHeadersLength":6000,"httpAgentOptions":{"keepAlive":true},"logging":{},"compiler":{},"expireTime":31536000,"staticPageGenerationTimeout":60,"output":"standalone","modularizeImports":{"@mui/icons-material":{"transform":"@mui/icons-material/{{member}}"},"lodash":{"transform":"lodash/{{member}}"}},"outputFileTracingRoot":"/home/runner/work/cli/cli","cacheComponents":false,"cacheLife":{"default":{"stale":300,"revalidate":900,"expire":4294967294},"seconds":{"stale":30,"revalidate":1,"expire":60},"minutes":{"stale":300,"revalidate":60,"expire":3600},"hours":{"stale":300,"revalidate":3600,"expire":86400},"days":{"stale":300,"revalidate":86400,"expire":604800},"weeks":{"stale":300,"revalidate":604800,"expire":2592000},"max":{"stale":300,"revalidate":2592000,"expire":31536000}},"cacheHandlers":{},"experimental":{"useSkewCookie":false,"cssChunking":true,"multiZoneDraftMode":false,"appNavFailHandling":false,"prerenderEarlyExit":true,"serverMinification":true,"linkNoTouchStart":false,"caseSensitiveRoutes":false,"dynamicOnHover":false,"preloadEntriesOnStart":true,"clientRouterFilter":true,"clientRouterFilterRedirects":false,"fetchCacheKeyPrefix":"","proxyPrefetch":"flexible","optimisticClientCache":true,"manualClientBasePath":false,"cpus":3,"memoryBasedWorkersCount":false,"imgOptConcurrency":null,"imgOptTimeoutInSeconds":7,"imgOptMaxInputPixels":268402689,"imgOptSequentialRead":null,"imgOptSkipMetadata":null,"isrFlushToDisk":true,"workerThreads":false,"optimizeCss":false,"nextScriptWorkers":false,"scrollRestoration":false,"externalDir":false,"disableOptimizedLoading":false,"gzipSize":true,"craCompat":false,"esmExternals":true,"fullySpecified":false,"swcTraceProfiling":false,"forceSwcTransforms":false,"largePageDataBytes":128000,"typedEnv":false,"parallelServerCompiles":false,"parallelServerBuildTraces":false,"ppr":false,"authInterrupts":false,"webpackMemoryOptimizations":false,"optimizeServerReact":true,"viewTransition":false,"removeUncaughtErrorAndRejectionListeners":false,"validateRSCRequestHeaders":false,"staleTimes":{"dynamic":0,"static":300},"reactDebugChannel":false,"serverComponentsHmrCache":true,"staticGenerationMaxConcurrency":8,"staticGenerationMinPagesPerWorker":25,"transitionIndicator":false,"inlineCss":false,"useCache":false,"globalNotFound":false,"browserDebugInfoInTerminal":false,"lockDistDir":true,"isolatedDevBuild":true,"proxyClientMaxBodySize":10485760,"hideLogsAfterAbort":false,"mcpServer":true,"turbopackFileSystemCacheForDev":true,"turbopackFileSystemCacheForBuild":false,"turbopackInferModuleSideEffects":false,"optimizePackageImports":["lucide-react","date-fns","lodash-es","ramda","antd","react-bootstrap","ahooks","@ant-design/icons","@headlessui/react","@headlessui-float/react","@heroicons/react/20/solid","@heroicons/react/24/solid","@heroicons/react/24/outline","@visx/visx","@tremor/react","rxjs","@mui/material","@mui/icons-material","recharts","react-use","effect","@effect/schema","@effect/platform","@effect/platform-node","@effect/platform-browser","@effect/platform-bun","@effect/sql","@effect/sql-mssql","@effect/sql-mysql2","@effect/sql-pg","@effect/sql-sqlite-node","@effect/sql-sqlite-bun","@effect/sql-sqlite-wasm","@effect/sql-sqlite-react-native","@effect/rpc","@effect/rpc-http","@effect/typeclass","@effect/experimental","@effect/opentelemetry","@material-ui/core","@material-ui/icons","@tabler/icons-react","mui-core","react-icons/ai","react-icons/bi","react-icons/bs","react-icons/cg","react-icons/ci","react-icons/di","react-icons/fa","react-icons/fa6","react-icons/fc","react-icons/fi","react-icons/gi","react-icons/go","react-icons/gr","react-icons/hi","react-icons/hi2","react-icons/im","react-icons/io","react-icons/io5","react-icons/lia","react-icons/lib","react-icons/lu","react-icons/md","react-icons/pi","react-icons/ri","react-icons/rx","react-icons/si","react-icons/sl","react-icons/tb","react-icons/tfi","react-icons/ti","react-icons/vsc","react-icons/wi"],"trustHostHeader":false,"isExperimentalCompile":false},"htmlLimitedBots":"[\\w-]+-Google|Google-[\\w-]+|Chrome-Lighthouse|Slurp|DuckDuckBot|baiduspider|yandex|sogou|bitlybot|tumblr|vkShare|quora link preview|redditbot|ia_archiver|Bingbot|BingPreview|applebot|facebookexternalhit|facebookcatalog|Twitterbot|LinkedInBot|Slackbot|Discordbot|WhatsApp|SkypeUriPreview|Yeti|googleweblight","bundlePagesRouterDependencies":false,"configFileName":"next.config.ts","turbopack":{"root":"/home/runner/work/cli/cli"},"serverExternalPackages":["tsyringe","reflect-metadata","better-sqlite3"],"distDirRoot":".next"}
19
19
 
20
20
  process.env.__NEXT_PRIVATE_STANDALONE_CONFIG = JSON.stringify(nextConfig)
21
21