@shepai/cli 1.26.0 → 1.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/packages/core/src/application/ports/output/index.d.ts +1 -1
- package/dist/packages/core/src/application/ports/output/index.d.ts.map +1 -1
- package/dist/packages/core/src/application/ports/output/services/index.d.ts +1 -0
- package/dist/packages/core/src/application/ports/output/services/index.d.ts.map +1 -1
- package/dist/packages/core/src/application/ports/output/services/notification-service.interface.d.ts +29 -0
- package/dist/packages/core/src/application/ports/output/services/notification-service.interface.d.ts.map +1 -0
- package/dist/packages/core/src/application/ports/output/services/notification-service.interface.js +12 -0
- package/dist/packages/core/src/application/use-cases/features/create/metadata-generator.d.ts +3 -2
- package/dist/packages/core/src/application/use-cases/features/create/metadata-generator.d.ts.map +1 -1
- package/dist/packages/core/src/application/use-cases/features/create/metadata-generator.js +36 -15
- package/dist/packages/core/src/domain/factories/settings-defaults.factory.d.ts.map +1 -1
- package/dist/packages/core/src/domain/factories/settings-defaults.factory.js +13 -0
- package/dist/packages/core/src/domain/generated/output.d.ts +109 -0
- package/dist/packages/core/src/domain/generated/output.d.ts.map +1 -1
- package/dist/packages/core/src/domain/generated/output.js +15 -0
- package/dist/packages/core/src/infrastructure/di/container.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/di/container.js +17 -0
- package/dist/packages/core/src/infrastructure/persistence/sqlite/mappers/settings.mapper.d.ts +8 -0
- package/dist/packages/core/src/infrastructure/persistence/sqlite/mappers/settings.mapper.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/persistence/sqlite/mappers/settings.mapper.js +22 -0
- package/dist/packages/core/src/infrastructure/persistence/sqlite/migrations.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/persistence/sqlite/migrations.js +14 -0
- package/dist/packages/core/src/infrastructure/repositories/sqlite-settings.repository.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/repositories/sqlite-settings.repository.js +17 -3
- package/dist/packages/core/src/infrastructure/services/notifications/desktop-notifier.d.ts +22 -0
- package/dist/packages/core/src/infrastructure/services/notifications/desktop-notifier.d.ts.map +1 -0
- package/dist/packages/core/src/infrastructure/services/notifications/desktop-notifier.js +39 -0
- package/dist/packages/core/src/infrastructure/services/notifications/notification-bus.d.ts +52 -0
- package/dist/packages/core/src/infrastructure/services/notifications/notification-bus.d.ts.map +1 -0
- package/dist/packages/core/src/infrastructure/services/notifications/notification-bus.js +64 -0
- package/dist/packages/core/src/infrastructure/services/notifications/notification-watcher.service.d.ts +60 -0
- package/dist/packages/core/src/infrastructure/services/notifications/notification-watcher.service.d.ts.map +1 -0
- package/dist/packages/core/src/infrastructure/services/notifications/notification-watcher.service.js +225 -0
- package/dist/packages/core/src/infrastructure/services/notifications/notification.service.d.ts +21 -0
- package/dist/packages/core/src/infrastructure/services/notifications/notification.service.d.ts.map +1 -0
- package/dist/packages/core/src/infrastructure/services/notifications/notification.service.js +43 -0
- package/dist/src/presentation/cli/commands/feat/new.command.d.ts.map +1 -1
- package/dist/src/presentation/cli/commands/feat/new.command.js +7 -6
- package/dist/src/presentation/cli/commands/ui.command.d.ts.map +1 -1
- package/dist/src/presentation/cli/commands/ui.command.js +8 -0
- package/dist/src/presentation/web/app/api/agent-events/route.d.ts +14 -0
- package/dist/src/presentation/web/app/api/agent-events/route.d.ts.map +1 -0
- package/dist/src/presentation/web/app/api/agent-events/route.js +77 -0
- package/dist/src/presentation/web/app/api/features/create/route.d.ts.map +1 -1
- package/dist/src/presentation/web/app/api/features/create/route.js +28 -2
- package/dist/src/presentation/web/app/page.d.ts.map +1 -1
- package/dist/src/presentation/web/app/page.js +9 -12
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts +4 -8
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.js +37 -4
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.d.ts +30 -6
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.js +74 -18
- package/dist/src/presentation/web/components/common/feature-create-drawer/index.d.ts +1 -1
- package/dist/src/presentation/web/components/common/feature-create-drawer/index.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.js +2 -2
- package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.stories.js +3 -3
- package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.d.ts +5 -2
- package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.js +9 -0
- package/dist/src/presentation/web/components/common/feature-node/feature-node.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-node/feature-node.js +9 -7
- package/dist/src/presentation/web/components/common/feature-node/feature-node.stories.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-node/feature-node.stories.js +3 -0
- 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-inner.d.ts.map +1 -1
- package/dist/src/presentation/web/components/features/control-center/control-center-inner.js +2 -2
- package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts +3 -2
- 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 +4 -12
- package/dist/src/presentation/web/components/layouts/app-shell/app-shell.d.ts.map +1 -1
- package/dist/src/presentation/web/components/layouts/app-shell/app-shell.js +3 -0
- package/dist/src/presentation/web/components/ui/button.d.ts +1 -1
- package/dist/src/presentation/web/components/ui/checkbox-group-item.d.ts +11 -0
- package/dist/src/presentation/web/components/ui/checkbox-group-item.d.ts.map +1 -0
- package/dist/src/presentation/web/components/ui/checkbox-group-item.js +8 -0
- package/dist/src/presentation/web/components/ui/checkbox-group-item.stories.d.ts +17 -0
- package/dist/src/presentation/web/components/ui/checkbox-group-item.stories.d.ts.map +1 -0
- package/dist/src/presentation/web/components/ui/checkbox-group-item.stories.js +33 -0
- package/dist/src/presentation/web/components/ui/checkbox-group.d.ts +21 -0
- package/dist/src/presentation/web/components/ui/checkbox-group.d.ts.map +1 -0
- package/dist/src/presentation/web/components/ui/checkbox-group.js +24 -0
- package/dist/src/presentation/web/components/ui/checkbox-group.stories.d.ts +23 -0
- package/dist/src/presentation/web/components/ui/checkbox-group.stories.d.ts.map +1 -0
- package/dist/src/presentation/web/components/ui/checkbox-group.stories.js +61 -0
- package/dist/src/presentation/web/components/ui/checkbox.d.ts +5 -0
- package/dist/src/presentation/web/components/ui/checkbox.d.ts.map +1 -0
- package/dist/src/presentation/web/components/ui/checkbox.js +9 -0
- package/dist/src/presentation/web/components/ui/checkbox.stories.d.ts +10 -0
- package/dist/src/presentation/web/components/ui/checkbox.stories.d.ts.map +1 -0
- package/dist/src/presentation/web/components/ui/checkbox.stories.js +24 -0
- package/dist/src/presentation/web/hooks/use-agent-events.d.ts +12 -0
- package/dist/src/presentation/web/hooks/use-agent-events.d.ts.map +1 -0
- package/dist/src/presentation/web/hooks/use-agent-events.js +58 -0
- package/dist/src/presentation/web/hooks/use-notifications.d.ts +10 -0
- package/dist/src/presentation/web/hooks/use-notifications.d.ts.map +1 -0
- package/dist/src/presentation/web/hooks/use-notifications.js +57 -0
- package/dist/src/presentation/web/hooks/use-notifications.stories.d.ts +6 -0
- package/dist/src/presentation/web/hooks/use-notifications.stories.d.ts.map +1 -0
- package/dist/src/presentation/web/hooks/use-notifications.stories.js +62 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +3 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/app-path-routes-manifest.json +1 -0
- 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/required-server-files.js +1 -1
- package/web/.next/required-server-files.json +1 -1
- package/web/.next/routes-manifest.json +6 -0
- 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.js.nft.json +1 -1
- package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_not-found.html +2 -2
- package/web/.next/server/app/_not-found.rsc +10 -10
- package/web/.next/server/app/_not-found.segments/_full.segment.rsc +10 -10
- package/web/.next/server/app/_not-found.segments/_head.segment.rsc +3 -3
- package/web/.next/server/app/_not-found.segments/_index.segment.rsc +6 -6
- package/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/web/.next/server/app/api/agent-events/route/app-paths-manifest.json +3 -0
- package/web/.next/server/app/api/agent-events/route/build-manifest.json +11 -0
- package/web/.next/server/app/api/agent-events/route/server-reference-manifest.json +4 -0
- package/web/.next/server/app/api/agent-events/route.js +6 -0
- package/web/.next/server/app/api/agent-events/route.js.map +5 -0
- package/web/.next/server/app/api/agent-events/route.js.nft.json +1 -0
- package/web/.next/server/app/api/agent-events/route_client-reference-manifest.js +2 -0
- package/web/.next/server/app/api/dialog/pick-files/route.js +3 -2
- package/web/.next/server/app/api/dialog/pick-files/route.js.nft.json +1 -1
- package/web/.next/server/app/api/dialog/pick-folder/route.js +3 -2
- package/web/.next/server/app/api/dialog/pick-folder/route.js.nft.json +1 -1
- package/web/.next/server/app/api/features/create/route.js +3 -2
- package/web/.next/server/app/api/features/create/route.js.nft.json +1 -1
- package/web/.next/server/app/api/ide/open/route.js +4 -2
- package/web/.next/server/app/api/ide/open/route.js.nft.json +1 -1
- package/web/.next/server/app/api/shell/open/route.js +3 -2
- package/web/.next/server/app/api/shell/open/route.js.nft.json +1 -1
- package/web/.next/server/app/page.js +1 -1
- package/web/.next/server/app/page.js.nft.json +1 -1
- package/web/.next/server/app/page_client-reference-manifest.js +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/app/version.html +2 -2
- package/web/.next/server/app/version.rsc +5 -5
- package/web/.next/server/app/version.segments/_full.segment.rsc +5 -5
- package/web/.next/server/app/version.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/version.segments/_index.segment.rsc +4 -4
- package/web/.next/server/app/version.segments/_tree.segment.rsc +2 -2
- package/web/.next/server/app/version.segments/version/__PAGE__.segment.rsc +2 -2
- package/web/.next/server/app/version.segments/version.segment.rsc +1 -1
- package/web/.next/server/app-paths-manifest.json +1 -0
- package/web/.next/server/chunks/403f9_next_567de315._.js +17 -0
- package/web/.next/server/chunks/403f9_next_567de315._.js.map +1 -0
- package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_12059e3e.js +3 -0
- package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_12059e3e.js.map +1 -0
- package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_agent-events_route_actions_686edb08.js +3 -0
- package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_agent-events_route_actions_686edb08.js.map +1 -0
- package/web/.next/server/chunks/[root-of-the-server]__4e987cf3._.js +8 -0
- package/web/.next/server/chunks/[root-of-the-server]__4e987cf3._.js.map +1 -0
- package/web/.next/server/chunks/[root-of-the-server]__57104a4c._.js +7 -0
- package/web/.next/server/chunks/[root-of-the-server]__57104a4c._.js.map +1 -0
- package/web/.next/server/chunks/{[root-of-the-server]__602cce1b._.js → [root-of-the-server]__61349adc._.js} +2 -2
- package/web/.next/server/chunks/{[root-of-the-server]__bb4fc5b5._.js → [root-of-the-server]__a6bf88cc._.js} +2 -2
- package/web/.next/server/chunks/[root-of-the-server]__aa2074ca._.js +3 -0
- package/web/.next/server/chunks/[root-of-the-server]__aa2074ca._.js.map +1 -0
- package/web/.next/server/chunks/{[root-of-the-server]__1913013c._.js → [root-of-the-server]__ec87d735._.js} +2 -2
- package/web/.next/server/chunks/[root-of-the-server]__ecaf05bc._.js +3 -0
- package/web/.next/server/chunks/[root-of-the-server]__ecaf05bc._.js.map +1 -0
- package/web/.next/server/chunks/ssr/403f9_next_dist_esm_build_templates_app-page_dbf21e04.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__80e5bed2._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__f12b60be._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__fa87595c._.js +1 -1
- package/web/.next/server/chunks/ssr/_09afa42a._.js +6 -1
- package/web/.next/server/chunks/ssr/_09afa42a._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_884ddade._.js +1 -1
- package/web/.next/server/chunks/ssr/_a4189414._.js +10 -0
- package/web/.next/server/chunks/ssr/_a4189414._.js.map +1 -0
- package/web/.next/server/chunks/ssr/_c5d377cc._.js +1 -1
- 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_ed0934e5._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js.map +1 -1
- package/web/.next/server/pages/404.html +2 -2
- 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 +1 -1
- package/web/.next/static/chunks/1d665ddc2230f73f.js +5 -0
- package/web/.next/static/chunks/{c2854b6c6d0ca1f3.js → 28c05929f2636c01.js} +7 -2
- package/web/.next/static/chunks/a7273c84449394e0.css +2 -0
- package/web/.next/static/chunks/b32b17f102226f32.js +1 -0
- package/web/.next/trace +1 -1
- package/web/.next/trace-build +1 -1
- package/web/.next/types/link.d.ts +1 -0
- package/web/.next/types/routes.d.ts +2 -1
- package/web/.next/types/validator.ts +9 -0
- package/web/.next/server/chunks/[root-of-the-server]__56ac1509._.js +0 -3
- package/web/.next/server/chunks/[root-of-the-server]__56ac1509._.js.map +0 -1
- package/web/.next/server/chunks/[root-of-the-server]__ba6c5641._.js +0 -21
- package/web/.next/server/chunks/[root-of-the-server]__ba6c5641._.js.map +0 -1
- package/web/.next/server/chunks/[root-of-the-server]__eaa478b7._.js +0 -8
- package/web/.next/server/chunks/[root-of-the-server]__eaa478b7._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_1c3962d7._.js +0 -10
- package/web/.next/server/chunks/ssr/_1c3962d7._.js.map +0 -1
- package/web/.next/static/chunks/78ef4acb26e4d6ea.js +0 -1
- package/web/.next/static/chunks/79009d1ca0d944d2.js +0 -5
- package/web/.next/static/chunks/86ff2b6444e06e88.css +0 -2
- /package/web/.next/server/chunks/{[root-of-the-server]__602cce1b._.js.map → [root-of-the-server]__61349adc._.js.map} +0 -0
- /package/web/.next/server/chunks/{[root-of-the-server]__bb4fc5b5._.js.map → [root-of-the-server]__a6bf88cc._.js.map} +0 -0
- /package/web/.next/server/chunks/{[root-of-the-server]__1913013c._.js.map → [root-of-the-server]__ec87d735._.js.map} +0 -0
- /package/web/.next/static/{_6j6GS-S6zPiASj5tsxJ8 → wymVMk3X_cTuoYKeS0Gbf}/_buildManifest.js +0 -0
- /package/web/.next/static/{_6j6GS-S6zPiASj5tsxJ8 → wymVMk3X_cTuoYKeS0Gbf}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{_6j6GS-S6zPiASj5tsxJ8 → wymVMk3X_cTuoYKeS0Gbf}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSE API Route: GET /api/agent-events
|
|
3
|
+
*
|
|
4
|
+
* Streams agent lifecycle notification events to connected web UI clients
|
|
5
|
+
* via Server-Sent Events (SSE). This is the first API route in the web UI.
|
|
6
|
+
*
|
|
7
|
+
* - Subscribes to the notification event bus (shared in-process singleton)
|
|
8
|
+
* - Formats events as SSE data frames (event: notification\ndata: JSON\n\n)
|
|
9
|
+
* - Sends heartbeat comments every 30 seconds to keep connection alive
|
|
10
|
+
* - Supports optional ?runId query parameter to filter events
|
|
11
|
+
* - Cleans up listeners and intervals on client disconnect
|
|
12
|
+
*/
|
|
13
|
+
import { getNotificationBus, hasNotificationBus, } from '../../../../../../packages/core/src/infrastructure/services/notifications/notification-bus.js';
|
|
14
|
+
const HEARTBEAT_INTERVAL_MS = 30_000;
|
|
15
|
+
function formatSSEEvent(event) {
|
|
16
|
+
return `event: notification\ndata: ${JSON.stringify(event)}\n\n`;
|
|
17
|
+
}
|
|
18
|
+
function formatHeartbeat() {
|
|
19
|
+
return ': heartbeat\n\n';
|
|
20
|
+
}
|
|
21
|
+
export function GET(request) {
|
|
22
|
+
const url = new URL(request.url);
|
|
23
|
+
const runIdFilter = url.searchParams.get('runId');
|
|
24
|
+
const stream = new ReadableStream({
|
|
25
|
+
start(controller) {
|
|
26
|
+
const encoder = new TextEncoder();
|
|
27
|
+
// Check if bus is available
|
|
28
|
+
if (!hasNotificationBus()) {
|
|
29
|
+
controller.close();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const bus = getNotificationBus();
|
|
33
|
+
// Notification event listener
|
|
34
|
+
const onNotification = (event) => {
|
|
35
|
+
// Apply runId filter if set
|
|
36
|
+
if (runIdFilter && event.agentRunId !== runIdFilter) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
controller.enqueue(encoder.encode(formatSSEEvent(event)));
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Stream may be closed — ignore enqueue errors
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
bus.on('notification', onNotification);
|
|
47
|
+
// Heartbeat to keep connection alive
|
|
48
|
+
const heartbeatInterval = setInterval(() => {
|
|
49
|
+
try {
|
|
50
|
+
controller.enqueue(encoder.encode(formatHeartbeat()));
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// Stream may be closed — ignore enqueue errors
|
|
54
|
+
}
|
|
55
|
+
}, HEARTBEAT_INTERVAL_MS);
|
|
56
|
+
// Cleanup on client disconnect
|
|
57
|
+
const cleanup = () => {
|
|
58
|
+
bus.removeListener('notification', onNotification);
|
|
59
|
+
clearInterval(heartbeatInterval);
|
|
60
|
+
try {
|
|
61
|
+
controller.close();
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// Stream may already be closed
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
request.signal.addEventListener('abort', cleanup, { once: true });
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
return new Response(stream, {
|
|
71
|
+
headers: {
|
|
72
|
+
'Content-Type': 'text/event-stream',
|
|
73
|
+
'Cache-Control': 'no-cache',
|
|
74
|
+
Connection: 'keep-alive',
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../../../src/presentation/web/app/api/features/create/route.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../../../src/presentation/web/app/api/features/create/route.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAqD3C,wBAAsB,IAAI,CAAC,OAAO,EAAE,OAAO;;kGAyC1C"}
|
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server';
|
|
2
2
|
import { createFeature } from '../../../../../../../packages/core/src/infrastructure/di/use-cases-bridge.js';
|
|
3
|
+
function isValidApprovalGates(value) {
|
|
4
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
5
|
+
return false;
|
|
6
|
+
const obj = value;
|
|
7
|
+
if (typeof obj.allowPrd !== 'boolean' || typeof obj.allowPlan !== 'boolean')
|
|
8
|
+
return false;
|
|
9
|
+
if ('allowMerge' in obj && typeof obj.allowMerge !== 'boolean')
|
|
10
|
+
return false;
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
function normalizeApprovalGates(gates) {
|
|
14
|
+
return {
|
|
15
|
+
allowPrd: gates.allowPrd,
|
|
16
|
+
allowPlan: gates.allowPlan,
|
|
17
|
+
allowMerge: 'allowMerge' in gates ? gates.allowMerge : false,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
3
20
|
function composeUserInput(name, description, attachments) {
|
|
4
21
|
let userInput = `Feature: ${name}`;
|
|
5
22
|
if (description) {
|
|
@@ -13,16 +30,25 @@ function composeUserInput(name, description, attachments) {
|
|
|
13
30
|
}
|
|
14
31
|
export async function POST(request) {
|
|
15
32
|
const body = await request.json();
|
|
16
|
-
const { name, description, repositoryPath, attachments } = body;
|
|
33
|
+
const { name, description, repositoryPath, attachments, approvalGates } = body;
|
|
17
34
|
if (!name?.trim()) {
|
|
18
35
|
return NextResponse.json({ error: 'name is required' }, { status: 400 });
|
|
19
36
|
}
|
|
20
37
|
if (!repositoryPath?.trim()) {
|
|
21
38
|
return NextResponse.json({ error: 'repositoryPath is required' }, { status: 400 });
|
|
22
39
|
}
|
|
40
|
+
if (approvalGates !== undefined && !isValidApprovalGates(approvalGates)) {
|
|
41
|
+
return NextResponse.json({
|
|
42
|
+
error: 'approvalGates must be an object with boolean allowPrd, allowPlan, and optional boolean allowMerge',
|
|
43
|
+
}, { status: 400 });
|
|
44
|
+
}
|
|
23
45
|
const userInput = composeUserInput(name, description, attachments);
|
|
46
|
+
const defaultGates = { allowPrd: false, allowPlan: false, allowMerge: false };
|
|
47
|
+
const gates = isValidApprovalGates(approvalGates)
|
|
48
|
+
? normalizeApprovalGates(approvalGates)
|
|
49
|
+
: defaultGates;
|
|
24
50
|
try {
|
|
25
|
-
const result = await createFeature({ userInput, repositoryPath });
|
|
51
|
+
const result = await createFeature({ userInput, repositoryPath, approvalGates: gates });
|
|
26
52
|
return NextResponse.json(result);
|
|
27
53
|
}
|
|
28
54
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/app/page.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/app/page.tsx"],"names":[],"mappings":"AAWA,6EAA6E;AAC7E,eAAO,MAAM,OAAO,kBAAkB,CAAC;AAqBvC,wBAA8B,QAAQ,qDAmFrC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { ControlCenter } from '../components/features/control-center/index.js';
|
|
3
3
|
import { getAgentRun, getFeatures } from '../../../../packages/core/src/infrastructure/di/use-cases-bridge.js';
|
|
4
|
-
import {
|
|
4
|
+
import { deriveNodeState, deriveProgress, } from '../components/common/feature-node/derive-feature-state.js';
|
|
5
5
|
import { layoutWithDagre } from '../lib/layout-with-dagre.js';
|
|
6
6
|
/** Force request-time rendering so the globalThis DI bridge is available. */
|
|
7
7
|
export const dynamic = 'force-dynamic';
|
|
@@ -26,12 +26,7 @@ export default async function HomePage() {
|
|
|
26
26
|
const features = await getFeatures();
|
|
27
27
|
const featuresWithRuns = await Promise.all(features.map(async (feature) => {
|
|
28
28
|
const run = feature.agentRunId ? await getAgentRun(feature.agentRunId) : null;
|
|
29
|
-
return {
|
|
30
|
-
feature,
|
|
31
|
-
agentStatus: run?.status,
|
|
32
|
-
agentError: run?.error,
|
|
33
|
-
agentResult: run?.result,
|
|
34
|
-
};
|
|
29
|
+
return { feature, run };
|
|
35
30
|
}));
|
|
36
31
|
// Group features by repository path
|
|
37
32
|
const featuresByRepo = {};
|
|
@@ -53,9 +48,9 @@ export default async function HomePage() {
|
|
|
53
48
|
position: { x: 0, y: 0 },
|
|
54
49
|
data: { name: repoName },
|
|
55
50
|
});
|
|
56
|
-
repoFeatures.forEach(({ feature,
|
|
57
|
-
const agentNode =
|
|
58
|
-
const lifecycle =
|
|
51
|
+
repoFeatures.forEach(({ feature, run }) => {
|
|
52
|
+
const agentNode = run?.result?.startsWith('node:') ? run.result.slice(5) : undefined;
|
|
53
|
+
const lifecycle = run?.status === 'completed'
|
|
59
54
|
? 'maintain'
|
|
60
55
|
: ((agentNode ? nodeToLifecyclePhase[agentNode] : undefined) ??
|
|
61
56
|
lifecycleMap[feature.lifecycle] ??
|
|
@@ -67,8 +62,10 @@ export default async function HomePage() {
|
|
|
67
62
|
lifecycle,
|
|
68
63
|
repositoryPath: feature.repositoryPath,
|
|
69
64
|
branch: feature.branch,
|
|
70
|
-
|
|
71
|
-
|
|
65
|
+
state: deriveNodeState(feature, run),
|
|
66
|
+
progress: deriveProgress(feature),
|
|
67
|
+
...(run?.agentType && { agentType: run.agentType }),
|
|
68
|
+
...(run?.error && { errorMessage: run.error }),
|
|
72
69
|
};
|
|
73
70
|
const featureNodeId = `feat-${feature.id}`;
|
|
74
71
|
nodes.push({
|
package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CreateFeatureInput } from '../../../../../../packages/core/src/infrastructure/di/use-cases-bridge.js';
|
|
2
2
|
export type { FileAttachment } from '../../../../../../packages/core/src/infrastructure/services/file-dialog.service.js';
|
|
3
|
-
export interface CreateFeatureFormData {
|
|
4
|
-
name: string;
|
|
5
|
-
description: string;
|
|
6
|
-
attachments: FileAttachment[];
|
|
7
|
-
}
|
|
8
3
|
export interface FeatureCreateDrawerProps {
|
|
9
4
|
open: boolean;
|
|
10
5
|
onClose: () => void;
|
|
11
|
-
onSubmit: (data:
|
|
6
|
+
onSubmit: (data: CreateFeatureInput) => void;
|
|
7
|
+
repositoryPath: string;
|
|
12
8
|
isSubmitting?: boolean;
|
|
13
9
|
}
|
|
14
|
-
export declare function FeatureCreateDrawer({ open, onClose, onSubmit, isSubmitting, }: FeatureCreateDrawerProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export declare function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, isSubmitting, }: FeatureCreateDrawerProps): import("react/jsx-runtime").JSX.Element;
|
|
15
11
|
//# sourceMappingURL=feature-create-drawer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-create-drawer.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"feature-create-drawer.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.tsx"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAG1F,YAAY,EAAE,cAAc,EAAE,MAAM,0DAA0D,CAAC;AAiC/F,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAC7C,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,cAAc,EACd,YAAoB,GACrB,EAAE,wBAAwB,2CA6L1B"}
|
package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.js
CHANGED
|
@@ -8,17 +8,41 @@ import { Button } from '../../ui/button.js';
|
|
|
8
8
|
import { Input } from '../../ui/input.js';
|
|
9
9
|
import { Textarea } from '../../ui/textarea.js';
|
|
10
10
|
import { Label } from '../../ui/label.js';
|
|
11
|
+
import { CheckboxGroup } from '../../ui/checkbox-group.js';
|
|
11
12
|
import { Separator } from '../../ui/separator.js';
|
|
12
13
|
import { Badge } from '../../ui/badge.js';
|
|
13
14
|
import { pickFiles } from './pick-files.js';
|
|
14
|
-
|
|
15
|
+
const AUTO_APPROVE_OPTIONS = [
|
|
16
|
+
{ id: 'allowPrd', label: 'PRD', description: 'Auto-approve requirements move to planning.' },
|
|
17
|
+
{ id: 'allowPlan', label: 'Plan', description: 'Auto-approve planning move to implementation.' },
|
|
18
|
+
{ id: 'allowMerge', label: 'Merge', description: 'Auto-approve merge move to Done.' },
|
|
19
|
+
];
|
|
20
|
+
const EMPTY_GATES = {
|
|
21
|
+
allowPrd: false,
|
|
22
|
+
allowPlan: false,
|
|
23
|
+
allowMerge: false,
|
|
24
|
+
};
|
|
25
|
+
function composeUserInput(name, description, attachments) {
|
|
26
|
+
let userInput = `Feature: ${name}`;
|
|
27
|
+
if (description) {
|
|
28
|
+
userInput += `\n\n${description}`;
|
|
29
|
+
}
|
|
30
|
+
if (attachments.length > 0) {
|
|
31
|
+
const paths = attachments.map((a) => `- ${a.path}`).join('\n');
|
|
32
|
+
userInput += `\n\nAttached files:\n${paths}`;
|
|
33
|
+
}
|
|
34
|
+
return userInput;
|
|
35
|
+
}
|
|
36
|
+
export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, isSubmitting = false, }) {
|
|
15
37
|
const [name, setName] = useState('');
|
|
16
38
|
const [description, setDescription] = useState('');
|
|
17
39
|
const [attachments, setAttachments] = useState([]);
|
|
40
|
+
const [approvalGates, setApprovalGates] = useState({ ...EMPTY_GATES });
|
|
18
41
|
const resetForm = useCallback(() => {
|
|
19
42
|
setName('');
|
|
20
43
|
setDescription('');
|
|
21
44
|
setAttachments([]);
|
|
45
|
+
setApprovalGates({ ...EMPTY_GATES });
|
|
22
46
|
}, []);
|
|
23
47
|
const handleOpenChange = useCallback((nextOpen) => {
|
|
24
48
|
if (!nextOpen) {
|
|
@@ -30,8 +54,17 @@ export function FeatureCreateDrawer({ open, onClose, onSubmit, isSubmitting = fa
|
|
|
30
54
|
e.preventDefault();
|
|
31
55
|
if (!name.trim())
|
|
32
56
|
return;
|
|
33
|
-
|
|
34
|
-
|
|
57
|
+
const userInput = composeUserInput(name.trim(), description.trim() || undefined, attachments);
|
|
58
|
+
onSubmit({
|
|
59
|
+
userInput,
|
|
60
|
+
repositoryPath,
|
|
61
|
+
approvalGates: {
|
|
62
|
+
allowPrd: approvalGates.allowPrd ?? false,
|
|
63
|
+
allowPlan: approvalGates.allowPlan ?? false,
|
|
64
|
+
allowMerge: approvalGates.allowMerge ?? false,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}, [name, description, attachments, approvalGates, repositoryPath, onSubmit]);
|
|
35
68
|
const handleAddFiles = useCallback(async () => {
|
|
36
69
|
try {
|
|
37
70
|
const files = await pickFiles();
|
|
@@ -46,7 +79,7 @@ export function FeatureCreateDrawer({ open, onClose, onSubmit, isSubmitting = fa
|
|
|
46
79
|
const handleRemoveFile = useCallback((path) => {
|
|
47
80
|
setAttachments((prev) => prev.filter((f) => f.path !== path));
|
|
48
81
|
}, []);
|
|
49
|
-
return (_jsx(Drawer, { direction: "right", modal: false, open: open, onOpenChange: handleOpenChange, children: _jsxs(DrawerContent, { direction: "right", className: "w-96", showCloseButton: false, children: [_jsx("button", { type: "button", "aria-label": "Close", onClick: onClose, className: "ring-offset-background focus:ring-ring absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden", children: _jsx(XIcon, { className: "size-4" }) }), _jsxs(DrawerHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "h-2.5 w-2.5 shrink-0 rounded-full bg-blue-500" }), _jsx(DrawerTitle, { children: "NEW FEATURE" })] }), _jsx(DrawerDescription, { asChild: true, children: _jsx("div", { children: _jsx(Badge, { variant: "secondary", children: "Creating..." }) }) })] }), _jsx(Separator, {}), _jsx("div", { className: "flex-1 overflow-y-auto p-4", children: _jsxs("form", { id: "create-feature-form", onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { htmlFor: "feature-name", className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "FEATURE NAME" }), _jsx(Input, { id: "feature-name", placeholder: "e.g. GitHub OAuth Login", value: name, onChange: (e) => setName(e.target.value), required: true, disabled: isSubmitting })] }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { htmlFor: "feature-description", className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "DESCRIPTION" }), _jsx(Textarea, { id: "feature-description", placeholder: "Describe what this feature does...", value: description, onChange: (e) => setDescription(e.target.value), rows: 4, disabled: isSubmitting })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs(Label, { className: "text-muted-foreground text-xs font-semibold tracking-wider", children: ["ATTACHMENTS", attachments.length > 0 && (_jsxs("span", { className: "text-muted-foreground/60 ml-1.5", children: ["(", attachments.length, ")"] }))] }), _jsxs(Button, { type: "button", variant: "outline", size: "xs", onClick: handleAddFiles, disabled: isSubmitting, children: [_jsx(PlusIcon, { className: "size-3" }), "Add Files"] })] }), attachments.length > 0 && (_jsx("div", { className: "flex flex-col gap-2", children: attachments.map((file) => (_jsx(AttachmentCard, { file: file, onRemove: () => handleRemoveFile(file.path), disabled: isSubmitting }, file.path))) }))] })] }) }), _jsx(Separator, {}), _jsxs(DrawerFooter, { className: "flex-row justify-end gap-2", children: [_jsx(Button, { variant: "outline", onClick: onClose, disabled: isSubmitting, children: "Cancel" }), _jsx(Button, { type: "submit", form: "create-feature-form", disabled: !name.trim() || isSubmitting, children: isSubmitting ? 'Creating...' : '+ Create Feature' })] })] }) }));
|
|
82
|
+
return (_jsx(Drawer, { direction: "right", modal: false, open: open, onOpenChange: handleOpenChange, children: _jsxs(DrawerContent, { direction: "right", className: "w-96", showCloseButton: false, children: [_jsx("button", { type: "button", "aria-label": "Close", onClick: onClose, className: "ring-offset-background focus:ring-ring absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden", children: _jsx(XIcon, { className: "size-4" }) }), _jsxs(DrawerHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "h-2.5 w-2.5 shrink-0 rounded-full bg-blue-500" }), _jsx(DrawerTitle, { children: "NEW FEATURE" })] }), _jsx(DrawerDescription, { asChild: true, children: _jsx("div", { children: _jsx(Badge, { variant: "secondary", children: "Creating..." }) }) })] }), _jsx(Separator, {}), _jsx("div", { className: "flex-1 overflow-y-auto p-4", children: _jsxs("form", { id: "create-feature-form", onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { htmlFor: "feature-name", className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "FEATURE NAME" }), _jsx(Input, { id: "feature-name", placeholder: "e.g. GitHub OAuth Login", value: name, onChange: (e) => setName(e.target.value), required: true, disabled: isSubmitting })] }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { htmlFor: "feature-description", className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "DESCRIPTION" }), _jsx(Textarea, { id: "feature-description", placeholder: "Describe what this feature does...", value: description, onChange: (e) => setDescription(e.target.value), rows: 4, disabled: isSubmitting })] }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "APPROVE" }), _jsx(CheckboxGroup, { label: "Autonomous Mode", description: "YOLO!", parentAriaLabel: "Auto approve all", options: AUTO_APPROVE_OPTIONS, value: approvalGates, onValueChange: setApprovalGates, disabled: isSubmitting })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs(Label, { className: "text-muted-foreground text-xs font-semibold tracking-wider", children: ["ATTACHMENTS", attachments.length > 0 && (_jsxs("span", { className: "text-muted-foreground/60 ml-1.5", children: ["(", attachments.length, ")"] }))] }), _jsxs(Button, { type: "button", variant: "outline", size: "xs", onClick: handleAddFiles, disabled: isSubmitting, children: [_jsx(PlusIcon, { className: "size-3" }), "Add Files"] })] }), attachments.length > 0 && (_jsx("div", { className: "flex flex-col gap-2", children: attachments.map((file) => (_jsx(AttachmentCard, { file: file, onRemove: () => handleRemoveFile(file.path), disabled: isSubmitting }, file.path))) }))] })] }) }), _jsx(Separator, {}), _jsxs(DrawerFooter, { className: "flex-row justify-end gap-2", children: [_jsx(Button, { variant: "outline", onClick: onClose, disabled: isSubmitting, children: "Cancel" }), _jsx(Button, { type: "submit", form: "create-feature-form", disabled: !name.trim() || isSubmitting, children: isSubmitting ? 'Creating...' : '+ Create Feature' })] })] }) }));
|
|
50
83
|
}
|
|
51
84
|
/* ---------------------------------------------------------------------------
|
|
52
85
|
* Private sub-components & utilities
|
|
@@ -7,6 +7,8 @@ import { FeatureCreateDrawer } from './feature-create-drawer.js';
|
|
|
7
7
|
* ### Form sections
|
|
8
8
|
* - **Feature Name** (required) — text input, submit is disabled when empty
|
|
9
9
|
* - **Description** (optional) — multi-line textarea
|
|
10
|
+
* - **Auto Approve** — Tri-state parent checkbox with 3 child checkboxes (PRD, Plan, Merge).
|
|
11
|
+
* Parent shows indeterminate when some children are selected, checks/unchecks all on click.
|
|
10
12
|
* - **Attachments** (optional) — native OS file picker with extension-based icon system:
|
|
11
13
|
* image → blue, PDF → red, code/text → emerald, generic → gray.
|
|
12
14
|
* Each attachment card shows the **full absolute file path**.
|
|
@@ -16,12 +18,14 @@ import { FeatureCreateDrawer } from './feature-create-drawer.js';
|
|
|
16
18
|
* |------|------|-------------|
|
|
17
19
|
* | `open` | `boolean` | Controls drawer visibility |
|
|
18
20
|
* | `onClose` | `() => void` | Called on dismiss (close button, cancel, or backdrop) |
|
|
19
|
-
* | `onSubmit` | `(data:
|
|
21
|
+
* | `onSubmit` | `(data: CreateFeatureInput) => void` | Called with `{ userInput, repositoryPath, approvalGates }` |
|
|
22
|
+
* | `repositoryPath` | `string` | Repository path (mandatory) included in the submitted data |
|
|
20
23
|
* | `isSubmitting` | `boolean` | Disables all fields and shows "Creating..." on submit button |
|
|
21
24
|
*
|
|
22
25
|
* ### Behavior
|
|
23
|
-
* - Form resets (name, description, attachments
|
|
26
|
+
* - Form resets (name, description, attachments, checkboxes) when the drawer closes
|
|
24
27
|
* - Submit button is disabled when name is empty OR `isSubmitting` is true
|
|
28
|
+
* - `approvalGates` always included: `{ allowPrd, allowPlan, allowMerge }` (all false by default)
|
|
25
29
|
* - Non-modal (`modal={false}`) — canvas stays interactive behind the drawer
|
|
26
30
|
* - Fixed width: 384px (`w-96`)
|
|
27
31
|
* - Attachments use native OS file picker via `pickFiles()` — returns `FileAttachment[]`
|
|
@@ -50,11 +54,31 @@ export declare const ValidationDisabled: Story;
|
|
|
50
54
|
/** Click to open the drawer for quick visual inspection. */
|
|
51
55
|
export declare const PreOpened: Story;
|
|
52
56
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
57
|
+
* All checkboxes unchecked (default) — drawer opens with no auto-approve gates enabled.
|
|
58
|
+
* This is the safest default: every phase requires manual review.
|
|
59
|
+
*/
|
|
60
|
+
export declare const AllUnchecked: Story;
|
|
61
|
+
/**
|
|
62
|
+
* All checkboxes checked via parent "Auto Approve" — fully autonomous mode.
|
|
63
|
+
* Clicking the parent checkbox selects all children at once.
|
|
64
|
+
*/
|
|
65
|
+
export declare const AllChecked: Story;
|
|
66
|
+
/**
|
|
67
|
+
* Only PRD checkbox checked — parent shows **indeterminate** (dash) state,
|
|
68
|
+
* indicating partial selection at a glance.
|
|
69
|
+
*/
|
|
70
|
+
export declare const PrdOnly: Story;
|
|
71
|
+
/**
|
|
72
|
+
* Only Merge checkbox checked — parent shows **indeterminate** (dash) state.
|
|
73
|
+
* Manual review for requirements and planning, auto-approve merge to Done.
|
|
74
|
+
*/
|
|
75
|
+
export declare const MergeOnly: Story;
|
|
76
|
+
/**
|
|
77
|
+
* Fully interactive story — open the drawer, fill the form, toggle checkboxes,
|
|
78
|
+
* and click "Add Files" to attach files via the native OS file picker.
|
|
55
79
|
*
|
|
56
|
-
* **Submitted data** is displayed in a styled panel showing `
|
|
57
|
-
* with
|
|
80
|
+
* **Submitted data** is displayed in a styled panel showing `CreateFeatureInput`
|
|
81
|
+
* with `approvalGates: { allowPrd, allowPlan, allowMerge }`.
|
|
58
82
|
*
|
|
59
83
|
* In Storybook, the native picker won't work (no backend), but submitted data
|
|
60
84
|
* would show the paths if files were attached programmatically.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-create-drawer.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAQ9D
|
|
1
|
+
{"version":3,"file":"feature-create-drawer.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAQ9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,mBAAmB,CA8B1C,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAiDlD,uFAAuF;AACvF,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,SAAS,EAAE,KAiBvB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,UAAU,EAAE,KAExB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAMhC,CAAC;AAEF,4DAA4D;AAC5D,eAAO,MAAM,SAAS,EAAE,KAMvB,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,KAM1B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,UAAU,EAAE,KAUxB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,OAAO,EAAE,KAUrB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,SAAS,EAAE,KAUvB,CAAC;AAMF;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,EAAE,KAsCzB,CAAC"}
|
|
@@ -13,6 +13,8 @@ import { Button } from '../../ui/button.js';
|
|
|
13
13
|
* ### Form sections
|
|
14
14
|
* - **Feature Name** (required) — text input, submit is disabled when empty
|
|
15
15
|
* - **Description** (optional) — multi-line textarea
|
|
16
|
+
* - **Auto Approve** — Tri-state parent checkbox with 3 child checkboxes (PRD, Plan, Merge).
|
|
17
|
+
* Parent shows indeterminate when some children are selected, checks/unchecks all on click.
|
|
16
18
|
* - **Attachments** (optional) — native OS file picker with extension-based icon system:
|
|
17
19
|
* image → blue, PDF → red, code/text → emerald, generic → gray.
|
|
18
20
|
* Each attachment card shows the **full absolute file path**.
|
|
@@ -22,12 +24,14 @@ import { Button } from '../../ui/button.js';
|
|
|
22
24
|
* |------|------|-------------|
|
|
23
25
|
* | `open` | `boolean` | Controls drawer visibility |
|
|
24
26
|
* | `onClose` | `() => void` | Called on dismiss (close button, cancel, or backdrop) |
|
|
25
|
-
* | `onSubmit` | `(data:
|
|
27
|
+
* | `onSubmit` | `(data: CreateFeatureInput) => void` | Called with `{ userInput, repositoryPath, approvalGates }` |
|
|
28
|
+
* | `repositoryPath` | `string` | Repository path (mandatory) included in the submitted data |
|
|
26
29
|
* | `isSubmitting` | `boolean` | Disables all fields and shows "Creating..." on submit button |
|
|
27
30
|
*
|
|
28
31
|
* ### Behavior
|
|
29
|
-
* - Form resets (name, description, attachments
|
|
32
|
+
* - Form resets (name, description, attachments, checkboxes) when the drawer closes
|
|
30
33
|
* - Submit button is disabled when name is empty OR `isSubmitting` is true
|
|
34
|
+
* - `approvalGates` always included: `{ allowPrd, allowPlan, allowMerge }` (all false by default)
|
|
31
35
|
* - Non-modal (`modal={false}`) — canvas stays interactive behind the drawer
|
|
32
36
|
* - Fixed width: 384px (`w-96`)
|
|
33
37
|
* - Attachments use native OS file picker via `pickFiles()` — returns `FileAttachment[]`
|
|
@@ -49,7 +53,11 @@ const meta = {
|
|
|
49
53
|
description: 'Callback fired when the drawer is dismissed (close button, cancel, or backdrop click).',
|
|
50
54
|
},
|
|
51
55
|
onSubmit: {
|
|
52
|
-
description: 'Callback fired with `
|
|
56
|
+
description: 'Callback fired with `CreateFeatureInput` when the form is submitted. Receives `{ userInput, repositoryPath, approvalGates }`.',
|
|
57
|
+
},
|
|
58
|
+
repositoryPath: {
|
|
59
|
+
control: 'text',
|
|
60
|
+
description: 'Repository path (mandatory) included in the submitted data.',
|
|
53
61
|
},
|
|
54
62
|
isSubmitting: {
|
|
55
63
|
control: 'boolean',
|
|
@@ -75,7 +83,7 @@ function CreateDrawerTrigger({ isSubmitting = false, label = 'Open Create Featur
|
|
|
75
83
|
}, onSubmit: (data) => {
|
|
76
84
|
logSubmit(data);
|
|
77
85
|
setOpen(false);
|
|
78
|
-
}, isSubmitting: isSubmitting })] }));
|
|
86
|
+
}, repositoryPath: "/Users/dev/my-repo", isSubmitting: isSubmitting })] }));
|
|
79
87
|
}
|
|
80
88
|
/* ---------------------------------------------------------------------------
|
|
81
89
|
* Per-state stories
|
|
@@ -127,15 +135,71 @@ export const PreOpened = {
|
|
|
127
135
|
await userEvent.click(canvas.getByRole('button', { name: 'Open Drawer' }));
|
|
128
136
|
},
|
|
129
137
|
};
|
|
138
|
+
/* ---------------------------------------------------------------------------
|
|
139
|
+
* Auto-approve checkbox stories
|
|
140
|
+
* ------------------------------------------------------------------------- */
|
|
141
|
+
/**
|
|
142
|
+
* All checkboxes unchecked (default) — drawer opens with no auto-approve gates enabled.
|
|
143
|
+
* This is the safest default: every phase requires manual review.
|
|
144
|
+
*/
|
|
145
|
+
export const AllUnchecked = {
|
|
146
|
+
render: () => _jsx(CreateDrawerTrigger, { label: "Open (All Unchecked)" }),
|
|
147
|
+
play: async ({ canvasElement }) => {
|
|
148
|
+
const canvas = within(canvasElement);
|
|
149
|
+
await userEvent.click(canvas.getByRole('button', { name: 'Open (All Unchecked)' }));
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* All checkboxes checked via parent "Auto Approve" — fully autonomous mode.
|
|
154
|
+
* Clicking the parent checkbox selects all children at once.
|
|
155
|
+
*/
|
|
156
|
+
export const AllChecked = {
|
|
157
|
+
render: () => _jsx(CreateDrawerTrigger, { label: "Open (All Checked)" }),
|
|
158
|
+
play: async ({ canvasElement }) => {
|
|
159
|
+
const canvas = within(canvasElement);
|
|
160
|
+
await userEvent.click(canvas.getByRole('button', { name: 'Open (All Checked)' }));
|
|
161
|
+
const body = within(canvasElement.ownerDocument.body);
|
|
162
|
+
const parent = await body.findByLabelText('Auto approve all');
|
|
163
|
+
await userEvent.click(parent);
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
/**
|
|
167
|
+
* Only PRD checkbox checked — parent shows **indeterminate** (dash) state,
|
|
168
|
+
* indicating partial selection at a glance.
|
|
169
|
+
*/
|
|
170
|
+
export const PrdOnly = {
|
|
171
|
+
render: () => _jsx(CreateDrawerTrigger, { label: "Open (PRD Only)" }),
|
|
172
|
+
play: async ({ canvasElement }) => {
|
|
173
|
+
const canvas = within(canvasElement);
|
|
174
|
+
await userEvent.click(canvas.getByRole('button', { name: 'Open (PRD Only)' }));
|
|
175
|
+
const body = within(canvasElement.ownerDocument.body);
|
|
176
|
+
const prd = await body.findByLabelText('PRD');
|
|
177
|
+
await userEvent.click(prd);
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
/**
|
|
181
|
+
* Only Merge checkbox checked — parent shows **indeterminate** (dash) state.
|
|
182
|
+
* Manual review for requirements and planning, auto-approve merge to Done.
|
|
183
|
+
*/
|
|
184
|
+
export const MergeOnly = {
|
|
185
|
+
render: () => _jsx(CreateDrawerTrigger, { label: "Open (Merge Only)" }),
|
|
186
|
+
play: async ({ canvasElement }) => {
|
|
187
|
+
const canvas = within(canvasElement);
|
|
188
|
+
await userEvent.click(canvas.getByRole('button', { name: 'Open (Merge Only)' }));
|
|
189
|
+
const body = within(canvasElement.ownerDocument.body);
|
|
190
|
+
const merge = await body.findByLabelText('Merge');
|
|
191
|
+
await userEvent.click(merge);
|
|
192
|
+
},
|
|
193
|
+
};
|
|
130
194
|
/* ---------------------------------------------------------------------------
|
|
131
195
|
* Interactive story — full paths shown in submitted data panel
|
|
132
196
|
* ------------------------------------------------------------------------- */
|
|
133
197
|
/**
|
|
134
|
-
* Fully interactive story — open the drawer, fill the form,
|
|
135
|
-
* "Add Files" to attach files via the native OS file picker.
|
|
198
|
+
* Fully interactive story — open the drawer, fill the form, toggle checkboxes,
|
|
199
|
+
* and click "Add Files" to attach files via the native OS file picker.
|
|
136
200
|
*
|
|
137
|
-
* **Submitted data** is displayed in a styled panel showing `
|
|
138
|
-
* with
|
|
201
|
+
* **Submitted data** is displayed in a styled panel showing `CreateFeatureInput`
|
|
202
|
+
* with `approvalGates: { allowPrd, allowPlan, allowMerge }`.
|
|
139
203
|
*
|
|
140
204
|
* In Storybook, the native picker won't work (no backend), but submitted data
|
|
141
205
|
* would show the paths if files were attached programmatically.
|
|
@@ -144,21 +208,13 @@ export const Interactive = {
|
|
|
144
208
|
render: function InteractiveRender() {
|
|
145
209
|
const [open, setOpen] = useState(false);
|
|
146
210
|
const [submitted, setSubmitted] = useState(null);
|
|
147
|
-
return (_jsxs("div", { className: "flex h-screen items-start gap-4 p-4", children: [_jsxs("div", { className: "flex flex-col gap-3", children: [_jsx(Button, { variant: "outline", onClick: () => setOpen(true), children: "Open Create Feature" }), submitted ? (_jsxs("div", { className: "bg-muted/50 max-w-sm rounded-md border p-3", children: [_jsx("span", { className: "text-muted-foreground mb-1 text-xs font-semibold tracking-wider uppercase", children: "Last submitted" }), _jsx("pre", { className: "mt-1 text-xs whitespace-pre-wrap", children: JSON.stringify({
|
|
148
|
-
name: submitted.name,
|
|
149
|
-
description: submitted.description,
|
|
150
|
-
attachments: submitted.attachments.map((f) => ({
|
|
151
|
-
path: f.path,
|
|
152
|
-
name: f.name,
|
|
153
|
-
size: f.size,
|
|
154
|
-
})),
|
|
155
|
-
}, null, 2) })] })) : null] }), _jsx(FeatureCreateDrawer, { open: open, onClose: () => {
|
|
211
|
+
return (_jsxs("div", { className: "flex h-screen items-start gap-4 p-4", children: [_jsxs("div", { className: "flex flex-col gap-3", children: [_jsx(Button, { variant: "outline", onClick: () => setOpen(true), children: "Open Create Feature" }), submitted ? (_jsxs("div", { className: "bg-muted/50 max-w-sm rounded-md border p-3", children: [_jsx("span", { className: "text-muted-foreground mb-1 text-xs font-semibold tracking-wider uppercase", children: "Last submitted" }), _jsx("pre", { className: "mt-1 text-xs whitespace-pre-wrap", children: JSON.stringify(submitted, null, 2) })] })) : null] }), _jsx(FeatureCreateDrawer, { open: open, onClose: () => {
|
|
156
212
|
setOpen(false);
|
|
157
213
|
logClose();
|
|
158
214
|
}, onSubmit: (data) => {
|
|
159
215
|
logSubmit(data);
|
|
160
216
|
setSubmitted(data);
|
|
161
217
|
setOpen(false);
|
|
162
|
-
} })] }));
|
|
218
|
+
}, repositoryPath: "/Users/dev/my-repo" })] }));
|
|
163
219
|
},
|
|
164
220
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { FeatureCreateDrawer, type FeatureCreateDrawerProps, type
|
|
1
|
+
export { FeatureCreateDrawer, type FeatureCreateDrawerProps, type FileAttachment, } from './feature-create-drawer.js';
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,KAAK,wBAAwB,EAC7B,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,KAAK,wBAAwB,EAC7B,KAAK,cAAc,GACpB,MAAM,yBAAyB,CAAC"}
|
|
@@ -20,10 +20,10 @@ function StateBadge({ data }) {
|
|
|
20
20
|
return (_jsxs("div", { className: cn('flex items-center gap-2 rounded-full px-3 py-1.5 text-sm font-medium', config.badgeBgClass, config.badgeClass), children: [data.state === 'running' ? (_jsx(CometSpinner, { size: "sm", className: "shrink-0" })) : (_jsx(Icon, { className: "h-4 w-4 shrink-0" })), _jsx("span", { children: config.label })] }));
|
|
21
21
|
}
|
|
22
22
|
function DetailsSection({ data }) {
|
|
23
|
-
const hasAnyDetail = data.description ?? data.
|
|
23
|
+
const hasAnyDetail = data.description ?? data.agentType ?? data.runtime ?? data.blockedBy ?? data.errorMessage;
|
|
24
24
|
if (!hasAnyDetail)
|
|
25
25
|
return null;
|
|
26
|
-
return (_jsxs("div", { "data-testid": "feature-drawer-details", className: "flex flex-col gap-3 p-4", children: [data.description ? _jsx(DetailRow, { label: "Description", value: data.description }) : null, data.
|
|
26
|
+
return (_jsxs("div", { "data-testid": "feature-drawer-details", className: "flex flex-col gap-3 p-4", children: [data.description ? _jsx(DetailRow, { label: "Description", value: data.description }) : null, data.agentType ? _jsx(DetailRow, { label: "Agent", value: data.agentType }) : null, data.runtime ? _jsx(DetailRow, { label: "Runtime", value: data.runtime }) : null, data.blockedBy ? _jsx(DetailRow, { label: "Blocked by", value: data.blockedBy }) : null, data.errorMessage ? _jsx(DetailRow, { label: "Error", value: data.errorMessage }) : null] }));
|
|
27
27
|
}
|
|
28
28
|
function DetailRow({ label, value }) {
|
|
29
29
|
return (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: label }), _jsx("span", { className: "text-sm", children: value })] }));
|
package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.stories.js
CHANGED
|
@@ -30,7 +30,7 @@ const runningData = {
|
|
|
30
30
|
lifecycle: 'implementation',
|
|
31
31
|
state: 'running',
|
|
32
32
|
progress: 45,
|
|
33
|
-
|
|
33
|
+
agentType: 'claude-code',
|
|
34
34
|
repositoryPath: '/home/user/my-repo',
|
|
35
35
|
branch: 'feat/auth-module',
|
|
36
36
|
};
|
|
@@ -118,7 +118,7 @@ export const AllFields = {
|
|
|
118
118
|
lifecycle: 'implementation',
|
|
119
119
|
state: 'running',
|
|
120
120
|
progress: 68,
|
|
121
|
-
|
|
121
|
+
agentType: 'cursor',
|
|
122
122
|
runtime: '3h 15m',
|
|
123
123
|
blockedBy: 'Database Migration',
|
|
124
124
|
errorMessage: 'Token refresh failed: invalid_grant',
|
|
@@ -169,7 +169,7 @@ function AllLifecyclesRender() {
|
|
|
169
169
|
lifecycle: phase,
|
|
170
170
|
state: 'running',
|
|
171
171
|
progress: 50,
|
|
172
|
-
|
|
172
|
+
agentType: 'claude-code',
|
|
173
173
|
repositoryPath: '/home/user/my-repo',
|
|
174
174
|
branch: 'feat/feature-name',
|
|
175
175
|
});
|
package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.d.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { type LucideIcon } from 'lucide-react';
|
|
2
2
|
import type { Node } from '@xyflow/react';
|
|
3
|
+
import type { AgentTypeValue } from './agent-type-icons.js';
|
|
3
4
|
export type FeatureNodeState = 'running' | 'action-required' | 'done' | 'blocked' | 'error';
|
|
4
5
|
export type FeatureLifecyclePhase = 'requirements' | 'research' | 'implementation' | 'review' | 'deploy' | 'maintain';
|
|
5
6
|
/** Human-readable display labels for lifecycle phases. */
|
|
6
7
|
export declare const lifecycleDisplayLabels: Record<FeatureLifecyclePhase, string>;
|
|
8
|
+
/** Present-participle verbs for the running badge, keyed by lifecycle phase. */
|
|
9
|
+
export declare const lifecycleRunningVerbs: Record<FeatureLifecyclePhase, string>;
|
|
7
10
|
export interface FeatureNodeData {
|
|
8
11
|
[key: string]: unknown;
|
|
9
12
|
name: string;
|
|
@@ -22,8 +25,8 @@ export interface FeatureNodeData {
|
|
|
22
25
|
blockedBy?: string;
|
|
23
26
|
/** Short error message for error state */
|
|
24
27
|
errorMessage?: string;
|
|
25
|
-
/** Agent
|
|
26
|
-
|
|
28
|
+
/** Agent executor type (e.g. "claude-code", "cursor"). */
|
|
29
|
+
agentType?: AgentTypeValue;
|
|
27
30
|
onAction?: () => void;
|
|
28
31
|
onSettings?: () => void;
|
|
29
32
|
showHandles?: boolean;
|
package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-node-state-config.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node-state-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmD,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAChG,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"feature-node-state-config.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node-state-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmD,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAChG,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,iBAAiB,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAE5F,MAAM,MAAM,qBAAqB,GAC7B,cAAc,GACd,UAAU,GACV,gBAAgB,GAChB,QAAQ,GACR,QAAQ,GACR,UAAU,CAAC;AAEf,0DAA0D;AAC1D,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAOxE,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAOvE,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,qBAAqB,CAAC;IACjC,KAAK,EAAE,gBAAgB,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,cAAc,EAAE,MAAM,CAAC;IACvB,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,eAAe,EAAE,aAAa,CAAC,CAAC;AAEnE,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,gBAAgB,EAAE,sBAAsB,CAmDnF,CAAC"}
|
package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.js
CHANGED
|
@@ -8,6 +8,15 @@ export const lifecycleDisplayLabels = {
|
|
|
8
8
|
deploy: 'DEPLOY & QA',
|
|
9
9
|
maintain: 'COMPLETED',
|
|
10
10
|
};
|
|
11
|
+
/** Present-participle verbs for the running badge, keyed by lifecycle phase. */
|
|
12
|
+
export const lifecycleRunningVerbs = {
|
|
13
|
+
requirements: 'Analyzing',
|
|
14
|
+
research: 'Researching',
|
|
15
|
+
implementation: 'Implementing',
|
|
16
|
+
review: 'Reviewing',
|
|
17
|
+
deploy: 'Deploying',
|
|
18
|
+
maintain: 'Maintaining',
|
|
19
|
+
};
|
|
11
20
|
export const featureNodeStateConfig = {
|
|
12
21
|
running: {
|
|
13
22
|
icon: Loader2,
|