@shepai/cli 1.24.0 → 1.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/dist/packages/core/src/application/use-cases/features/create/metadata-generator.d.ts.map +1 -1
  2. package/dist/packages/core/src/application/use-cases/features/create/metadata-generator.js +3 -0
  3. package/dist/packages/core/src/infrastructure/di/populate-use-cases-bridge.d.ts.map +1 -1
  4. package/dist/packages/core/src/infrastructure/di/populate-use-cases-bridge.js +2 -0
  5. package/dist/packages/core/src/infrastructure/di/use-cases-bridge.d.ts +9 -1
  6. package/dist/packages/core/src/infrastructure/di/use-cases-bridge.d.ts.map +1 -1
  7. package/dist/packages/core/src/infrastructure/di/use-cases-bridge.js +17 -1
  8. package/dist/src/presentation/web/app/api/features/create/route.d.ts +5 -0
  9. package/dist/src/presentation/web/app/api/features/create/route.d.ts.map +1 -0
  10. package/dist/src/presentation/web/app/api/features/create/route.js +32 -0
  11. package/dist/src/presentation/web/app/layout.d.ts.map +1 -1
  12. package/dist/src/presentation/web/app/layout.js +2 -1
  13. package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map +1 -1
  14. package/dist/src/presentation/web/components/features/control-center/control-center-inner.js +2 -2
  15. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts +1 -0
  16. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts.map +1 -1
  17. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js +45 -9
  18. package/dist/src/presentation/web/dev-server.js +17 -4
  19. package/dist/tsconfig.build.tsbuildinfo +1 -1
  20. package/package.json +1 -1
  21. package/web/.next/BUILD_ID +1 -1
  22. package/web/.next/app-path-routes-manifest.json +1 -0
  23. package/web/.next/build-manifest.json +2 -2
  24. package/web/.next/cache/.previewinfo +1 -1
  25. package/web/.next/cache/.rscinfo +1 -1
  26. package/web/.next/cache/.tsbuildinfo +1 -1
  27. package/web/.next/cache/config.json +3 -3
  28. package/web/.next/fallback-build-manifest.json +2 -2
  29. package/web/.next/prerender-manifest.json +3 -3
  30. package/web/.next/required-server-files.js +1 -1
  31. package/web/.next/required-server-files.json +1 -1
  32. package/web/.next/routes-manifest.json +6 -0
  33. package/web/.next/server/app/_global-error.html +2 -2
  34. package/web/.next/server/app/_global-error.rsc +1 -1
  35. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  36. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  37. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  38. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  39. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  40. package/web/.next/server/app/_not-found/page.js +1 -1
  41. package/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  42. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  43. package/web/.next/server/app/_not-found.html +2 -2
  44. package/web/.next/server/app/_not-found.rsc +13 -12
  45. package/web/.next/server/app/_not-found.segments/_full.segment.rsc +13 -12
  46. package/web/.next/server/app/_not-found.segments/_head.segment.rsc +3 -3
  47. package/web/.next/server/app/_not-found.segments/_index.segment.rsc +5 -4
  48. package/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  49. package/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  50. package/web/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  51. package/web/.next/server/app/api/dialog/pick-files/route.js +2 -2
  52. package/web/.next/server/app/api/dialog/pick-files/route.js.nft.json +1 -1
  53. package/web/.next/server/app/api/dialog/pick-folder/route.js +2 -2
  54. package/web/.next/server/app/api/dialog/pick-folder/route.js.nft.json +1 -1
  55. package/web/.next/server/app/api/features/create/route/app-paths-manifest.json +3 -0
  56. package/web/.next/server/app/api/features/create/route/build-manifest.json +11 -0
  57. package/web/.next/server/app/api/features/create/route/server-reference-manifest.json +4 -0
  58. package/web/.next/server/app/api/features/create/route.js +6 -0
  59. package/web/.next/server/app/api/features/create/route.js.map +5 -0
  60. package/web/.next/server/app/api/features/create/route.js.nft.json +1 -0
  61. package/web/.next/server/app/api/features/create/route_client-reference-manifest.js +2 -0
  62. package/web/.next/server/app/page.js +1 -1
  63. package/web/.next/server/app/page.js.nft.json +1 -1
  64. package/web/.next/server/app/page_client-reference-manifest.js +1 -1
  65. package/web/.next/server/app/version/page.js +1 -1
  66. package/web/.next/server/app/version/page.js.nft.json +1 -1
  67. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  68. package/web/.next/server/app/version.html +2 -2
  69. package/web/.next/server/app/version.rsc +12 -11
  70. package/web/.next/server/app/version.segments/_full.segment.rsc +12 -11
  71. package/web/.next/server/app/version.segments/_head.segment.rsc +1 -1
  72. package/web/.next/server/app/version.segments/_index.segment.rsc +3 -2
  73. package/web/.next/server/app/version.segments/_tree.segment.rsc +1 -1
  74. package/web/.next/server/app/version.segments/version/__PAGE__.segment.rsc +2 -2
  75. package/web/.next/server/app/version.segments/version.segment.rsc +1 -1
  76. package/web/.next/server/app-paths-manifest.json +1 -0
  77. package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_features_create_route_actions_de49a114.js +3 -0
  78. package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_features_create_route_actions_de49a114.js.map +1 -0
  79. package/web/.next/server/chunks/[root-of-the-server]__4e987cf3._.js +8 -0
  80. package/web/.next/server/chunks/[root-of-the-server]__4e987cf3._.js.map +1 -0
  81. package/web/.next/server/chunks/{[root-of-the-server]__22cabd43._.js → [root-of-the-server]__a6bf88cc._.js} +3 -3
  82. package/web/.next/server/chunks/{[root-of-the-server]__22cabd43._.js.map → [root-of-the-server]__a6bf88cc._.js.map} +1 -1
  83. package/web/.next/server/chunks/{[root-of-the-server]__00833626._.js → [root-of-the-server]__ba6c5641._.js} +2 -2
  84. package/web/.next/server/chunks/{[root-of-the-server]__00833626._.js.map → [root-of-the-server]__ba6c5641._.js.map} +1 -1
  85. package/web/.next/server/chunks/[root-of-the-server]__ec87d735._.js +3 -0
  86. package/web/.next/server/chunks/{[root-of-the-server]__95785ac7._.js.map → [root-of-the-server]__ec87d735._.js.map} +1 -1
  87. package/web/.next/server/chunks/ssr/[root-of-the-server]__a21ef212._.js +3 -0
  88. package/web/.next/server/chunks/ssr/[root-of-the-server]__a21ef212._.js.map +1 -0
  89. package/web/.next/server/chunks/ssr/[root-of-the-server]__f12b60be._.js +1 -1
  90. package/web/.next/server/chunks/ssr/_09afa42a._.js +1 -1
  91. package/web/.next/server/chunks/ssr/_09afa42a._.js.map +1 -1
  92. package/web/.next/server/chunks/ssr/_1c3962d7._.js +10 -0
  93. package/web/.next/server/chunks/ssr/_1c3962d7._.js.map +1 -0
  94. package/web/.next/server/chunks/ssr/src_presentation_web_67178b88._.js +13 -0
  95. package/web/.next/server/chunks/ssr/src_presentation_web_67178b88._.js.map +1 -0
  96. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +3 -0
  97. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +1 -0
  98. package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js +1 -1
  99. package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js.map +1 -1
  100. package/web/.next/server/pages/404.html +2 -2
  101. package/web/.next/server/pages/500.html +2 -2
  102. package/web/.next/server/server-reference-manifest.js +1 -1
  103. package/web/.next/server/server-reference-manifest.json +1 -1
  104. package/web/.next/static/chunks/{f6b5484377f4e78e.js → 1c236dcc303f3033.js} +1 -1
  105. package/web/.next/static/chunks/78ef4acb26e4d6ea.js +1 -0
  106. package/web/.next/static/chunks/79009d1ca0d944d2.js +5 -0
  107. package/web/.next/trace +1 -1
  108. package/web/.next/trace-build +1 -1
  109. package/web/.next/types/link.d.ts +1 -0
  110. package/web/.next/types/routes.d.ts +2 -1
  111. package/web/.next/types/validator.ts +9 -0
  112. package/web/.next/server/chunks/[root-of-the-server]__95785ac7._.js +0 -3
  113. package/web/.next/server/chunks/ssr/[root-of-the-server]__7c64f85c._.js +0 -3
  114. package/web/.next/server/chunks/ssr/[root-of-the-server]__7c64f85c._.js.map +0 -1
  115. package/web/.next/server/chunks/ssr/_1f9347c8._.js +0 -7
  116. package/web/.next/server/chunks/ssr/_1f9347c8._.js.map +0 -1
  117. package/web/.next/server/chunks/ssr/_a0c04017._.js +0 -6
  118. package/web/.next/server/chunks/ssr/_a0c04017._.js.map +0 -1
  119. package/web/.next/server/chunks/ssr/src_presentation_web_372075f9._.js +0 -13
  120. package/web/.next/server/chunks/ssr/src_presentation_web_372075f9._.js.map +0 -1
  121. package/web/.next/static/chunks/8bc34062bc0f4ddc.js +0 -1
  122. package/web/.next/static/chunks/f5099c5888ef9a74.js +0 -5
  123. /package/web/.next/static/{LOfelu4nKYgB7PB1XlRqc → -dFLqLT3RMg1ueqbAhsJk}/_buildManifest.js +0 -0
  124. /package/web/.next/static/{LOfelu4nKYgB7PB1XlRqc → -dFLqLT3RMg1ueqbAhsJk}/_clientMiddlewareManifest.json +0 -0
  125. /package/web/.next/static/{LOfelu4nKYgB7PB1XlRqc → -dFLqLT3RMg1ueqbAhsJk}/_ssgManifest.js +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"metadata-generator.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/src/application/use-cases/features/create/metadata-generator.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mEAAmE,CAAC;AAChH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAKlD,qBACa,iBAAiB;IAG1B,OAAO,CAAC,QAAQ,CAAC,gBAAgB;gBAAhB,gBAAgB,EAAE,sBAAsB;IAG3D;;;OAGG;IACG,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAyCnE;;OAEG;IACH,OAAO,CAAC,cAAc;IAgBtB;;;OAGG;IACH,OAAO,CAAC,MAAM;CAgBf"}
1
+ {"version":3,"file":"metadata-generator.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/src/application/use-cases/features/create/metadata-generator.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mEAAmE,CAAC;AAChH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAKlD,qBACa,iBAAiB;IAG1B,OAAO,CAAC,QAAQ,CAAC,gBAAgB;gBAAhB,gBAAgB,EAAE,sBAAsB;IAG3D;;;OAGG;IACG,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IA4CnE;;OAEG;IACH,OAAO,CAAC,cAAc;IAgBtB;;;OAGG;IACH,OAAO,CAAC,MAAM;CAgBf"}
@@ -53,6 +53,9 @@ JSON only, no markdown fences.`;
53
53
  silent: true,
54
54
  });
55
55
  const cleaned = this.stripCodeFence(result.result);
56
+ if (!cleaned) {
57
+ throw new Error('AI returned empty response for metadata generation');
58
+ }
56
59
  const parsed = JSON.parse(cleaned);
57
60
  if (!parsed.slug || !parsed.name || !parsed.description) {
58
61
  throw new Error('Missing required fields in AI response');
@@ -1 +1 @@
1
- {"version":3,"file":"populate-use-cases-bridge.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/src/infrastructure/di/populate-use-cases-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAKpD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAOnE"}
1
+ {"version":3,"file":"populate-use-cases-bridge.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/src/infrastructure/di/populate-use-cases-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAMpD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAQnE"}
@@ -12,6 +12,7 @@
12
12
  * @see use-cases-bridge.ts for the reader side (web-safe).
13
13
  */
14
14
  import { ListFeaturesUseCase } from '../../application/use-cases/features/list-features.use-case.js';
15
+ import { CreateFeatureUseCase } from '../../application/use-cases/features/create/create-feature.use-case.js';
15
16
  import { SHEP_USE_CASES_KEY } from './use-cases-bridge.js';
16
17
  /**
17
18
  * Populate the globalThis bridge with resolved use cases from the DI container.
@@ -21,6 +22,7 @@ export function populateUseCasesBridge(c) {
21
22
  const bridge = {
22
23
  listFeatures: c.resolve(ListFeaturesUseCase),
23
24
  agentRunRepo: c.resolve('IAgentRunRepository'),
25
+ createFeature: c.resolve(CreateFeatureUseCase),
24
26
  };
25
27
  globalThis[SHEP_USE_CASES_KEY] = bridge;
26
28
  process[SHEP_USE_CASES_KEY] = bridge;
@@ -17,7 +17,8 @@
17
17
  */
18
18
  import type { Feature, AgentRun } from '../../domain/generated/output.js';
19
19
  import type { FeatureListFilters } from '../../application/ports/output/repositories/feature-repository.interface.js';
20
- export type { FeatureListFilters };
20
+ import type { CreateFeatureInput, CreateFeatureResult } from '../../application/use-cases/features/create/types.js';
21
+ export type { FeatureListFilters, CreateFeatureInput, CreateFeatureResult };
21
22
  /** The globalThis key shared between writer and reader. */
22
23
  export declare const SHEP_USE_CASES_KEY = "__shepUseCases";
23
24
  /**
@@ -31,4 +32,11 @@ export declare function getFeatures(filters?: FeatureListFilters): Promise<Featu
31
32
  * Returns null if the DI bridge is not initialized or the run is not found.
32
33
  */
33
34
  export declare function getAgentRun(id: string): Promise<AgentRun | null>;
35
+ /**
36
+ * Create a new feature via the DI-managed use case.
37
+ * Unlike getFeatures/getAgentRun, this function propagates errors so callers
38
+ * (API routes) can return proper HTTP error responses and surface messages to the user.
39
+ * Throws if the bridge is not initialized or the use case fails.
40
+ */
41
+ export declare function createFeature(input: CreateFeatureInput): Promise<CreateFeatureResult>;
34
42
  //# sourceMappingURL=use-cases-bridge.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-cases-bridge.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/src/infrastructure/di/use-cases-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAC1E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6EAA6E,CAAC;AAEtH,YAAY,EAAE,kBAAkB,EAAE,CAAC;AAEnC,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,mBAAmB,CAAC;AAiCnD;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAWlF;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAStE"}
1
+ {"version":3,"file":"use-cases-bridge.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/src/infrastructure/di/use-cases-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAC1E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6EAA6E,CAAC;AACtH,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,sDAAsD,CAAC;AAE9D,YAAY,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,CAAC;AAE5E,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,mBAAmB,CAAC;AAqCnD;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAWlF;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAStE;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAO3F"}
@@ -23,10 +23,13 @@ function isShepUseCases(value) {
23
23
  const maybe = value;
24
24
  const listFeatures = maybe.listFeatures;
25
25
  const agentRunRepo = maybe.agentRunRepo;
26
+ const createFeature = maybe.createFeature;
26
27
  return (!!listFeatures &&
27
28
  typeof listFeatures.execute === 'function' &&
28
29
  !!agentRunRepo &&
29
- typeof agentRunRepo.findById === 'function');
30
+ typeof agentRunRepo.findById === 'function' &&
31
+ !!createFeature &&
32
+ typeof createFeature.execute === 'function');
30
33
  }
31
34
  function getUseCases() {
32
35
  const globalBridge = globalThis[SHEP_USE_CASES_KEY];
@@ -70,3 +73,16 @@ export async function getAgentRun(id) {
70
73
  return null;
71
74
  }
72
75
  }
76
+ /**
77
+ * Create a new feature via the DI-managed use case.
78
+ * Unlike getFeatures/getAgentRun, this function propagates errors so callers
79
+ * (API routes) can return proper HTTP error responses and surface messages to the user.
80
+ * Throws if the bridge is not initialized or the use case fails.
81
+ */
82
+ export async function createFeature(input) {
83
+ const useCases = getUseCases();
84
+ if (!useCases?.createFeature) {
85
+ throw new Error('Use-cases bridge is not initialized. Ensure the CLI process is running.');
86
+ }
87
+ return useCases.createFeature.execute(input);
88
+ }
@@ -0,0 +1,5 @@
1
+ import { NextResponse } from 'next/server';
2
+ export declare function POST(request: Request): Promise<NextResponse<{
3
+ error: string;
4
+ }> | NextResponse<import("../../../../../../../packages/core/src/infrastructure/di/use-cases-bridge.js").CreateFeatureResult>>;
5
+ //# sourceMappingURL=route.d.ts.map
@@ -0,0 +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;AA2B3C,wBAAsB,IAAI,CAAC,OAAO,EAAE,OAAO;;kGA0B1C"}
@@ -0,0 +1,32 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { createFeature } from '../../../../../../../packages/core/src/infrastructure/di/use-cases-bridge.js';
3
+ function composeUserInput(name, description, attachments) {
4
+ let userInput = `Feature: ${name}`;
5
+ if (description) {
6
+ userInput += `\n\n${description}`;
7
+ }
8
+ if (attachments && attachments.length > 0) {
9
+ const paths = attachments.map((a) => `- ${a.path}`).join('\n');
10
+ userInput += `\n\nAttached files:\n${paths}`;
11
+ }
12
+ return userInput;
13
+ }
14
+ export async function POST(request) {
15
+ const body = await request.json();
16
+ const { name, description, repositoryPath, attachments } = body;
17
+ if (!name?.trim()) {
18
+ return NextResponse.json({ error: 'name is required' }, { status: 400 });
19
+ }
20
+ if (!repositoryPath?.trim()) {
21
+ return NextResponse.json({ error: 'repositoryPath is required' }, { status: 400 });
22
+ }
23
+ const userInput = composeUserInput(name, description, attachments);
24
+ try {
25
+ const result = await createFeature({ userInput, repositoryPath });
26
+ return NextResponse.json(result);
27
+ }
28
+ catch (error) {
29
+ const message = error instanceof Error ? error.message : 'Failed to create feature';
30
+ return NextResponse.json({ error: message }, { status: 500 });
31
+ }
32
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/app/layout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,6BAA6B,CAAC;AACrC,OAAO,eAAe,CAAC;AAGvB,eAAO,MAAM,QAAQ,EAAE,QAItB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,EACjC,QAAQ,GACT,EAAE,QAAQ,CAAC;IACV,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,CAAC,2CAyBD"}
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/app/layout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,6BAA6B,CAAC;AACrC,OAAO,eAAe,CAAC;AAIvB,eAAO,MAAM,QAAQ,EAAE,QAItB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,EACjC,QAAQ,GACT,EAAE,QAAQ,CAAC;IACV,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,CAAC,2CA0BD"}
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import '@xyflow/react/dist/base.css';
3
3
  import './globals.css';
4
4
  import { AppShell } from '../components/layouts/app-shell/index.js';
5
+ import { Toaster } from '../components/ui/sonner.js';
5
6
  export const metadata = {
6
7
  title: 'Shep AI',
7
8
  description: 'Autonomous AI Native SDLC Platform - Automate the development cycle from idea to deploy',
@@ -19,5 +20,5 @@ export default function RootLayout({ children, }) {
19
20
  } catch (e) {}
20
21
  })();
21
22
  `,
22
- } }) }), _jsx("body", { className: "min-h-screen antialiased", children: _jsx(AppShell, { children: children }) })] }));
23
+ } }) }), _jsxs("body", { className: "min-h-screen antialiased", children: [_jsx(AppShell, { children: children }), _jsx(Toaster, {})] })] }));
23
24
  }
@@ -1 +1 @@
1
- {"version":3,"file":"control-center-inner.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center-inner.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAE1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAK5E,UAAU,uBAAuB;IAC/B,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,YAAY,EAAE,IAAI,EAAE,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,EAAE,uBAAuB,2CAgDzF"}
1
+ {"version":3,"file":"control-center-inner.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center-inner.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAE1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAK5E,UAAU,uBAAuB;IAC/B,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,YAAY,EAAE,IAAI,EAAE,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,EAAE,uBAAuB,2CAkDzF"}
@@ -6,12 +6,12 @@ import { FeatureDrawer, FeatureCreateDrawer } from '../../common/index.js';
6
6
  import { ControlCenterEmptyState } from './control-center-empty-state.js';
7
7
  import { useControlCenterState } from './use-control-center-state.js';
8
8
  export function ControlCenterInner({ initialNodes, initialEdges }) {
9
- const { nodes, edges, selectedNode, isCreateDrawerOpen, onNodesChange, handleConnect, handleAddFeature, handleAddFeatureToRepo, handleAddFeatureToFeature, handleAddRepository, handleNodeClick, clearSelection, handleCreateFeatureSubmit, closeCreateDrawer, } = useControlCenterState(initialNodes, initialEdges);
9
+ const { nodes, edges, selectedNode, isCreateDrawerOpen, isSubmitting, onNodesChange, handleConnect, handleAddFeature, handleAddFeatureToRepo, handleAddFeatureToFeature, handleAddRepository, handleNodeClick, clearSelection, handleCreateFeatureSubmit, closeCreateDrawer, } = useControlCenterState(initialNodes, initialEdges);
10
10
  // Listen for global "open create drawer" events from the sidebar
11
11
  useEffect(() => {
12
12
  const handler = () => handleAddFeature();
13
13
  window.addEventListener('shep:open-create-drawer', handler);
14
14
  return () => window.removeEventListener('shep:open-create-drawer', handler);
15
15
  }, [handleAddFeature]);
16
- return (_jsxs(_Fragment, { children: [_jsx(FeaturesCanvas, { nodes: nodes, edges: edges, onNodesChange: onNodesChange, onConnect: handleConnect, onAddFeature: handleAddFeature, onNodeAction: handleAddFeatureToFeature, onNodeClick: handleNodeClick, onPaneClick: clearSelection, onRepositoryAdd: handleAddFeatureToRepo, onRepositorySelect: handleAddRepository, emptyState: _jsx(ControlCenterEmptyState, { onRepositorySelect: handleAddRepository }) }), _jsx(FeatureDrawer, { selectedNode: selectedNode, onClose: clearSelection }), _jsx(FeatureCreateDrawer, { open: isCreateDrawerOpen, onClose: closeCreateDrawer, onSubmit: handleCreateFeatureSubmit })] }));
16
+ return (_jsxs(_Fragment, { children: [_jsx(FeaturesCanvas, { nodes: nodes, edges: edges, onNodesChange: onNodesChange, onConnect: handleConnect, onAddFeature: handleAddFeature, onNodeAction: handleAddFeatureToFeature, onNodeClick: handleNodeClick, onPaneClick: clearSelection, onRepositoryAdd: handleAddFeatureToRepo, onRepositorySelect: handleAddRepository, emptyState: _jsx(ControlCenterEmptyState, { onRepositorySelect: handleAddRepository }) }), _jsx(FeatureDrawer, { selectedNode: selectedNode, onClose: clearSelection }), _jsx(FeatureCreateDrawer, { open: isCreateDrawerOpen, onClose: closeCreateDrawer, onSubmit: handleCreateFeatureSubmit, isSubmitting: isSubmitting })] }));
17
17
  }
@@ -8,6 +8,7 @@ export interface ControlCenterState {
8
8
  edges: Edge[];
9
9
  selectedNode: FeatureNodeData | null;
10
10
  isCreateDrawerOpen: boolean;
11
+ isSubmitting: boolean;
11
12
  onNodesChange: (changes: NodeChange<CanvasNodeType>[]) => void;
12
13
  handleConnect: (connection: Connection) => void;
13
14
  clearSelection: () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"use-control-center-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/use-control-center-state.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2CAA2C,CAAC;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAEhF,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,YAAY,EAAE,eAAe,GAAG,IAAI,CAAC;IACrC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,aAAa,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,cAAc,CAAC,EAAE,KAAK,IAAI,CAAC;IAC/D,aAAa,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAChD,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,eAAe,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACzE,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,sBAAsB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,yBAAyB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3D,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,YAAY,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,yBAAyB,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAC;IACjE,iBAAiB,EAAE,MAAM,IAAI,CAAC;CAC/B;AAID,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CAkRpB"}
1
+ {"version":3,"file":"use-control-center-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/use-control-center-state.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2CAA2C,CAAC;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAEhF,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,YAAY,EAAE,eAAe,GAAG,IAAI,CAAC;IACrC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,cAAc,CAAC,EAAE,KAAK,IAAI,CAAC;IAC/D,aAAa,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAChD,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,eAAe,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACzE,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,sBAAsB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,yBAAyB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3D,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,YAAY,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,yBAAyB,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAC;IACjE,iBAAiB,EAAE,MAAM,IAAI,CAAC;CAC/B;AAID,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CAmTpB"}
@@ -1,13 +1,18 @@
1
1
  'use client';
2
2
  import { useState, useCallback, useEffect } from 'react';
3
+ import { useRouter } from 'next/navigation';
4
+ import { toast } from 'sonner';
3
5
  import { applyNodeChanges } from '@xyflow/react';
4
6
  import { layoutWithDagre } from '../../../lib/layout-with-dagre.js';
5
7
  let nextFeatureId = 0;
6
8
  export function useControlCenterState(initialNodes, initialEdges) {
9
+ const router = useRouter();
7
10
  const [nodes, setNodes] = useState(initialNodes);
8
11
  const [edges, setEdges] = useState(initialEdges);
9
12
  const [selectedNode, setSelectedNode] = useState(null);
10
13
  const [isCreateDrawerOpen, setIsCreateDrawerOpen] = useState(false);
14
+ const [isSubmitting, setIsSubmitting] = useState(false);
15
+ const [pendingRepoNodeId, setPendingRepoNodeId] = useState(null);
11
16
  const onNodesChange = useCallback((changes) => {
12
17
  setNodes((ns) => applyNodeChanges(changes, ns));
13
18
  }, []);
@@ -161,19 +166,49 @@ export function useControlCenterState(initialNodes, initialEdges) {
161
166
  setSelectedNode(null);
162
167
  setIsCreateDrawerOpen(true);
163
168
  }, []);
164
- const handleCreateFeatureSubmit = useCallback((data) => {
165
- setIsCreateDrawerOpen(false);
166
- createFeatureNode(null, {
167
- name: data.name,
168
- description: data.description || undefined,
169
- });
170
- }, [createFeatureNode]);
169
+ const handleCreateFeatureSubmit = useCallback(async (data) => {
170
+ if (!pendingRepoNodeId) {
171
+ toast.error('Please create the feature from a repository node.');
172
+ return;
173
+ }
174
+ const repositoryPath = pendingRepoNodeId.replace(/^repo-/, '');
175
+ setIsSubmitting(true);
176
+ try {
177
+ const response = await fetch('/api/features/create', {
178
+ method: 'POST',
179
+ headers: { 'Content-Type': 'application/json' },
180
+ body: JSON.stringify({
181
+ name: data.name,
182
+ description: data.description || '',
183
+ repositoryPath,
184
+ attachments: data.attachments,
185
+ }),
186
+ });
187
+ if (!response.ok) {
188
+ const body = await response.json();
189
+ toast.error(body.error ?? 'Failed to create feature');
190
+ return;
191
+ }
192
+ setIsCreateDrawerOpen(false);
193
+ setPendingRepoNodeId(null);
194
+ toast.success('Feature created successfully');
195
+ router.refresh();
196
+ }
197
+ catch {
198
+ toast.error('Failed to create feature');
199
+ }
200
+ finally {
201
+ setIsSubmitting(false);
202
+ }
203
+ }, [pendingRepoNodeId, router]);
171
204
  const closeCreateDrawer = useCallback(() => {
172
205
  setIsCreateDrawerOpen(false);
173
206
  }, []);
174
207
  const handleAddFeatureToRepo = useCallback((repoNodeId) => {
175
- createFeatureNode(repoNodeId);
176
- }, [createFeatureNode]);
208
+ setSelectedNode(null);
209
+ setPendingRepoNodeId(repoNodeId);
210
+ setIsCreateDrawerOpen(true);
211
+ }, []);
177
212
  const handleAddFeatureToFeature = useCallback((featureNodeId) => {
178
213
  createFeatureNode(featureNodeId);
179
214
  }, [createFeatureNode]);
@@ -220,6 +255,7 @@ export function useControlCenterState(initialNodes, initialEdges) {
220
255
  edges,
221
256
  selectedNode,
222
257
  isCreateDrawerOpen,
258
+ isSubmitting,
223
259
  onNodesChange,
224
260
  handleConnect,
225
261
  clearSelection,
@@ -39,21 +39,34 @@ async function main() {
39
39
  const server = http.createServer((req, res) => {
40
40
  handle(req, res);
41
41
  });
42
+ // Forward WebSocket upgrades to Next.js for HMR/Fast Refresh
43
+ server.on('upgrade', (req, socket, head) => {
44
+ app.getUpgradeHandler()(req, socket, head);
45
+ });
42
46
  await new Promise((resolve, reject) => {
43
47
  server.on('error', reject);
44
48
  server.listen(port, 'localhost', resolve);
45
49
  });
46
50
  console.log(`[dev-server] Ready at http://localhost:${port}`);
47
- // Graceful shutdown
51
+ // Graceful shutdown with timeout to avoid hanging on open connections
48
52
  let isShuttingDown = false;
49
53
  const shutdown = async () => {
50
54
  if (isShuttingDown)
51
55
  return;
52
56
  isShuttingDown = true;
53
57
  console.log('\n[dev-server] Shutting down...');
54
- await new Promise((resolve) => server.close(() => resolve()));
55
- await app.close();
56
- process.exit(0);
58
+ const forceExit = setTimeout(() => process.exit(0), 2000);
59
+ try {
60
+ server.closeAllConnections();
61
+ await Promise.all([
62
+ new Promise((resolve) => server.close(() => resolve())),
63
+ app.close(),
64
+ ]);
65
+ }
66
+ finally {
67
+ clearTimeout(forceExit);
68
+ process.exit(0);
69
+ }
57
70
  };
58
71
  process.on('SIGINT', shutdown);
59
72
  process.on('SIGTERM', shutdown);