@shepai/cli 1.72.9-pr218.000ada0 → 1.72.9-pr220.e1bfec0
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 -1
- package/dist/src/presentation/web/components/common/index.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/index.js +1 -1
- 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 +23 -4
- 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 +152 -350
- 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 +41 -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 +230 -0
- 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/_63688261._.js +6 -0
- package/web/.next/server/chunks/ssr/_63688261._.js.map +1 -0
- 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/{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/_63688261._.js +6 -0
- 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/{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 -5
- 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 +24 -8
- 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 +180 -429
- 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 +343 -0
- 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/{0eeb65b8300fb7cd.js → 0a883e2efecfd563.js} +1 -1
- package/web/.next/static/chunks/{4a8df922364596f6.js → 596ca28d78baae25.js} +1 -1
- package/web/.next/static/chunks/{99381b9c479936d5.js → 638824249d103905.js} +1 -1
- package/web/.next/static/chunks/63a80e9d4c4b0253.js +4 -0
- package/web/.next/static/chunks/71c9ecdceac1939f.css +2 -0
- package/web/.next/static/chunks/a040b058d79f4ade.js +1 -0
- package/web/.next/static/chunks/{1249c7c8745b972e.js → c575ab63c138645b.js} +1 -1
- package/web/.next/static/chunks/cdc92cdf81005c84.js +1 -0
- package/web/.next/static/chunks/{a676f15c03fa66b9.js → e05eb1cc84ee254a.js} +1 -1
- package/web/.next/static/chunks/ef8f8940eac6c542.js +1 -0
- package/web/.next/static/chunks/{9be617fc61b893f2.js → f32c98a709949c5b.js} +1 -1
- 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/1c15ab8668ecf473.js +0 -1
- package/web/.next/static/chunks/2cf89d17ee7717a6.js +0 -4
- package/web/.next/static/chunks/981b8df9ad045742.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 → uMxPSDPwZCludF5408rWJ}/_buildManifest.js +0 -0
- /package/web/.next/static/{nnR12fAsrSagAfeV6xj6D → uMxPSDPwZCludF5408rWJ}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{nnR12fAsrSagAfeV6xj6D → uMxPSDPwZCludF5408rWJ}/_ssgManifest.js +0 -0
package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js
CHANGED
|
@@ -1,46 +1,29 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import {
|
|
2
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
3
3
|
import { useRouter } from 'next/navigation';
|
|
4
4
|
import { toast } from 'sonner';
|
|
5
|
-
import { applyNodeChanges } from '@xyflow/react';
|
|
6
5
|
import { layoutWithDagre, CANVAS_LAYOUT_DEFAULTS, } from '../../../lib/layout-with-dagre.js';
|
|
7
6
|
import { deleteFeature } from '../../../app/actions/delete-feature.js';
|
|
8
7
|
import { addRepository } from '../../../app/actions/add-repository.js';
|
|
9
8
|
import { deleteRepository } from '../../../app/actions/delete-repository.js';
|
|
10
|
-
import {
|
|
9
|
+
import { getFeatureMetadata } from '../../../app/actions/get-feature-metadata.js';
|
|
11
10
|
import { useAgentEventsContext } from '../../../hooks/agent-events-provider.js';
|
|
12
11
|
import { useSoundAction } from '../../../hooks/use-sound-action.js';
|
|
13
12
|
import { mapEventTypeToState, mapPhaseNameToLifecycle, } from '../../common/feature-node/derive-feature-state.js';
|
|
13
|
+
import { NotificationEventType } from '../../../../../../packages/core/src/domain/generated/output.js';
|
|
14
|
+
import { useGraphState } from '../../../hooks/use-graph-state.js';
|
|
14
15
|
let nextFeatureId = 0;
|
|
15
16
|
export function useControlCenterState(initialNodes, initialEdges) {
|
|
16
17
|
const router = useRouter();
|
|
17
|
-
const [nodes, setNodes] = useState(initialNodes);
|
|
18
|
-
// eslint-disable-next-line react/hook-use-state -- raw setter renamed; public setEdges wrapper keeps edgesRef in sync
|
|
19
|
-
const [edges, setEdgesRaw] = useState(initialEdges);
|
|
20
|
-
const edgesRef = useRef(initialEdges);
|
|
21
|
-
// Wrapper that keeps edgesRef in sync with edges state, allowing
|
|
22
|
-
// createFeatureNode to read current edges without a closure dependency.
|
|
23
|
-
const setEdges = useCallback((update) => {
|
|
24
|
-
setEdgesRaw((prev) => {
|
|
25
|
-
const next = typeof update === 'function' ? update(prev) : update;
|
|
26
|
-
edgesRef.current = next;
|
|
27
|
-
return next;
|
|
28
|
-
});
|
|
29
|
-
}, []);
|
|
30
18
|
const deleteSound = useSoundAction('delete');
|
|
31
19
|
const createSound = useSoundAction('create');
|
|
32
|
-
|
|
20
|
+
const { nodes, edges, reconcile, updateFeature, addPendingFeature, removeFeature, restoreFeature, addRepository: addRepositoryToMap, removeRepository, replaceRepository, getFeatureRepositoryPath, getRepositoryData, setCallbacks, } = useGraphState(initialNodes, initialEdges);
|
|
21
|
+
// Sync server props into domain Maps when initialNodes/initialEdges change.
|
|
22
|
+
// Keyed by a stable string so we don't re-reconcile on every render.
|
|
33
23
|
const initialNodeKey = initialNodes
|
|
34
24
|
.map((n) => n.id)
|
|
35
25
|
.sort()
|
|
36
26
|
.join(',');
|
|
37
|
-
const initialEdgeKey = initialEdges
|
|
38
|
-
.map((e) => e.id)
|
|
39
|
-
.sort()
|
|
40
|
-
.join(',');
|
|
41
|
-
// Track previous feature states so we can detect transitions on server refresh
|
|
42
|
-
const prevFeatureStatesRef = useRef(new Map());
|
|
43
|
-
// Stable key that changes when feature DATA changes (state/lifecycle), not just IDs.
|
|
44
27
|
const initialDataKey = initialNodes
|
|
45
28
|
.filter((n) => n.type === 'featureNode')
|
|
46
29
|
.map((n) => {
|
|
@@ -49,52 +32,24 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
49
32
|
})
|
|
50
33
|
.sort()
|
|
51
34
|
.join(',');
|
|
52
|
-
|
|
53
|
-
// to avoid infinite re-renders from unstable array references.
|
|
54
|
-
useEffect(() => {
|
|
55
|
-
setNodes((currentNodes) => {
|
|
56
|
-
// Build a lookup of current node positions by ID
|
|
57
|
-
const currentById = new Map(currentNodes.map((n) => [n.id, n]));
|
|
58
|
-
// Identify optimistic "creating" nodes (they have temp IDs not in server data)
|
|
59
|
-
const serverIds = new Set(initialNodes.map((n) => n.id));
|
|
60
|
-
const creatingNodes = currentNodes.filter((n) => n.type === 'featureNode' &&
|
|
61
|
-
n.data.state === 'creating' &&
|
|
62
|
-
!serverIds.has(n.id));
|
|
63
|
-
// Merge server nodes with client positions, tracking if relayout is needed
|
|
64
|
-
let needsRelayout = false;
|
|
65
|
-
const merged = initialNodes.map((serverNode) => {
|
|
66
|
-
// Node already exists on canvas — keep its position, update data
|
|
67
|
-
const existing = currentById.get(serverNode.id);
|
|
68
|
-
if (existing) {
|
|
69
|
-
return { ...serverNode, position: existing.position };
|
|
70
|
-
}
|
|
71
|
-
// New server node — inherit position from an optimistic creating node if available
|
|
72
|
-
if (serverNode.type === 'featureNode' && creatingNodes.length > 0) {
|
|
73
|
-
const donor = creatingNodes.shift();
|
|
74
|
-
return { ...serverNode, position: donor.position };
|
|
75
|
-
}
|
|
76
|
-
// Truly new node with no optimistic counterpart — needs relayout
|
|
77
|
-
needsRelayout = true;
|
|
78
|
-
return serverNode;
|
|
79
|
-
});
|
|
80
|
-
// Also check if non-creating nodes were removed (graph got smaller)
|
|
81
|
-
if (!needsRelayout) {
|
|
82
|
-
needsRelayout = currentNodes.some((cn) => !serverIds.has(cn.id) &&
|
|
83
|
-
!(cn.type === 'featureNode' && cn.data.state === 'creating'));
|
|
84
|
-
}
|
|
85
|
-
if (needsRelayout) {
|
|
86
|
-
const layoutResult = layoutWithDagre(merged, initialEdges, CANVAS_LAYOUT_DEFAULTS);
|
|
87
|
-
setEdges(layoutResult.edges);
|
|
88
|
-
return layoutResult.nodes;
|
|
89
|
-
}
|
|
90
|
-
return merged;
|
|
91
|
-
});
|
|
92
|
-
}, [initialNodeKey, initialNodes, initialEdges, setEdges]);
|
|
35
|
+
const prevReconcileKey = useRef('');
|
|
93
36
|
useEffect(() => {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
37
|
+
const key = `${initialNodeKey}|${initialDataKey}`;
|
|
38
|
+
if (key !== prevReconcileKey.current) {
|
|
39
|
+
prevReconcileKey.current = key;
|
|
40
|
+
reconcile(initialNodes, initialEdges);
|
|
41
|
+
}
|
|
42
|
+
}, [initialNodeKey, initialDataKey, initialNodes, initialEdges, reconcile]);
|
|
43
|
+
// Track previous feature states for fallback notifications
|
|
44
|
+
const prevFeatureStatesRef = useRef(new Map());
|
|
45
|
+
const initialFeatureDataKey = initialNodes
|
|
46
|
+
.filter((n) => n.type === 'featureNode')
|
|
47
|
+
.map((n) => {
|
|
48
|
+
const d = n.data;
|
|
49
|
+
return `${n.id}:${d.state}`;
|
|
50
|
+
})
|
|
51
|
+
.sort()
|
|
52
|
+
.join(',');
|
|
98
53
|
const { events } = useAgentEventsContext();
|
|
99
54
|
useEffect(() => {
|
|
100
55
|
const prevStates = prevFeatureStatesRef.current;
|
|
@@ -104,7 +59,6 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
104
59
|
const data = node.data;
|
|
105
60
|
const prev = prevStates.get(node.id);
|
|
106
61
|
if (prev !== undefined && prev !== data.state) {
|
|
107
|
-
// Check if SSE already delivered a matching event for this feature
|
|
108
62
|
const sseAlreadyCovered = events.some((e) => e.featureId === data.featureId && mapEventTypeToState(e.eventType) === data.state);
|
|
109
63
|
if (!sseAlreadyCovered) {
|
|
110
64
|
if (data.state === 'done') {
|
|
@@ -115,9 +69,7 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
115
69
|
description: 'Waiting for your approval',
|
|
116
70
|
action: {
|
|
117
71
|
label: 'Review',
|
|
118
|
-
onClick: () => {
|
|
119
|
-
router.push(`/feature/${data.featureId}`);
|
|
120
|
-
},
|
|
72
|
+
onClick: () => router.push(`/feature/${data.featureId}`),
|
|
121
73
|
},
|
|
122
74
|
});
|
|
123
75
|
}
|
|
@@ -128,272 +80,139 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
128
80
|
}
|
|
129
81
|
prevStates.set(node.id, data.state);
|
|
130
82
|
}
|
|
131
|
-
}, [
|
|
132
|
-
//
|
|
133
|
-
// Uses `events` array (not `lastEvent`) so that React batching cannot silently
|
|
134
|
-
// drop events when multiple SSE messages arrive in the same tick.
|
|
83
|
+
}, [initialFeatureDataKey, initialNodes, events, router]);
|
|
84
|
+
// SSE effect: only state + lifecycle updates
|
|
135
85
|
const processedEventCountRef = useRef(0);
|
|
136
86
|
useEffect(() => {
|
|
137
87
|
if (events.length <= processedEventCountRef.current)
|
|
138
88
|
return;
|
|
139
89
|
const newEvents = events.slice(processedEventCountRef.current);
|
|
140
90
|
processedEventCountRef.current = events.length;
|
|
91
|
+
// PhaseCompleted must NOT set state when WaitingApproval is in the same poll batch —
|
|
92
|
+
// they arrive together and PhaseCompleted → 'running' would revert 'action-required'.
|
|
93
|
+
// But when PhaseCompleted arrives WITHOUT WaitingApproval (e.g. after approval), we
|
|
94
|
+
// must allow it to update state as a fallback for any missed AgentStarted event.
|
|
95
|
+
const waitingApprovalFeatures = new Set(newEvents
|
|
96
|
+
.filter((e) => e.eventType === NotificationEventType.WaitingApproval)
|
|
97
|
+
.map((e) => e.featureId));
|
|
141
98
|
for (const event of newEvents) {
|
|
142
|
-
const newState =
|
|
99
|
+
const newState = event.eventType === NotificationEventType.PhaseCompleted &&
|
|
100
|
+
waitingApprovalFeatures.has(event.featureId)
|
|
101
|
+
? undefined
|
|
102
|
+
: mapEventTypeToState(event.eventType);
|
|
143
103
|
const newLifecycle = mapPhaseNameToLifecycle(event.phaseName);
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if (data.featureId !== event.featureId)
|
|
150
|
-
return node;
|
|
151
|
-
return {
|
|
152
|
-
...node,
|
|
153
|
-
data: {
|
|
154
|
-
...data,
|
|
155
|
-
state: newState,
|
|
156
|
-
...(newLifecycle !== undefined && { lifecycle: newLifecycle }),
|
|
157
|
-
},
|
|
158
|
-
};
|
|
159
|
-
}));
|
|
160
|
-
}
|
|
161
|
-
}, [events]);
|
|
162
|
-
// Lightweight server-action polling fallback: fetches fresh graph data every 5s
|
|
163
|
-
// when any feature is in an active state. This avoids full router.refresh() while
|
|
164
|
-
// ensuring the UI stays in sync even if SSE events are missed.
|
|
165
|
-
//
|
|
166
|
-
// We derive a stable boolean so the interval isn't torn down on every node change.
|
|
167
|
-
const hasActiveFeature = nodes.some((n) => {
|
|
168
|
-
if (n.type !== 'featureNode')
|
|
169
|
-
return false;
|
|
170
|
-
const data = n.data;
|
|
171
|
-
return (data.state === 'running' || data.state === 'action-required' || data.state === 'creating');
|
|
172
|
-
});
|
|
173
|
-
useEffect(() => {
|
|
174
|
-
if (!hasActiveFeature)
|
|
175
|
-
return;
|
|
176
|
-
const interval = setInterval(async () => {
|
|
177
|
-
try {
|
|
178
|
-
const { nodes: serverNodes } = await fetchGraphData();
|
|
179
|
-
setNodes((currentNodes) => {
|
|
180
|
-
const serverById = new Map(serverNodes.map((n) => [n.id, n]));
|
|
181
|
-
let changed = false;
|
|
182
|
-
const merged = currentNodes.map((node) => {
|
|
183
|
-
const serverNode = serverById.get(node.id);
|
|
184
|
-
if (!serverNode || node.type !== 'featureNode')
|
|
185
|
-
return node;
|
|
186
|
-
const currentData = node.data;
|
|
187
|
-
const serverData = serverNode.data;
|
|
188
|
-
// Only update if server data has different state or lifecycle
|
|
189
|
-
if (currentData.state === serverData.state &&
|
|
190
|
-
currentData.lifecycle === serverData.lifecycle) {
|
|
191
|
-
return node;
|
|
192
|
-
}
|
|
193
|
-
changed = true;
|
|
194
|
-
return {
|
|
195
|
-
...node,
|
|
196
|
-
data: {
|
|
197
|
-
...currentData,
|
|
198
|
-
state: serverData.state,
|
|
199
|
-
lifecycle: serverData.lifecycle,
|
|
200
|
-
progress: serverData.progress,
|
|
201
|
-
...(serverData.errorMessage && { errorMessage: serverData.errorMessage }),
|
|
202
|
-
...(serverData.runtime && { runtime: serverData.runtime }),
|
|
203
|
-
...(serverData.agentType && { agentType: serverData.agentType }),
|
|
204
|
-
...(serverData.pr && { pr: serverData.pr }),
|
|
205
|
-
...(serverData.blockedBy && { blockedBy: serverData.blockedBy }),
|
|
206
|
-
},
|
|
207
|
-
};
|
|
208
|
-
});
|
|
209
|
-
// Find new server nodes not yet on the client
|
|
210
|
-
const clientIds = new Set(currentNodes.map((n) => n.id));
|
|
211
|
-
const newServerNodes = serverNodes.filter((n) => !clientIds.has(n.id));
|
|
212
|
-
// Replace optimistic "creating" temp nodes with their real server
|
|
213
|
-
// counterparts. Temp nodes have IDs like "feature-<ts>" while real
|
|
214
|
-
// nodes use "feat-<uuid>". Match by repositoryPath + name.
|
|
215
|
-
if (newServerNodes.length > 0) {
|
|
216
|
-
const creatingIndices = [];
|
|
217
|
-
merged.forEach((n, i) => {
|
|
218
|
-
if (n.type === 'featureNode' && n.data.state === 'creating') {
|
|
219
|
-
creatingIndices.push(i);
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
for (const serverNode of newServerNodes) {
|
|
223
|
-
if (serverNode.type !== 'featureNode' || creatingIndices.length === 0)
|
|
224
|
-
continue;
|
|
225
|
-
const idx = creatingIndices.shift();
|
|
226
|
-
// Replace the temp node in-place, keeping its position
|
|
227
|
-
merged[idx] = { ...serverNode, position: merged[idx].position };
|
|
228
|
-
changed = true;
|
|
229
|
-
}
|
|
230
|
-
// Any remaining new server nodes that didn't replace a temp node
|
|
231
|
-
const replacedIds = new Set(merged.map((n) => n.id));
|
|
232
|
-
const trueNewNodes = newServerNodes.filter((n) => !replacedIds.has(n.id));
|
|
233
|
-
if (trueNewNodes.length > 0) {
|
|
234
|
-
changed = true;
|
|
235
|
-
return [...merged, ...trueNewNodes];
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
return changed ? merged : currentNodes;
|
|
104
|
+
const nodeId = `feat-${event.featureId}`;
|
|
105
|
+
if (newState !== undefined || newLifecycle !== undefined) {
|
|
106
|
+
updateFeature(nodeId, {
|
|
107
|
+
...(newState !== undefined && { state: newState }),
|
|
108
|
+
...(newLifecycle !== undefined && { lifecycle: newLifecycle }),
|
|
239
109
|
});
|
|
240
110
|
}
|
|
241
|
-
catch {
|
|
242
|
-
// Silently ignore polling errors — SSE is the primary mechanism
|
|
243
|
-
}
|
|
244
|
-
}, 5_000);
|
|
245
|
-
return () => clearInterval(interval);
|
|
246
|
-
}, [hasActiveFeature]);
|
|
247
|
-
const onNodesChange = useCallback((changes) => {
|
|
248
|
-
// Filter out 'replace' changes — in controlled mode these come from
|
|
249
|
-
// React Flow's StoreUpdater syncing the nodes prop back into state.
|
|
250
|
-
// Applying them creates a circular update loop (nodes → enrichedNodes →
|
|
251
|
-
// StoreUpdater → onNodesChange → setNodes → nodes → …) that both causes
|
|
252
|
-
// excessive re-renders and overwrites SSE-driven state changes with stale data.
|
|
253
|
-
const applicable = changes.filter((c) => c.type !== 'replace');
|
|
254
|
-
if (applicable.length > 0) {
|
|
255
|
-
setNodes((ns) => applyNodeChanges(applicable, ns));
|
|
256
111
|
}
|
|
257
|
-
}, []);
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
id: `edge-${connection.source}-${connection.target}`,
|
|
277
|
-
source: connection.source,
|
|
278
|
-
target: connection.target,
|
|
279
|
-
style: { strokeDasharray: '5 5' },
|
|
280
|
-
},
|
|
281
|
-
];
|
|
112
|
+
}, [events, updateFeature]);
|
|
113
|
+
// Separate effect: fetch metadata (name + description) when SSE reports it changed
|
|
114
|
+
const metadataFetchedRef = useRef(new Set());
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
for (const event of events) {
|
|
117
|
+
if (event.message !== 'Feature metadata updated')
|
|
118
|
+
continue;
|
|
119
|
+
if (metadataFetchedRef.current.has(event.featureId))
|
|
120
|
+
continue;
|
|
121
|
+
metadataFetchedRef.current.add(event.featureId);
|
|
122
|
+
const nodeId = `feat-${event.featureId}`;
|
|
123
|
+
getFeatureMetadata(event.featureId)
|
|
124
|
+
.then((meta) => {
|
|
125
|
+
if (meta) {
|
|
126
|
+
updateFeature(nodeId, { name: meta.name, description: meta.description });
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
.catch(() => {
|
|
130
|
+
// Silent: metadata fetch failure is non-critical
|
|
282
131
|
});
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
132
|
+
}
|
|
133
|
+
}, [events, updateFeature]);
|
|
134
|
+
// onNodesChange is a no-op: nodes are derived from domain Maps.
|
|
135
|
+
// Since nodesDraggable=false and elementsSelectable=false, only React Flow's
|
|
136
|
+
// internal replace/dimensions changes come through — we ignore them all.
|
|
137
|
+
const onNodesChange = useCallback((_changes) => {
|
|
138
|
+
// Intentional no-op: domain Maps are the source of truth.
|
|
139
|
+
}, []);
|
|
140
|
+
const handleConnect = useCallback((_connection) => {
|
|
141
|
+
// Connections are managed via domain operations, not direct edge manipulation.
|
|
142
|
+
}, []);
|
|
286
143
|
const createFeatureNode = useCallback((sourceNodeId, dataOverride, edgeType) => {
|
|
287
|
-
|
|
144
|
+
// Use real feature ID when available (from server), otherwise temp ID
|
|
145
|
+
const id = dataOverride?.featureId
|
|
146
|
+
? `feat-${dataOverride.featureId}`
|
|
147
|
+
: `feature-${Date.now()}-${nextFeatureId++}`;
|
|
288
148
|
const newFeatureData = {
|
|
289
149
|
name: dataOverride?.name ?? 'New Feature',
|
|
290
150
|
description: dataOverride?.description ?? 'Describe what this feature does',
|
|
291
|
-
featureId: `#${id.slice(-4)}`,
|
|
151
|
+
featureId: dataOverride?.featureId ?? `#${id.slice(-4)}`,
|
|
292
152
|
lifecycle: 'requirements',
|
|
293
153
|
state: dataOverride?.state ?? 'running',
|
|
294
154
|
progress: 0,
|
|
295
155
|
repositoryPath: dataOverride?.repositoryPath ?? '',
|
|
296
156
|
branch: dataOverride?.branch ?? '',
|
|
297
157
|
};
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
id,
|
|
301
|
-
type: 'featureNode',
|
|
302
|
-
position: { x: 0, y: 0 },
|
|
303
|
-
data: newFeatureData,
|
|
304
|
-
};
|
|
305
|
-
const newEdge = sourceNodeId
|
|
306
|
-
? {
|
|
307
|
-
id: edgeType === 'dependencyEdge'
|
|
308
|
-
? `dep-${sourceNodeId}-${id}`
|
|
309
|
-
: `edge-${sourceNodeId}-${id}`,
|
|
310
|
-
source: sourceNodeId,
|
|
311
|
-
target: id,
|
|
312
|
-
...(edgeType ? { type: edgeType } : { style: { strokeDasharray: '5 5' } }),
|
|
313
|
-
}
|
|
314
|
-
: null;
|
|
315
|
-
const allEdges = newEdge
|
|
316
|
-
? [...edgesRef.current.filter((e) => e.id !== newEdge.id), newEdge]
|
|
317
|
-
: edgesRef.current;
|
|
318
|
-
// Insert the new node after the last sibling connected to the same source,
|
|
319
|
-
// so dagre's input-order preservation keeps repo groups together.
|
|
320
|
-
let insertIndex = currentNodes.length;
|
|
321
|
-
if (sourceNodeId) {
|
|
322
|
-
const siblingIds = new Set(allEdges.filter((e) => e.source === sourceNodeId).map((e) => e.target));
|
|
323
|
-
for (let i = currentNodes.length - 1; i >= 0; i--) {
|
|
324
|
-
if (siblingIds.has(currentNodes[i].id)) {
|
|
325
|
-
insertIndex = i + 1;
|
|
326
|
-
break;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
const allNodes = [
|
|
331
|
-
...currentNodes.slice(0, insertIndex),
|
|
332
|
-
newNode,
|
|
333
|
-
...currentNodes.slice(insertIndex),
|
|
334
|
-
];
|
|
335
|
-
// Run dagre layout on the full graph so the new node is positioned consistently
|
|
336
|
-
const layoutResult = layoutWithDagre(allNodes, allEdges, CANVAS_LAYOUT_DEFAULTS);
|
|
337
|
-
setEdges(layoutResult.edges);
|
|
338
|
-
return layoutResult.nodes;
|
|
339
|
-
});
|
|
158
|
+
const parentNodeId = edgeType === 'dependencyEdge' && sourceNodeId ? sourceNodeId : undefined;
|
|
159
|
+
addPendingFeature(id, newFeatureData, parentNodeId);
|
|
340
160
|
return id;
|
|
341
|
-
}, [
|
|
161
|
+
}, [addPendingFeature]);
|
|
342
162
|
const handleDeleteFeature = useCallback((featureId) => {
|
|
343
163
|
const nodeId = `feat-${featureId}`;
|
|
344
|
-
//
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
return layoutResult.nodes;
|
|
354
|
-
});
|
|
164
|
+
// Find the current entry for rollback
|
|
165
|
+
const prevEntry = nodes
|
|
166
|
+
.filter((n) => n.id === nodeId)
|
|
167
|
+
.map((n) => ({
|
|
168
|
+
nodeId: n.id,
|
|
169
|
+
data: n.data,
|
|
170
|
+
}))[0];
|
|
171
|
+
// Optimistic removal
|
|
172
|
+
removeFeature(nodeId);
|
|
355
173
|
deleteSound.play();
|
|
356
174
|
toast.success('Feature deleted successfully');
|
|
357
175
|
router.push('/');
|
|
358
|
-
// Persist in background — rollback on failure
|
|
359
176
|
deleteFeature(featureId)
|
|
360
177
|
.then((result) => {
|
|
361
178
|
if (result.error) {
|
|
362
|
-
|
|
363
|
-
|
|
179
|
+
if (prevEntry)
|
|
180
|
+
restoreFeature(nodeId, prevEntry);
|
|
364
181
|
toast.error(result.error);
|
|
365
182
|
}
|
|
366
183
|
})
|
|
367
184
|
.catch(() => {
|
|
368
|
-
|
|
369
|
-
|
|
185
|
+
if (prevEntry)
|
|
186
|
+
restoreFeature(nodeId, prevEntry);
|
|
370
187
|
toast.error('Failed to delete feature');
|
|
371
188
|
});
|
|
372
|
-
}, [nodes, router, deleteSound,
|
|
189
|
+
}, [nodes, router, deleteSound, removeFeature, restoreFeature]);
|
|
373
190
|
const handleDeleteRepository = useCallback(async (repositoryId) => {
|
|
374
191
|
const repoNodeId = `repo-${repositoryId}`;
|
|
192
|
+
// Find children of this repo via edges
|
|
193
|
+
const childFeatureIds = new Set(edges.filter((e) => e.source === repoNodeId).map((e) => e.target));
|
|
375
194
|
// Snapshot for rollback
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
const remainingNodes = currentNodes.filter((n) => n.id !== repoNodeId && !childFeatureIds.has(n.id));
|
|
383
|
-
const remainingEdges = edgesRef.current.filter((e) => e.source !== repoNodeId &&
|
|
384
|
-
e.target !== repoNodeId &&
|
|
385
|
-
!childFeatureIds.has(e.source) &&
|
|
386
|
-
!childFeatureIds.has(e.target));
|
|
387
|
-
const layoutResult = layoutWithDagre(remainingNodes, remainingEdges, CANVAS_LAYOUT_DEFAULTS);
|
|
388
|
-
setEdges(layoutResult.edges);
|
|
389
|
-
return layoutResult.nodes;
|
|
390
|
-
});
|
|
195
|
+
const prevRepoData = getRepositoryData(repoNodeId);
|
|
196
|
+
// Optimistic: remove repo + children
|
|
197
|
+
removeRepository(repoNodeId);
|
|
198
|
+
for (const childId of childFeatureIds) {
|
|
199
|
+
removeFeature(childId);
|
|
200
|
+
}
|
|
391
201
|
try {
|
|
392
202
|
const result = await deleteRepository(repositoryId);
|
|
393
203
|
if (!result.success) {
|
|
394
204
|
toast.error(result.error ?? 'Failed to remove repository');
|
|
395
|
-
|
|
396
|
-
|
|
205
|
+
if (prevRepoData)
|
|
206
|
+
addRepositoryToMap(repoNodeId, prevRepoData);
|
|
207
|
+
for (const childId of childFeatureIds) {
|
|
208
|
+
const childNode = nodes.find((n) => n.id === childId);
|
|
209
|
+
if (childNode) {
|
|
210
|
+
restoreFeature(childId, {
|
|
211
|
+
nodeId: childId,
|
|
212
|
+
data: childNode.data,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
397
216
|
return;
|
|
398
217
|
}
|
|
399
218
|
deleteSound.play();
|
|
@@ -401,82 +220,62 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
401
220
|
}
|
|
402
221
|
catch {
|
|
403
222
|
toast.error('Failed to remove repository');
|
|
404
|
-
|
|
405
|
-
|
|
223
|
+
if (prevRepoData)
|
|
224
|
+
addRepositoryToMap(repoNodeId, prevRepoData);
|
|
225
|
+
for (const childId of childFeatureIds) {
|
|
226
|
+
const childNode = nodes.find((n) => n.id === childId);
|
|
227
|
+
if (childNode) {
|
|
228
|
+
restoreFeature(childId, {
|
|
229
|
+
nodeId: childId,
|
|
230
|
+
data: childNode.data,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
406
234
|
}
|
|
407
|
-
}, [
|
|
235
|
+
}, [
|
|
236
|
+
edges,
|
|
237
|
+
nodes,
|
|
238
|
+
deleteSound,
|
|
239
|
+
removeRepository,
|
|
240
|
+
removeFeature,
|
|
241
|
+
addRepositoryToMap,
|
|
242
|
+
restoreFeature,
|
|
243
|
+
getRepositoryData,
|
|
244
|
+
]);
|
|
408
245
|
const handleLayout = useCallback((direction) => {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
setEdges(result.edges);
|
|
415
|
-
return result.nodes;
|
|
416
|
-
});
|
|
417
|
-
}, [setEdges]);
|
|
246
|
+
// Layout is applied via reconcile on next server prop update.
|
|
247
|
+
// For immediate re-layout, we apply dagre and trigger a reconcile-like update.
|
|
248
|
+
const result = layoutWithDagre(nodes, edges, { ...CANVAS_LAYOUT_DEFAULTS, direction });
|
|
249
|
+
reconcile(result.nodes, result.edges);
|
|
250
|
+
}, [nodes, edges, reconcile]);
|
|
418
251
|
const handleAddRepository = useCallback((path) => {
|
|
419
252
|
const tempId = `repo-temp-${Date.now()}`;
|
|
420
253
|
const repoName = path
|
|
421
254
|
.replace(/[\\/]+$/, '')
|
|
422
255
|
.split(/[\\/]/)
|
|
423
256
|
.pop() ?? path;
|
|
424
|
-
|
|
425
|
-
setNodes((currentNodes) => {
|
|
426
|
-
const newNode = {
|
|
427
|
-
id: tempId,
|
|
428
|
-
type: 'repositoryNode',
|
|
429
|
-
position: { x: 0, y: 0 },
|
|
430
|
-
data: { name: repoName, repositoryPath: path, id: tempId },
|
|
431
|
-
};
|
|
432
|
-
const allNodes = [...currentNodes, newNode];
|
|
433
|
-
const layoutResult = layoutWithDagre(allNodes, edgesRef.current, CANVAS_LAYOUT_DEFAULTS);
|
|
434
|
-
setEdges(layoutResult.edges);
|
|
435
|
-
return layoutResult.nodes;
|
|
436
|
-
});
|
|
437
|
-
// Persist via server action
|
|
257
|
+
addRepositoryToMap(tempId, { name: repoName, repositoryPath: path, id: tempId });
|
|
438
258
|
addRepository({ path, name: repoName })
|
|
439
259
|
.then((result) => {
|
|
440
260
|
if (result.error) {
|
|
441
|
-
|
|
442
|
-
setNodes((prev) => {
|
|
443
|
-
const remaining = prev.filter((n) => n.id !== tempId);
|
|
444
|
-
const layoutResult = layoutWithDagre(remaining, edgesRef.current, CANVAS_LAYOUT_DEFAULTS);
|
|
445
|
-
setEdges(layoutResult.edges);
|
|
446
|
-
return layoutResult.nodes;
|
|
447
|
-
});
|
|
261
|
+
removeRepository(tempId);
|
|
448
262
|
toast.error(result.error);
|
|
449
263
|
return;
|
|
450
264
|
}
|
|
451
|
-
// Replace temp ID with real repository ID
|
|
452
265
|
const repo = result.repository;
|
|
453
266
|
const realId = `repo-${repo.id}`;
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
}
|
|
460
|
-
: n));
|
|
461
|
-
setEdges((prev) => prev.map((e) => ({
|
|
462
|
-
...e,
|
|
463
|
-
source: e.source === tempId ? realId : e.source,
|
|
464
|
-
target: e.target === tempId ? realId : e.target,
|
|
465
|
-
id: e.id.replace(tempId, realId),
|
|
466
|
-
})));
|
|
267
|
+
replaceRepository(tempId, realId, {
|
|
268
|
+
name: repo.name,
|
|
269
|
+
repositoryPath: repo.path,
|
|
270
|
+
id: repo.id,
|
|
271
|
+
});
|
|
467
272
|
createSound.play();
|
|
468
273
|
})
|
|
469
274
|
.catch(() => {
|
|
470
|
-
|
|
471
|
-
setNodes((prev) => {
|
|
472
|
-
const remaining = prev.filter((n) => n.id !== tempId);
|
|
473
|
-
const layoutResult = layoutWithDagre(remaining, edgesRef.current, CANVAS_LAYOUT_DEFAULTS);
|
|
474
|
-
setEdges(layoutResult.edges);
|
|
475
|
-
return layoutResult.nodes;
|
|
476
|
-
});
|
|
275
|
+
removeRepository(tempId);
|
|
477
276
|
toast.error('Failed to add repository');
|
|
478
277
|
});
|
|
479
|
-
}, [
|
|
278
|
+
}, [addRepositoryToMap, removeRepository, replaceRepository, createSound]);
|
|
480
279
|
return {
|
|
481
280
|
nodes,
|
|
482
281
|
edges,
|
|
@@ -487,5 +286,8 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
487
286
|
handleDeleteFeature,
|
|
488
287
|
handleDeleteRepository,
|
|
489
288
|
createFeatureNode,
|
|
289
|
+
getFeatureRepositoryPath,
|
|
290
|
+
getRepositoryData,
|
|
291
|
+
setCallbacks,
|
|
490
292
|
};
|
|
491
293
|
}
|