@shepai/cli 1.72.9-pr218.000ada0 → 1.72.9-pr220.99e5e13
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.
- package/dist/packages/core/src/application/use-cases/features/create/create-feature.use-case.d.ts +20 -1
- package/dist/packages/core/src/application/use-cases/features/create/create-feature.use-case.d.ts.map +1 -1
- package/dist/packages/core/src/application/use-cases/features/create/create-feature.use-case.js +68 -41
- package/dist/packages/core/src/application/use-cases/features/create/types.d.ts +10 -0
- package/dist/packages/core/src/application/use-cases/features/create/types.d.ts.map +1 -1
- package/dist/src/presentation/web/app/actions/create-feature.d.ts.map +1 -1
- package/dist/src/presentation/web/app/actions/create-feature.js +20 -2
- package/dist/src/presentation/web/app/actions/get-feature-metadata.d.ts +10 -0
- package/dist/src/presentation/web/app/actions/get-feature-metadata.d.ts.map +1 -0
- package/dist/src/presentation/web/app/actions/get-feature-metadata.js +14 -0
- package/dist/src/presentation/web/app/api/agent-events/route.d.ts.map +1 -1
- package/dist/src/presentation/web/app/api/agent-events/route.js +15 -0
- package/dist/src/presentation/web/components/common/add-repository-button/add-repository-button.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/add-repository-button/index.d.ts +2 -0
- package/dist/src/presentation/web/components/common/add-repository-button/index.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/{add-repository-node → add-repository-button}/index.js +0 -1
- package/dist/src/presentation/web/components/common/add-repository-button/pick-folder.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/control-center-drawer/create-drawer-client.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/control-center-drawer/create-drawer-client.js +13 -16
- package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.js +35 -16
- package/dist/src/presentation/web/components/common/index.d.ts +1 -2
- package/dist/src/presentation/web/components/common/index.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/index.js +1 -2
- package/dist/src/presentation/web/components/features/control-center/control-center-empty-state.js +1 -1
- package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map +1 -1
- package/dist/src/presentation/web/components/features/control-center/control-center-inner.js +25 -7
- package/dist/src/presentation/web/components/features/control-center/control-center.stories.d.ts +0 -11
- package/dist/src/presentation/web/components/features/control-center/control-center.stories.d.ts.map +1 -1
- package/dist/src/presentation/web/components/features/control-center/control-center.stories.js +2 -37
- package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts +8 -0
- package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts.map +1 -1
- package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js +143 -348
- package/dist/src/presentation/web/components/features/features-canvas/features-canvas.d.ts +2 -10
- package/dist/src/presentation/web/components/features/features-canvas/features-canvas.d.ts.map +1 -1
- package/dist/src/presentation/web/components/features/features-canvas/features-canvas.js +10 -42
- package/dist/src/presentation/web/components/features/features-canvas/features-canvas.stories.d.ts.map +1 -1
- package/dist/src/presentation/web/components/features/features-canvas/features-canvas.stories.js +0 -46
- package/dist/src/presentation/web/components/layouts/app-shell/app-shell.js +1 -1
- package/dist/src/presentation/web/hooks/use-graph-state.d.ts +42 -0
- package/dist/src/presentation/web/hooks/use-graph-state.d.ts.map +1 -0
- package/dist/src/presentation/web/hooks/use-graph-state.js +257 -0
- package/dist/src/presentation/web/hooks/use-notifications.d.ts +1 -5
- package/dist/src/presentation/web/hooks/use-notifications.d.ts.map +1 -1
- package/dist/src/presentation/web/hooks/use-notifications.js +1 -26
- package/dist/src/presentation/web/hooks/use-notifications.stories.d.ts.map +1 -1
- package/dist/src/presentation/web/hooks/use-notifications.stories.js +28 -38
- package/dist/src/presentation/web/lib/derive-graph.d.ts +52 -0
- package/dist/src/presentation/web/lib/derive-graph.d.ts.map +1 -0
- package/dist/src/presentation/web/lib/derive-graph.js +126 -0
- package/dist/src/presentation/web/lib/layout-with-dagre.d.ts.map +1 -1
- package/dist/src/presentation/web/lib/layout-with-dagre.js +0 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/build-manifest.json +2 -2
- package/web/.next/cache/.previewinfo +1 -1
- package/web/.next/cache/.rscinfo +1 -1
- package/web/.next/cache/.tsbuildinfo +1 -1
- package/web/.next/cache/config.json +3 -3
- package/web/.next/fallback-build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -3
- package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +34 -34
- package/web/.next/server/app/(dashboard)/@drawer/create/page.js +3 -3
- package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +40 -40
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js +3 -3
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +30 -30
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js +3 -3
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +34 -34
- package/web/.next/server/app/(dashboard)/create/page.js +3 -3
- package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +40 -40
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js +3 -3
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +30 -30
- package/web/.next/server/app/(dashboard)/page.js +3 -3
- package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +30 -30
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js +3 -3
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
- package/web/.next/server/app/skills/page.js.nft.json +1 -1
- package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
- package/web/.next/server/app/tools/page.js.nft.json +1 -1
- package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/version/page.js.nft.json +1 -1
- package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
- package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js +2 -2
- package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js.map +1 -1
- package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
- package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_816618e2.js +3 -0
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_816618e2.js.map +1 -0
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_ba2e050f.js +3 -0
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_ba2e050f.js.map +1 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__17a611f0._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__17a611f0._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__45dbf93d._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__45dbf93d._.js.map +1 -1
- package/web/.next/server/chunks/ssr/{[root-of-the-server]__cbae9863._.js → [root-of-the-server]__6258349c._.js} +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6258349c._.js.map +1 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6cbc1149._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6cbc1149._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js.map +1 -1
- package/web/.next/{standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__e5e09f72._.js → server/chunks/ssr/[root-of-the-server]__94a6d275._.js} +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__94a6d275._.js.map +1 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_0e9d6bf7._.js +1 -1
- package/web/.next/server/chunks/ssr/_0e9d6bf7._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_250a63ae._.js +3 -0
- package/web/.next/server/chunks/ssr/_250a63ae._.js.map +1 -0
- package/web/.next/server/chunks/ssr/_61351ed3._.js +1 -1
- package/web/.next/server/chunks/ssr/_61351ed3._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_8400c8bb._.js +1 -1
- package/web/.next/server/chunks/ssr/_8400c8bb._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_9f940437._.js +1 -1
- package/web/.next/server/chunks/ssr/_9f940437._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_9fd79ac5._.js +6 -0
- package/web/.next/server/chunks/ssr/_9fd79ac5._.js.map +1 -0
- package/web/.next/{standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_5ea689db._.js → server/chunks/ssr/src_presentation_web_2b91e4ec._.js} +2 -2
- package/web/.next/server/chunks/ssr/{src_presentation_web_7188a290._.js.map → src_presentation_web_2b91e4ec._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/{src_presentation_web_5ea689db._.js → src_presentation_web_5bcc1537._.js} +2 -2
- package/web/.next/server/chunks/ssr/{src_presentation_web_5ea689db._.js.map → src_presentation_web_5bcc1537._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_5955c012.js +3 -0
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_5955c012.js.map +1 -0
- package/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js +3 -0
- package/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js.map +1 -0
- package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_d5b0cb86._.js +8 -0
- package/web/.next/server/chunks/ssr/src_presentation_web_d5b0cb86._.js.map +1 -0
- package/web/.next/server/chunks/ssr/src_presentation_web_e626b63c._.js +8 -0
- package/web/.next/server/chunks/ssr/src_presentation_web_e626b63c._.js.map +1 -0
- package/web/.next/server/pages/500.html +2 -2
- package/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/server/server-reference-manifest.json +142 -142
- package/web/.next/standalone/src/presentation/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/src/presentation/web/.next/build-manifest.json +2 -2
- package/web/.next/standalone/src/presentation/web/.next/prerender-manifest.json +3 -3
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +34 -34
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page.js +3 -3
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +40 -40
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js +3 -3
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +30 -30
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js +3 -3
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +34 -34
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page.js +3 -3
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +40 -40
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page.js +3 -3
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +30 -30
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page.js +3 -3
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +30 -30
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js +3 -3
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
- package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page.js.nft.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
- package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page.js.nft.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/version/page.js.nft.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_816618e2.js +3 -0
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_ba2e050f.js +3 -0
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__17a611f0._.js +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__45dbf93d._.js +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/{[root-of-the-server]__cbae9863._.js → [root-of-the-server]__6258349c._.js} +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6cbc1149._.js +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
- package/web/.next/{server/chunks/ssr/[root-of-the-server]__e5e09f72._.js → standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__94a6d275._.js} +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_0e9d6bf7._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_250a63ae._.js +3 -0
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_61351ed3._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_8400c8bb._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_9f940437._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_9fd79ac5._.js +6 -0
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/{src_presentation_web_7188a290._.js → src_presentation_web_2b91e4ec._.js} +2 -2
- package/web/.next/{server/chunks/ssr/src_presentation_web_7188a290._.js → standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_5bcc1537._.js} +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_5955c012.js +3 -0
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js +3 -0
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_d5b0cb86._.js +8 -0
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_e626b63c._.js +8 -0
- package/web/.next/standalone/src/presentation/web/.next/server/pages/500.html +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.json +142 -142
- package/web/.next/standalone/src/presentation/web/CLAUDE.md +77 -33
- package/web/.next/standalone/src/presentation/web/app/actions/create-feature.ts +27 -2
- package/web/.next/standalone/src/presentation/web/app/actions/get-feature-metadata.ts +18 -0
- package/web/.next/standalone/src/presentation/web/app/api/agent-events/route.ts +17 -0
- package/web/.next/standalone/src/presentation/web/components/common/add-repository-button/index.ts +1 -0
- package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/create-drawer-client.tsx +15 -19
- package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.tsx +44 -18
- package/web/.next/standalone/src/presentation/web/components/common/index.ts +1 -6
- package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center-empty-state.tsx +1 -1
- package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center-inner.tsx +36 -29
- package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center.stories.tsx +2 -41
- package/web/.next/standalone/src/presentation/web/components/features/control-center/use-control-center-state.ts +175 -428
- package/web/.next/standalone/src/presentation/web/components/features/features-canvas/features-canvas.stories.tsx +0 -48
- package/web/.next/standalone/src/presentation/web/components/features/features-canvas/features-canvas.tsx +13 -59
- package/web/.next/standalone/src/presentation/web/components/layouts/app-shell/app-shell.tsx +1 -1
- package/web/.next/standalone/src/presentation/web/hooks/use-graph-state.ts +376 -0
- package/web/.next/standalone/src/presentation/web/hooks/use-notifications.stories.tsx +2 -30
- package/web/.next/standalone/src/presentation/web/hooks/use-notifications.ts +2 -36
- package/web/.next/standalone/src/presentation/web/lib/derive-graph.ts +178 -0
- package/web/.next/standalone/src/presentation/web/lib/layout-with-dagre.ts +0 -1
- package/web/.next/static/chunks/{99381b9c479936d5.js → 0b409f42a82665ec.js} +1 -1
- package/web/.next/static/chunks/0e035a073754dde7.js +1 -0
- package/web/.next/static/chunks/109d6898972d7b78.js +1 -0
- package/web/.next/static/chunks/{1249c7c8745b972e.js → 5238c7e71fc81e4d.js} +1 -1
- package/web/.next/static/chunks/6b4899963367ea96.js +4 -0
- package/web/.next/static/chunks/71c9ecdceac1939f.css +2 -0
- package/web/.next/static/chunks/752846e217931a38.js +1 -0
- package/web/.next/static/chunks/9f9e6f5083600784.js +1 -0
- package/web/.next/static/chunks/ae00da647a8354b9.js +1 -0
- package/web/.next/static/chunks/cb41849bcfaa304e.js +1 -0
- package/web/.next/static/chunks/dc23a5773f5cd610.js +1 -0
- package/web/.next/trace +1 -1
- package/web/.next/trace-build +1 -1
- package/dist/src/presentation/web/components/common/add-repository-node/add-repository-button.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node-config.d.ts +0 -7
- package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node-config.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node-config.js +0 -1
- package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.d.ts +0 -6
- package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.js +0 -23
- package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.stories.d.ts +0 -10
- package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.stories.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.stories.js +0 -33
- package/dist/src/presentation/web/components/common/add-repository-node/index.d.ts +0 -4
- package/dist/src/presentation/web/components/common/add-repository-node/index.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/add-repository-node/pick-folder.d.ts.map +0 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_3a02d8a5.js +0 -3
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_3a02d8a5.js.map +0 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_a3044852.js +0 -3
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_a3044852.js.map +0 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__cbae9863._.js.map +0 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__e5e09f72._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_6e08ce21._.js +0 -3
- package/web/.next/server/chunks/ssr/_6e08ce21._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_6ef06ca9._.js +0 -6
- package/web/.next/server/chunks/ssr/_6ef06ca9._.js.map +0 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_0a49d9ee._.js +0 -8
- package/web/.next/server/chunks/ssr/src_presentation_web_0a49d9ee._.js.map +0 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_79b0305a._.js +0 -8
- package/web/.next/server/chunks/ssr/src_presentation_web_79b0305a._.js.map +0 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_cc16bd01.js +0 -3
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_cc16bd01.js.map +0 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_app_(dashboard)_get-graph-data_ts_b018483e._.js +0 -3
- package/web/.next/server/chunks/ssr/src_presentation_web_app_(dashboard)_get-graph-data_ts_b018483e._.js.map +0 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_3a02d8a5.js +0 -3
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_a3044852.js +0 -3
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_6e08ce21._.js +0 -3
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_6ef06ca9._.js +0 -6
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_0a49d9ee._.js +0 -8
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_79b0305a._.js +0 -8
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_cc16bd01.js +0 -3
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_app_(dashboard)_get-graph-data_ts_b018483e._.js +0 -3
- package/web/.next/standalone/src/presentation/web/components/common/add-repository-node/add-repository-node-config.ts +0 -8
- package/web/.next/standalone/src/presentation/web/components/common/add-repository-node/add-repository-node.stories.tsx +0 -70
- package/web/.next/standalone/src/presentation/web/components/common/add-repository-node/add-repository-node.tsx +0 -49
- package/web/.next/standalone/src/presentation/web/components/common/add-repository-node/index.ts +0 -3
- package/web/.next/static/chunks/0eeb65b8300fb7cd.js +0 -1
- package/web/.next/static/chunks/1c15ab8668ecf473.js +0 -1
- package/web/.next/static/chunks/2cf89d17ee7717a6.js +0 -4
- package/web/.next/static/chunks/4a8df922364596f6.js +0 -1
- package/web/.next/static/chunks/981b8df9ad045742.js +0 -1
- package/web/.next/static/chunks/9be617fc61b893f2.js +0 -1
- package/web/.next/static/chunks/a676f15c03fa66b9.js +0 -1
- package/web/.next/static/chunks/acce8a15d6896dea.css +0 -2
- package/web/.next/static/chunks/b579fd590a79bf35.js +0 -1
- /package/dist/src/presentation/web/components/common/{add-repository-node → add-repository-button}/add-repository-button.d.ts +0 -0
- /package/dist/src/presentation/web/components/common/{add-repository-node → add-repository-button}/add-repository-button.js +0 -0
- /package/dist/src/presentation/web/components/common/{add-repository-node → add-repository-button}/pick-folder.d.ts +0 -0
- /package/dist/src/presentation/web/components/common/{add-repository-node → add-repository-button}/pick-folder.js +0 -0
- /package/web/.next/standalone/src/presentation/web/components/common/{add-repository-node → add-repository-button}/add-repository-button.tsx +0 -0
- /package/web/.next/standalone/src/presentation/web/components/common/{add-repository-node → add-repository-button}/pick-folder.ts +0 -0
- /package/web/.next/static/{nnR12fAsrSagAfeV6xj6D → JYBKfZ0ROsNn817Id2TJ7}/_buildManifest.js +0 -0
- /package/web/.next/static/{nnR12fAsrSagAfeV6xj6D → JYBKfZ0ROsNn817Id2TJ7}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{nnR12fAsrSagAfeV6xj6D → JYBKfZ0ROsNn817Id2TJ7}/_ssgManifest.js +0 -0
|
@@ -37,6 +37,66 @@ pnpm dev:cli ui
|
|
|
37
37
|
| WebSocket upgrades | Forwarded (HMR works) | NOT forwarded in `WebServerService` |
|
|
38
38
|
| Next.js mode | Always dev | dev when run from source, prod when installed |
|
|
39
39
|
|
|
40
|
+
## Graph State Architecture (Domain-Model-Driven)
|
|
41
|
+
|
|
42
|
+
All canvas node/edge state is managed through **domain Maps as the single source of truth**.
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
Server (layout.tsx) Client (useGraphState)
|
|
46
|
+
┌──────────────────┐ ┌─────────────────────────────┐
|
|
47
|
+
│ initialNodes[] │──parseMaps──→ │ Domain Maps (source of truth)│
|
|
48
|
+
│ initialEdges[] │ │ featureMap: Map<id, Entry> │
|
|
49
|
+
└──────────────────┘ │ repoMap: Map<id, Entry> │
|
|
50
|
+
│ pendingMap: Map<id, Entry> │
|
|
51
|
+
SSE Events │ │
|
|
52
|
+
┌──────────────────┐ └──────────┬──────────────────┘
|
|
53
|
+
│ NotificationEvent│──updateFeature──→ │
|
|
54
|
+
└──────────────────┘ │ deriveGraph() + layoutWithDagre()
|
|
55
|
+
▼
|
|
56
|
+
Optimistic UI ┌─────────────────────────────┐
|
|
57
|
+
┌──────────────────┐ │ Derived (read-only) │
|
|
58
|
+
│ shep:feature- │──addPending───→ │ nodes: CanvasNodeType[] │
|
|
59
|
+
│ created event │ │ edges: Edge[] │
|
|
60
|
+
└──────────────────┘ │ (callbacks + layout applied)│
|
|
61
|
+
└─────────────────────────────┘
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Key Files
|
|
65
|
+
|
|
66
|
+
| File | Purpose |
|
|
67
|
+
| ---------------------------------------------------------------- | -------------------------------------------------------------------- |
|
|
68
|
+
| `lib/derive-graph.ts` | Pure function: domain Maps → nodes + edges (with callbacks injected) |
|
|
69
|
+
| `hooks/use-graph-state.ts` | Hook: domain Maps state + reconciliation + derivation + layout |
|
|
70
|
+
| `components/features/control-center/use-control-center-state.ts` | Wraps useGraphState, handles server actions + SSE events |
|
|
71
|
+
| `components/features/control-center/control-center-inner.tsx` | Wires callbacks via `setCallbacks()`, passes nodes/edges to canvas |
|
|
72
|
+
| `components/features/features-canvas/features-canvas.tsx` | Renders React Flow; only adds `selectedFeatureId` highlighting |
|
|
73
|
+
|
|
74
|
+
### Design Principles
|
|
75
|
+
|
|
76
|
+
1. **Domain Maps are the source of truth** — all mutations go through Maps (featureMap, repoMap, pendingMap)
|
|
77
|
+
2. **Edges are derived, never stored** — repo→feature edges from `repositoryPath` matching, dep edges from `parentNodeId`
|
|
78
|
+
3. **Callbacks via ref** — `callbacksRef` + stable `useMemo([])` wrapper prevents circular re-renders
|
|
79
|
+
4. **Layout in derivation** — `layoutWithDagre` runs inside `useMemo` after `deriveGraph()`
|
|
80
|
+
5. **`onNodesChange` is a no-op** — since `nodesDraggable=false`, we ignore all React Flow changes
|
|
81
|
+
|
|
82
|
+
### Mutation Paths
|
|
83
|
+
|
|
84
|
+
| Operation | Flow |
|
|
85
|
+
| ----------------- | ------------------------------------------------------------------------------------------------------- |
|
|
86
|
+
| SSE event | `useAgentEventsContext` → `updateFeature(nodeId, {state, lifecycle})` |
|
|
87
|
+
| Server prop sync | `useEffect` watches `initialNodeKey` → `reconcile(newNodes, newEdges)` |
|
|
88
|
+
| Optimistic create | `shep:feature-created` event → `createFeatureNode()` → `addPendingFeature()` |
|
|
89
|
+
| Delete feature | `handleDeleteFeature()` → `removeFeature()` → server action → `restoreFeature()` on error |
|
|
90
|
+
| Add repository | `handleAddRepository()` → `addRepository(tempId)` → server action → `replaceRepository(tempId, realId)` |
|
|
91
|
+
|
|
92
|
+
### Reconciliation
|
|
93
|
+
|
|
94
|
+
`reconcile(newNodes, newEdges)` merges server data into domain Maps:
|
|
95
|
+
|
|
96
|
+
- Replaces featureMap and repoMap with fresh server data
|
|
97
|
+
- Preserves pending (creating) features in featureMap unless matched by `name + repositoryPath`
|
|
98
|
+
- Cleans pendingMap entries that now have a real counterpart in server data
|
|
99
|
+
|
|
40
100
|
## SSE / Real-Time Update Architecture
|
|
41
101
|
|
|
42
102
|
```
|
|
@@ -45,8 +105,8 @@ DB polling (500ms) -> /api/agent-events (SSE route)
|
|
|
45
105
|
-> postMessage to all browser tabs
|
|
46
106
|
-> useAgentEvents hook (hooks/use-agent-events.ts)
|
|
47
107
|
-> AgentEventsProvider context (hooks/agent-events-provider.tsx)
|
|
48
|
-
-> useControlCenterState (
|
|
49
|
-
->
|
|
108
|
+
-> useControlCenterState (SSE effect calls updateFeature)
|
|
109
|
+
-> domain Maps update -> derivation -> React Flow canvas
|
|
50
110
|
```
|
|
51
111
|
|
|
52
112
|
### SSE Key Files
|
|
@@ -58,18 +118,9 @@ DB polling (500ms) -> /api/agent-events (SSE route)
|
|
|
58
118
|
| `public/agent-events-sw.js` | Service Worker: multiplexes single SSE to all tabs |
|
|
59
119
|
| `hooks/use-agent-events.ts` | Client hook: registers with SW, receives events |
|
|
60
120
|
| `hooks/agent-events-provider.tsx` | React context wrapping `useAgentEvents` |
|
|
61
|
-
| `components/features/control-center/use-control-center-state.ts` |
|
|
121
|
+
| `components/features/control-center/use-control-center-state.ts` | SSE effect calls `updateFeature()` on domain Maps |
|
|
62
122
|
| `components/common/feature-node/derive-feature-state.ts` | Maps event types/phases to UI state/lifecycle |
|
|
63
123
|
|
|
64
|
-
### SSE Event Flow Details
|
|
65
|
-
|
|
66
|
-
1. **SSE route** seeds a per-connection cache on first poll (no events emitted for initial state)
|
|
67
|
-
2. Subsequent polls compare DB state against cache, emit only deltas
|
|
68
|
-
3. **Service Worker** connects to `/api/agent-events` via EventSource on first subscriber
|
|
69
|
-
4. SW broadcasts `{ type: 'notification', data }` to all tabs via `clients.matchAll()`
|
|
70
|
-
5. **useControlCenterState** effect processes new events, calls `setNodes()` to update node state/lifecycle
|
|
71
|
-
6. **Polling fallback** (5s interval) fetches via `fetchGraphData()` server action for active features
|
|
72
|
-
|
|
73
124
|
### SSE Lifecycle Mappings
|
|
74
125
|
|
|
75
126
|
The SSE route maps `SdlcLifecycle` enum -> agent graph node names -> client `FeatureLifecyclePhase`:
|
|
@@ -94,21 +145,17 @@ React Flow v12 (`@xyflow/react` ^12.10.0) has specific behaviors that affect rea
|
|
|
94
145
|
- **Internal Zustand store**: React Flow syncs props to an internal `nodeLookup` Map via `StoreUpdater`
|
|
95
146
|
- **Memoized NodeWrapper**: Each node is wrapped in `memo(NodeWrapper)` which subscribes to store via `useStore(selector, shallow)`
|
|
96
147
|
- **NodeWrapper gets node from store**, NOT from props directly. It does `s.nodeLookup.get(id)` with `shallow` comparison
|
|
97
|
-
- **Single ReactFlowProvider**: There must be exactly ONE `ReactFlowProvider` ancestor. Multiple nested providers create separate stores
|
|
148
|
+
- **Single ReactFlowProvider**: There must be exactly ONE `ReactFlowProvider` ancestor. Multiple nested providers create separate stores
|
|
98
149
|
|
|
99
|
-
### Circular Update Loop
|
|
150
|
+
### Circular Update Loop Prevention
|
|
100
151
|
|
|
101
|
-
|
|
102
|
-
(adding callbacks to `data`). StoreUpdater sees different references and emits `replace` changes via
|
|
103
|
-
`onNodesChange`. If `onNodesChange` applies these 'replace' changes back to state via `applyNodeChanges`,
|
|
104
|
-
it overwrites SSE-driven state updates with stale data from the previous render, AND triggers another
|
|
105
|
-
`enrichedNodes` recalculation → infinite loop (110+ renders on page load).
|
|
152
|
+
With the domain-model-driven architecture, the circular update loop is prevented by design:
|
|
106
153
|
|
|
107
|
-
**
|
|
108
|
-
|
|
109
|
-
|
|
154
|
+
1. **Callbacks are stable** — injected via `callbacksRef` + stable `useMemo([])` wrapper, so node objects don't change when callbacks change
|
|
155
|
+
2. **`onNodesChange` is a no-op** — domain Maps are the source of truth, React Flow's internal changes are ignored
|
|
156
|
+
3. **`enrichedNodes` only adds `selectedFeatureId`** — no callback injection in FeaturesCanvas
|
|
110
157
|
|
|
111
|
-
**NEVER**
|
|
158
|
+
**NEVER** make `onNodesChange` apply `replace` changes back to state.
|
|
112
159
|
|
|
113
160
|
## Testing Real-Time Updates
|
|
114
161
|
|
|
@@ -124,24 +171,21 @@ our state remains the source of truth.
|
|
|
124
171
|
|
|
125
172
|
When a user creates a feature, the UI uses optimistic updates:
|
|
126
173
|
|
|
127
|
-
1. **Create drawer** dispatches `shep:feature-created` →
|
|
174
|
+
1. **Create drawer** dispatches `shep:feature-created` → `createFeatureNode()` → `addPendingFeature()` adds temp node to pendingMap
|
|
128
175
|
2. Drawer closes immediately via `router.push('/')`
|
|
129
176
|
3. Server action `createFeature` runs in background → agent starts
|
|
130
|
-
4. SSE events arrive with real feature ID (`feat-<uuid>`) —
|
|
131
|
-
5. **
|
|
177
|
+
4. SSE events arrive with real feature ID (`feat-<uuid>`) — these update featureMap via `updateFeature()`
|
|
178
|
+
5. **Server prop sync** (rerender with new initialNodes) → `reconcile()` detects matching real feature → removes pending from both featureMap and pendingMap
|
|
132
179
|
|
|
133
180
|
### Key constraints
|
|
134
181
|
|
|
135
|
-
- **Temp node IDs don't match SSE events**: Optimistic nodes use `feature-<ts>` IDs, server uses `feat-<uuid>`.
|
|
136
|
-
|
|
137
|
-
- **
|
|
138
|
-
causes the drawer to flash open because `router.push('/')` is async and pathname hasn't changed yet.
|
|
139
|
-
- **Parallel routes preserve state**: The `CreateDrawerClient` component is NOT unmounted on navigation.
|
|
140
|
-
`isOpen` is derived from pathname, not mount/unmount lifecycle.
|
|
182
|
+
- **Temp node IDs don't match SSE events**: Optimistic nodes use `feature-<ts>` IDs, server uses `feat-<uuid>`. SSE events call `updateFeature('feat-<uuid>', ...)` which doesn't match the temp ID. The reconcile on next server prop update handles the swap.
|
|
183
|
+
- **Create drawer `isSubmitting`**: Must NOT reset until pathname leaves `/create`. Resetting in `.finally()` causes the drawer to flash open because `router.push('/')` is async and pathname hasn't changed yet.
|
|
184
|
+
- **Parallel routes preserve state**: The `CreateDrawerClient` component is NOT unmounted on navigation. `isOpen` is derived from pathname, not mount/unmount lifecycle.
|
|
141
185
|
|
|
142
186
|
## Common Pitfalls
|
|
143
187
|
|
|
144
188
|
- **No events when nothing changes**: SSE only emits deltas. If no feature state changes, no events are sent. This is correct behavior.
|
|
145
|
-
- **Polling fallback dependency**: The 5s polling `useEffect` depends on a derived `hasActiveFeature` boolean (not `nodes` directly). Using `[nodes]` as a dependency would reset the interval on every node change, potentially preventing it from ever firing.
|
|
146
189
|
- **Storybook mocks**: New server actions need mocks in `.storybook/mocks/app/actions/` or Storybook build breaks.
|
|
147
190
|
- **`output: 'standalone'`** in next.config.ts: Production builds use standalone mode. Route handlers are bundled differently.
|
|
191
|
+
- **Map insertion order affects dagre layout**: After remove+restore of a feature, the Map insertion order may change, causing slightly different dagre positions. This is expected behavior — don't write tests asserting exact position preservation across remove/restore cycles.
|
|
@@ -72,15 +72,40 @@ export async function createFeature(
|
|
|
72
72
|
|
|
73
73
|
try {
|
|
74
74
|
const createFeatureUseCase = resolve<CreateFeatureUseCase>('CreateFeatureUseCase');
|
|
75
|
-
|
|
75
|
+
|
|
76
|
+
// Phase 1 (fast): create DB record with real UUID — returns immediately
|
|
77
|
+
const { feature, shouldSpawn } = await createFeatureUseCase.createRecord({
|
|
76
78
|
userInput,
|
|
77
79
|
repositoryPath,
|
|
78
80
|
approvalGates: gates,
|
|
79
81
|
push: push ?? false,
|
|
80
82
|
openPr: openPr ?? false,
|
|
81
83
|
...(parentId ? { parentId } : {}),
|
|
84
|
+
name,
|
|
85
|
+
description,
|
|
82
86
|
});
|
|
83
|
-
|
|
87
|
+
|
|
88
|
+
// Phase 2 (background): metadata generation, worktree, spec, agent spawn
|
|
89
|
+
// Fire-and-forget — the UI gets the real feature ID immediately
|
|
90
|
+
createFeatureUseCase
|
|
91
|
+
.initializeAndSpawn(
|
|
92
|
+
feature,
|
|
93
|
+
{
|
|
94
|
+
userInput,
|
|
95
|
+
repositoryPath,
|
|
96
|
+
approvalGates: gates,
|
|
97
|
+
push: push ?? false,
|
|
98
|
+
openPr: openPr ?? false,
|
|
99
|
+
...(parentId ? { parentId } : {}),
|
|
100
|
+
},
|
|
101
|
+
shouldSpawn
|
|
102
|
+
)
|
|
103
|
+
.catch((err: unknown) => {
|
|
104
|
+
// eslint-disable-next-line no-console
|
|
105
|
+
console.error('[createFeature] initializeAndSpawn failed:', err);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
return { feature };
|
|
84
109
|
} catch (error: unknown) {
|
|
85
110
|
const message = error instanceof Error ? error.message : 'Failed to create feature';
|
|
86
111
|
return { error: message };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use server';
|
|
2
|
+
|
|
3
|
+
import { resolve } from '@/lib/server-container';
|
|
4
|
+
import type { IFeatureRepository } from '@shepai/core/application/ports/output/repositories/feature-repository.interface';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lightweight server action to fetch a single feature's name and description.
|
|
8
|
+
* Used by the SSE effect to update node metadata after AI metadata generation
|
|
9
|
+
* without a full graph reconcile (which can overwrite SSE-driven state).
|
|
10
|
+
*/
|
|
11
|
+
export async function getFeatureMetadata(
|
|
12
|
+
featureId: string
|
|
13
|
+
): Promise<{ name: string; description: string } | null> {
|
|
14
|
+
const featureRepo = resolve<IFeatureRepository>('IFeatureRepository');
|
|
15
|
+
const feature = await featureRepo.findById(featureId);
|
|
16
|
+
if (!feature) return null;
|
|
17
|
+
return { name: feature.name, description: feature.description };
|
|
18
|
+
}
|
|
@@ -191,6 +191,23 @@ export function GET(request: Request): Response {
|
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
+
// Check for feature name change (AI metadata generation updates name)
|
|
195
|
+
if (prev.featureName !== feature.name) {
|
|
196
|
+
prev.featureName = feature.name;
|
|
197
|
+
const nodeName =
|
|
198
|
+
LIFECYCLE_TO_NODE[feature.lifecycle as SdlcLifecycle] ?? 'requirements';
|
|
199
|
+
emitEvent({
|
|
200
|
+
eventType: NotificationEventType.PhaseCompleted,
|
|
201
|
+
agentRunId: run.id,
|
|
202
|
+
featureId: feature.id,
|
|
203
|
+
featureName: feature.name,
|
|
204
|
+
phaseName: nodeName,
|
|
205
|
+
message: `Feature metadata updated`,
|
|
206
|
+
severity: NotificationSeverity.Info,
|
|
207
|
+
timestamp: new Date().toISOString(),
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
194
211
|
// Check for lifecycle change (agent stays "running" but moves through phases)
|
|
195
212
|
if (prev.lifecycle !== feature.lifecycle) {
|
|
196
213
|
prev.lifecycle = feature.lifecycle;
|
package/web/.next/standalone/src/presentation/web/components/common/add-repository-button/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { AddRepositoryButton } from './add-repository-button';
|
|
@@ -8,7 +8,6 @@ import { FeatureCreateDrawer } from '@/components/common/feature-create-drawer';
|
|
|
8
8
|
import type { FeatureCreatePayload } from '@/components/common/feature-create-drawer';
|
|
9
9
|
import type { ParentFeatureOption } from '@/components/common/feature-create-drawer/feature-create-drawer';
|
|
10
10
|
import type { WorkflowDefaults } from '@/app/actions/get-workflow-defaults';
|
|
11
|
-
import { useSoundAction } from '@/hooks/use-sound-action';
|
|
12
11
|
|
|
13
12
|
export interface CreateDrawerClientProps {
|
|
14
13
|
repositoryPath: string;
|
|
@@ -24,7 +23,6 @@ export function CreateDrawerClient({
|
|
|
24
23
|
workflowDefaults,
|
|
25
24
|
}: CreateDrawerClientProps) {
|
|
26
25
|
const router = useRouter();
|
|
27
|
-
const createSound = useSoundAction('create');
|
|
28
26
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
29
27
|
|
|
30
28
|
// Derive open state from the URL. Next.js parallel routes preserve slot
|
|
@@ -54,40 +52,38 @@ export function CreateDrawerClient({
|
|
|
54
52
|
(data: FeatureCreatePayload) => {
|
|
55
53
|
setIsSubmitting(true);
|
|
56
54
|
|
|
57
|
-
// Dispatch event for optimistic canvas update before navigating
|
|
58
|
-
window.dispatchEvent(
|
|
59
|
-
new CustomEvent('shep:feature-created', {
|
|
60
|
-
detail: {
|
|
61
|
-
name: data.name,
|
|
62
|
-
description: data.description,
|
|
63
|
-
repositoryPath: data.repositoryPath,
|
|
64
|
-
parentId: data.parentId,
|
|
65
|
-
},
|
|
66
|
-
})
|
|
67
|
-
);
|
|
68
|
-
|
|
69
55
|
// Close the drawer immediately for responsive UI
|
|
70
56
|
router.push('/');
|
|
71
57
|
|
|
72
|
-
//
|
|
58
|
+
// Server action Phase 1 returns fast with real feature ID (DB record created)
|
|
59
|
+
// Phase 2 (metadata, worktree, agent) runs in background on server
|
|
73
60
|
createFeature(data)
|
|
74
61
|
.then((result) => {
|
|
75
62
|
if (result.error) {
|
|
76
63
|
toast.error(result.error);
|
|
77
|
-
// Dispatch rollback event
|
|
78
64
|
window.dispatchEvent(new CustomEvent('shep:feature-create-failed'));
|
|
79
65
|
return;
|
|
80
66
|
}
|
|
81
|
-
|
|
67
|
+
// Dispatch event with real feature ID so control center adds it to featureMap
|
|
68
|
+
window.dispatchEvent(
|
|
69
|
+
new CustomEvent('shep:feature-created', {
|
|
70
|
+
detail: {
|
|
71
|
+
featureId: result.feature!.id,
|
|
72
|
+
name: result.feature!.name,
|
|
73
|
+
description: result.feature!.description,
|
|
74
|
+
repositoryPath: result.feature!.repositoryPath,
|
|
75
|
+
parentId: data.parentId,
|
|
76
|
+
},
|
|
77
|
+
})
|
|
78
|
+
);
|
|
82
79
|
})
|
|
83
80
|
.catch(() => {
|
|
84
81
|
toast.error('Failed to create feature');
|
|
85
82
|
window.dispatchEvent(new CustomEvent('shep:feature-create-failed'));
|
|
86
|
-
// Reset on error so the user can retry without navigating away
|
|
87
83
|
setIsSubmitting(false);
|
|
88
84
|
});
|
|
89
85
|
},
|
|
90
|
-
[router
|
|
86
|
+
[router]
|
|
91
87
|
);
|
|
92
88
|
|
|
93
89
|
return (
|
|
@@ -8,7 +8,7 @@ import type {
|
|
|
8
8
|
PrdApprovalPayload,
|
|
9
9
|
QuestionSelectionChange,
|
|
10
10
|
} from '@shepai/core/domain/generated/output';
|
|
11
|
-
import { PrStatus } from '@shepai/core/domain/generated/output';
|
|
11
|
+
import { PrStatus, NotificationEventType } from '@shepai/core/domain/generated/output';
|
|
12
12
|
import { approveFeature } from '@/app/actions/approve-feature';
|
|
13
13
|
import { rejectFeature } from '@/app/actions/reject-feature';
|
|
14
14
|
import { getFeatureArtifact } from '@/app/actions/get-feature-artifact';
|
|
@@ -94,9 +94,22 @@ export function FeatureDrawerClient({ view: initialView }: FeatureDrawerClientPr
|
|
|
94
94
|
const newEvents = events.slice(processedCountRef.current);
|
|
95
95
|
processedCountRef.current = events.length;
|
|
96
96
|
|
|
97
|
+
// PhaseCompleted must NOT set state when WaitingApproval is in the same poll batch.
|
|
98
|
+
// But when it arrives without WaitingApproval (e.g. post-approval), allow it as
|
|
99
|
+
// a fallback for missed AgentStarted events.
|
|
100
|
+
const waitingApprovalInBatch = newEvents.some(
|
|
101
|
+
(e) =>
|
|
102
|
+
e.featureId === featureNode.featureId &&
|
|
103
|
+
e.eventType === NotificationEventType.WaitingApproval
|
|
104
|
+
);
|
|
105
|
+
|
|
97
106
|
for (const event of newEvents) {
|
|
98
107
|
if (event.featureId !== featureNode.featureId) continue;
|
|
99
|
-
|
|
108
|
+
|
|
109
|
+
const newState =
|
|
110
|
+
event.eventType === NotificationEventType.PhaseCompleted && waitingApprovalInBatch
|
|
111
|
+
? undefined
|
|
112
|
+
: mapEventTypeToState(event.eventType);
|
|
100
113
|
const newLifecycle = mapPhaseNameToLifecycle(event.phaseName);
|
|
101
114
|
|
|
102
115
|
// Trigger a server refresh to get the latest drawer view,
|
|
@@ -104,24 +117,26 @@ export function FeatureDrawerClient({ view: initialView }: FeatureDrawerClientPr
|
|
|
104
117
|
// to avoid the refresh interfering with the reject handler's navigation.
|
|
105
118
|
if (!isDirtyRef.current && !isRejectingRef.current) router.refresh();
|
|
106
119
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
120
|
+
if (newState !== undefined || newLifecycle !== undefined) {
|
|
121
|
+
// Optimistically update the node data AND re-derive the view type so the
|
|
122
|
+
// drawer switches immediately (e.g. prd-review → feature when agent resumes)
|
|
123
|
+
// without waiting for the router.refresh() round-trip.
|
|
124
|
+
setView((prev) => {
|
|
125
|
+
if (
|
|
126
|
+
prev.type !== 'feature' &&
|
|
127
|
+
prev.type !== 'prd-review' &&
|
|
128
|
+
prev.type !== 'tech-review' &&
|
|
129
|
+
prev.type !== 'merge-review'
|
|
130
|
+
)
|
|
131
|
+
return prev;
|
|
132
|
+
const updatedNode = {
|
|
119
133
|
...prev.node,
|
|
120
|
-
state: newState,
|
|
134
|
+
...(newState !== undefined && { state: newState }),
|
|
121
135
|
...(newLifecycle !== undefined && { lifecycle: newLifecycle }),
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
|
|
136
|
+
};
|
|
137
|
+
return { ...prev, type: deriveViewType(updatedNode), node: updatedNode };
|
|
138
|
+
});
|
|
139
|
+
}
|
|
125
140
|
}
|
|
126
141
|
}, [events, featureNode, router]);
|
|
127
142
|
|
|
@@ -765,3 +780,14 @@ function DetailRow({ label, value }: { label: string; value: string }) {
|
|
|
765
780
|
</div>
|
|
766
781
|
);
|
|
767
782
|
}
|
|
783
|
+
|
|
784
|
+
/** Mirrors computeDrawerView logic — derives view type from live node data. */
|
|
785
|
+
function deriveViewType(
|
|
786
|
+
node: FeatureNodeData
|
|
787
|
+
): 'feature' | 'prd-review' | 'tech-review' | 'merge-review' {
|
|
788
|
+
if (node.lifecycle === 'requirements' && node.state === 'action-required') return 'prd-review';
|
|
789
|
+
if (node.lifecycle === 'implementation' && node.state === 'action-required') return 'tech-review';
|
|
790
|
+
if (node.lifecycle === 'review' && (node.state === 'action-required' || node.state === 'error'))
|
|
791
|
+
return 'merge-review';
|
|
792
|
+
return 'feature';
|
|
793
|
+
}
|
|
@@ -4,11 +4,7 @@ export {
|
|
|
4
4
|
computeDrawerView,
|
|
5
5
|
type DrawerView,
|
|
6
6
|
} from './control-center-drawer';
|
|
7
|
-
export {
|
|
8
|
-
AddRepositoryNode,
|
|
9
|
-
type AddRepositoryNodeData,
|
|
10
|
-
type AddRepositoryNodeType,
|
|
11
|
-
} from './add-repository-node';
|
|
7
|
+
export { AddRepositoryButton } from './add-repository-button';
|
|
12
8
|
export { ElapsedTime, formatElapsed } from './elapsed-time';
|
|
13
9
|
export { EmptyState, type EmptyStateProps } from './empty-state';
|
|
14
10
|
export {
|
|
@@ -35,7 +31,6 @@ export {
|
|
|
35
31
|
export { FeatureStatusBadges, type FeatureStatusBadgesProps } from './feature-status-badges';
|
|
36
32
|
export { FeatureStatusGroup, type FeatureStatusGroupProps } from './feature-status-group';
|
|
37
33
|
export { LoadingSkeleton } from './loading-skeleton';
|
|
38
|
-
export { NotificationPermissionBanner } from './notification-permission-banner';
|
|
39
34
|
export {
|
|
40
35
|
RepositoryNode,
|
|
41
36
|
type RepositoryNodeData,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { useState, useCallback } from 'react';
|
|
4
4
|
import { Github, Plus, TerminalSquare, Copy, Check, Loader2 } from 'lucide-react';
|
|
5
5
|
import { cn } from '@/lib/utils';
|
|
6
|
-
import { pickFolder } from '@/components/common/add-repository-
|
|
6
|
+
import { pickFolder } from '@/components/common/add-repository-button/pick-folder';
|
|
7
7
|
|
|
8
8
|
export interface ControlCenterEmptyStateProps {
|
|
9
9
|
onRepositorySelect?: (path: string) => void;
|
|
@@ -7,7 +7,6 @@ import { FeaturesCanvas } from '@/components/features/features-canvas';
|
|
|
7
7
|
import type { CanvasNodeType } from '@/components/features/features-canvas';
|
|
8
8
|
import type { FeatureNodeData } from '@/components/common/feature-node';
|
|
9
9
|
import type { RepositoryNodeData } from '@/components/common/repository-node';
|
|
10
|
-
import { NotificationPermissionBanner } from '@/components/common/notification-permission-banner';
|
|
11
10
|
import {
|
|
12
11
|
useSidebarFeaturesContext,
|
|
13
12
|
mapNodeStateToSidebarStatus,
|
|
@@ -39,6 +38,7 @@ export function ControlCenterInner({ initialNodes, initialEdges }: ControlCenter
|
|
|
39
38
|
handleDeleteFeature,
|
|
40
39
|
handleDeleteRepository,
|
|
41
40
|
createFeatureNode,
|
|
41
|
+
setCallbacks,
|
|
42
42
|
} = useControlCenterState(initialNodes, initialEdges);
|
|
43
43
|
|
|
44
44
|
// Publish sidebar features to context whenever feature node data changes
|
|
@@ -163,11 +163,12 @@ export function ControlCenterInner({ initialNodes, initialEdges }: ControlCenter
|
|
|
163
163
|
return () => window.removeEventListener('shep:add-repository', handler);
|
|
164
164
|
}, [handleAddRepository]);
|
|
165
165
|
|
|
166
|
-
// Listen for
|
|
166
|
+
// Listen for create events from the create drawer (with real feature ID from server)
|
|
167
167
|
useEffect(() => {
|
|
168
168
|
const handler = (e: Event) => {
|
|
169
169
|
const detail = (
|
|
170
170
|
e as CustomEvent<{
|
|
171
|
+
featureId: string;
|
|
171
172
|
name: string;
|
|
172
173
|
description?: string;
|
|
173
174
|
repositoryPath: string;
|
|
@@ -183,6 +184,7 @@ export function ControlCenterInner({ initialNodes, initialEdges }: ControlCenter
|
|
|
183
184
|
parentNodeId,
|
|
184
185
|
{
|
|
185
186
|
state: 'creating',
|
|
187
|
+
featureId: detail.featureId,
|
|
186
188
|
name: detail.name,
|
|
187
189
|
description: detail.description,
|
|
188
190
|
repositoryPath: detail.repositoryPath,
|
|
@@ -200,7 +202,8 @@ export function ControlCenterInner({ initialNodes, initialEdges }: ControlCenter
|
|
|
200
202
|
);
|
|
201
203
|
|
|
202
204
|
createFeatureNode(repoNode?.id ?? null, {
|
|
203
|
-
state: '
|
|
205
|
+
state: 'running',
|
|
206
|
+
featureId: detail.featureId,
|
|
204
207
|
name: detail.name,
|
|
205
208
|
description: detail.description,
|
|
206
209
|
repositoryPath: detail.repositoryPath,
|
|
@@ -210,37 +213,41 @@ export function ControlCenterInner({ initialNodes, initialEdges }: ControlCenter
|
|
|
210
213
|
return () => window.removeEventListener('shep:feature-created', handler);
|
|
211
214
|
}, [nodes, createFeatureNode]);
|
|
212
215
|
|
|
216
|
+
// Wire callbacks into derived node data (via ref — no re-render).
|
|
217
|
+
useEffect(() => {
|
|
218
|
+
setCallbacks({
|
|
219
|
+
onNodeAction: handleAddFeatureToFeature,
|
|
220
|
+
onFeatureDelete: handleDeleteFeature,
|
|
221
|
+
onRepositoryAdd: handleAddFeatureToRepo,
|
|
222
|
+
onRepositoryClick: handleRepositoryClick,
|
|
223
|
+
onRepositoryDelete: handleDeleteRepository,
|
|
224
|
+
});
|
|
225
|
+
}, [
|
|
226
|
+
setCallbacks,
|
|
227
|
+
handleAddFeatureToFeature,
|
|
228
|
+
handleDeleteFeature,
|
|
229
|
+
handleAddFeatureToRepo,
|
|
230
|
+
handleRepositoryClick,
|
|
231
|
+
handleDeleteRepository,
|
|
232
|
+
]);
|
|
233
|
+
|
|
213
234
|
const hasRepositories = nodes.some((n) => n.type === 'repositoryNode');
|
|
214
235
|
|
|
215
236
|
if (!hasRepositories) {
|
|
216
|
-
return
|
|
217
|
-
<>
|
|
218
|
-
<NotificationPermissionBanner />
|
|
219
|
-
<ControlCenterEmptyState onRepositorySelect={handleAddRepository} />
|
|
220
|
-
</>
|
|
221
|
-
);
|
|
237
|
+
return <ControlCenterEmptyState onRepositorySelect={handleAddRepository} />;
|
|
222
238
|
}
|
|
223
239
|
|
|
224
240
|
return (
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
onPaneClick={handleClearDrawers}
|
|
237
|
-
onRepositoryAdd={handleAddFeatureToRepo}
|
|
238
|
-
onRepositoryClick={handleRepositoryClick}
|
|
239
|
-
onRepositoryDelete={handleDeleteRepository}
|
|
240
|
-
onFeatureDelete={handleDeleteFeature}
|
|
241
|
-
onRepositorySelect={handleAddRepository}
|
|
242
|
-
emptyState={<ControlCenterEmptyState onRepositorySelect={handleAddRepository} />}
|
|
243
|
-
/>
|
|
244
|
-
</>
|
|
241
|
+
<FeaturesCanvas
|
|
242
|
+
nodes={nodes}
|
|
243
|
+
edges={edges}
|
|
244
|
+
selectedFeatureId={selectedFeatureId}
|
|
245
|
+
onNodesChange={onNodesChange}
|
|
246
|
+
onConnect={handleConnect}
|
|
247
|
+
onAddFeature={handleAddFeature}
|
|
248
|
+
onNodeClick={handleNodeClick}
|
|
249
|
+
onPaneClick={handleClearDrawers}
|
|
250
|
+
emptyState={<ControlCenterEmptyState onRepositorySelect={handleAddRepository} />}
|
|
251
|
+
/>
|
|
245
252
|
);
|
|
246
253
|
}
|
|
@@ -4,7 +4,6 @@ import { ControlCenter } from './control-center';
|
|
|
4
4
|
import type { CanvasNodeType } from '@/components/features/features-canvas';
|
|
5
5
|
import type { FeatureNodeType } from '@/components/common/feature-node';
|
|
6
6
|
import type { RepositoryNodeType } from '@/components/common/repository-node';
|
|
7
|
-
import type { AddRepositoryNodeType } from '@/components/common/add-repository-node';
|
|
8
7
|
import { layoutWithDagre } from '@/lib/layout-with-dagre';
|
|
9
8
|
import { AgentEventsProvider } from '@/hooks/agent-events-provider';
|
|
10
9
|
import { DrawerCloseGuardProvider } from '@/hooks/drawer-close-guard';
|
|
@@ -102,13 +101,6 @@ const repoNode: RepositoryNodeType = {
|
|
|
102
101
|
data: { name: 'shep-ai/cli' },
|
|
103
102
|
};
|
|
104
103
|
|
|
105
|
-
const addRepoNode: AddRepositoryNodeType = {
|
|
106
|
-
id: 'add-repo',
|
|
107
|
-
type: 'addRepositoryNode',
|
|
108
|
-
position: { x: 50, y: 302 },
|
|
109
|
-
data: {},
|
|
110
|
-
};
|
|
111
|
-
|
|
112
104
|
const dashedEdge = { style: { strokeDasharray: '5 5' } };
|
|
113
105
|
|
|
114
106
|
export const Empty: Story = {
|
|
@@ -137,7 +129,7 @@ export const WithToolbar: Story = {
|
|
|
137
129
|
|
|
138
130
|
export const WithNodeActions: Story = {
|
|
139
131
|
args: {
|
|
140
|
-
initialNodes: [repoNode,
|
|
132
|
+
initialNodes: [repoNode, ...featureNodes] as CanvasNodeType[],
|
|
141
133
|
initialEdges: [
|
|
142
134
|
{ id: 'e-repo-f1', source: 'repo-1', target: 'feat-1', ...dashedEdge },
|
|
143
135
|
{ id: 'e-repo-f2', source: 'repo-1', target: 'feat-2', ...dashedEdge },
|
|
@@ -146,30 +138,6 @@ export const WithNodeActions: Story = {
|
|
|
146
138
|
},
|
|
147
139
|
};
|
|
148
140
|
|
|
149
|
-
/**
|
|
150
|
-
* Interactive story — starts with just an "Add Repository" button.
|
|
151
|
-
*
|
|
152
|
-
* User flow:
|
|
153
|
-
* 1. Click "Add Repository" → native folder picker → select a folder → repo node appears
|
|
154
|
-
* 2. Hover the repo node → click (+) → a new feature node appears connected to the repo
|
|
155
|
-
* 3. Hover the feature node → click (+) → another feature appears connected to the first
|
|
156
|
-
*
|
|
157
|
-
* You can also use the toolbar "Add Feature" button to create unconnected features.
|
|
158
|
-
*/
|
|
159
|
-
export const Interactive: Story = {
|
|
160
|
-
args: {
|
|
161
|
-
initialNodes: [
|
|
162
|
-
{
|
|
163
|
-
id: 'add-repo',
|
|
164
|
-
type: 'addRepositoryNode',
|
|
165
|
-
position: { x: 50, y: 50 },
|
|
166
|
-
data: {},
|
|
167
|
-
} as AddRepositoryNodeType,
|
|
168
|
-
] as CanvasNodeType[],
|
|
169
|
-
initialEdges: [],
|
|
170
|
-
},
|
|
171
|
-
};
|
|
172
|
-
|
|
173
141
|
// --- Dagre-layouted story: multiple repos with feature trees ---
|
|
174
142
|
|
|
175
143
|
const dagreRepoNodes: RepositoryNodeType[] = [
|
|
@@ -266,14 +234,7 @@ const dagreFeatureNodes: FeatureNodeType[] = [
|
|
|
266
234
|
},
|
|
267
235
|
];
|
|
268
236
|
|
|
269
|
-
const
|
|
270
|
-
id: 'add-repo',
|
|
271
|
-
type: 'addRepositoryNode',
|
|
272
|
-
position: { x: 0, y: 0 },
|
|
273
|
-
data: {},
|
|
274
|
-
};
|
|
275
|
-
|
|
276
|
-
const dagreNodesRaw: CanvasNodeType[] = [...dagreRepoNodes, ...dagreFeatureNodes, dagreAddRepo];
|
|
237
|
+
const dagreNodesRaw: CanvasNodeType[] = [...dagreRepoNodes, ...dagreFeatureNodes];
|
|
277
238
|
|
|
278
239
|
const dagreEdges: Edge[] = [
|
|
279
240
|
// cli repo → Auth, SSO
|