@shepai/cli 1.44.2 → 1.46.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 (81) hide show
  1. package/dist/src/presentation/cli/commands/repo/index.d.ts +18 -0
  2. package/dist/src/presentation/cli/commands/repo/index.d.ts.map +1 -0
  3. package/dist/src/presentation/cli/commands/repo/index.js +25 -0
  4. package/dist/src/presentation/cli/commands/repo/ls.command.d.ts +11 -0
  5. package/dist/src/presentation/cli/commands/repo/ls.command.d.ts.map +1 -0
  6. package/dist/src/presentation/cli/commands/repo/ls.command.js +64 -0
  7. package/dist/src/presentation/cli/commands/repo/resolve-repository.d.ts +10 -0
  8. package/dist/src/presentation/cli/commands/repo/resolve-repository.d.ts.map +1 -0
  9. package/dist/src/presentation/cli/commands/repo/resolve-repository.js +24 -0
  10. package/dist/src/presentation/cli/commands/repo/show.command.d.ts +11 -0
  11. package/dist/src/presentation/cli/commands/repo/show.command.d.ts.map +1 -0
  12. package/dist/src/presentation/cli/commands/repo/show.command.js +95 -0
  13. package/dist/src/presentation/cli/index.d.ts +1 -0
  14. package/dist/src/presentation/cli/index.d.ts.map +1 -1
  15. package/dist/src/presentation/cli/index.js +3 -0
  16. package/dist/src/presentation/web/app/page.d.ts.map +1 -1
  17. package/dist/src/presentation/web/app/page.js +18 -10
  18. package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map +1 -1
  19. package/dist/src/presentation/web/components/features/control-center/control-center-inner.js +4 -0
  20. package/dist/src/presentation/web/lib/layout-with-dagre.d.ts.map +1 -1
  21. package/dist/src/presentation/web/lib/layout-with-dagre.js +3 -0
  22. package/dist/tsconfig.build.tsbuildinfo +1 -1
  23. package/package.json +1 -1
  24. package/web/.next/BUILD_ID +1 -1
  25. package/web/.next/build-manifest.json +2 -2
  26. package/web/.next/cache/.previewinfo +1 -1
  27. package/web/.next/cache/.rscinfo +1 -1
  28. package/web/.next/cache/.tsbuildinfo +1 -1
  29. package/web/.next/cache/config.json +3 -3
  30. package/web/.next/fallback-build-manifest.json +2 -2
  31. package/web/.next/prerender-manifest.json +3 -3
  32. package/web/.next/required-server-files.js +1 -1
  33. package/web/.next/required-server-files.json +1 -1
  34. package/web/.next/server/app/_global-error.html +2 -2
  35. package/web/.next/server/app/_global-error.rsc +1 -1
  36. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  37. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  38. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  39. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  40. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  41. package/web/.next/server/app/page/server-reference-manifest.json +11 -11
  42. package/web/.next/server/app/page_client-reference-manifest.js +1 -1
  43. package/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js +2 -2
  44. package/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js.map +1 -1
  45. package/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js +1 -1
  46. package/web/.next/server/chunks/ssr/[root-of-the-server]__4e8ca89c._.js +2 -2
  47. package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js +1 -1
  48. package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js.map +1 -1
  49. package/web/.next/server/pages/500.html +2 -2
  50. package/web/.next/server/server-reference-manifest.js +1 -1
  51. package/web/.next/server/server-reference-manifest.json +12 -12
  52. package/web/.next/standalone/src/presentation/web/.next/BUILD_ID +1 -1
  53. package/web/.next/standalone/src/presentation/web/.next/build-manifest.json +2 -2
  54. package/web/.next/standalone/src/presentation/web/.next/prerender-manifest.json +3 -3
  55. package/web/.next/standalone/src/presentation/web/.next/required-server-files.json +1 -1
  56. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.html +2 -2
  57. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.rsc +1 -1
  58. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  59. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  60. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  61. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  62. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  63. package/web/.next/standalone/src/presentation/web/.next/server/app/page/server-reference-manifest.json +11 -11
  64. package/web/.next/standalone/src/presentation/web/.next/server/app/page_client-reference-manifest.js +1 -1
  65. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js +2 -2
  66. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js +1 -1
  67. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__4e8ca89c._.js +2 -2
  68. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js +1 -1
  69. package/web/.next/standalone/src/presentation/web/.next/server/pages/500.html +2 -2
  70. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.js +1 -1
  71. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.json +12 -12
  72. package/web/.next/standalone/src/presentation/web/app/page.tsx +19 -10
  73. package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center-inner.tsx +17 -0
  74. package/web/.next/standalone/src/presentation/web/lib/layout-with-dagre.ts +3 -0
  75. package/web/.next/standalone/src/presentation/web/server.js +1 -1
  76. package/web/.next/static/chunks/{fde97f50747ffbcc.js → ecf44fa8a155c854.js} +2 -2
  77. package/web/.next/trace +1 -1
  78. package/web/.next/trace-build +1 -1
  79. /package/web/.next/static/{DFCnsrgJiPWig4e8ubqSt → nM-uXIiW9k_0Z_GM_4bI4}/_buildManifest.js +0 -0
  80. /package/web/.next/static/{DFCnsrgJiPWig4e8ubqSt → nM-uXIiW9k_0Z_GM_4bI4}/_clientMiddlewareManifest.json +0 -0
  81. /package/web/.next/static/{DFCnsrgJiPWig4e8ubqSt → nM-uXIiW9k_0Z_GM_4bI4}/_ssgManifest.js +0 -0
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Repo Command
3
+ *
4
+ * Top-level repo command with subcommands for managing tracked repositories.
5
+ *
6
+ * Usage:
7
+ * shep repo [subcommand]
8
+ *
9
+ * Subcommands:
10
+ * shep repo ls List tracked repositories
11
+ * shep repo show <id> Display details of a tracked repository
12
+ */
13
+ import { Command } from 'commander';
14
+ /**
15
+ * Create the repo command with all subcommands
16
+ */
17
+ export declare function createRepoCommand(): Command;
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/repo/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAO3C"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Repo Command
3
+ *
4
+ * Top-level repo command with subcommands for managing tracked repositories.
5
+ *
6
+ * Usage:
7
+ * shep repo [subcommand]
8
+ *
9
+ * Subcommands:
10
+ * shep repo ls List tracked repositories
11
+ * shep repo show <id> Display details of a tracked repository
12
+ */
13
+ import { Command } from 'commander';
14
+ import { createShowCommand } from './show.command.js';
15
+ import { createLsCommand } from './ls.command.js';
16
+ /**
17
+ * Create the repo command with all subcommands
18
+ */
19
+ export function createRepoCommand() {
20
+ const repo = new Command('repo')
21
+ .description('Manage tracked repositories')
22
+ .addCommand(createLsCommand())
23
+ .addCommand(createShowCommand());
24
+ return repo;
25
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Repo List Command
3
+ *
4
+ * List all tracked repositories in a formatted table.
5
+ *
6
+ * Usage:
7
+ * shep repo ls
8
+ */
9
+ import { Command } from 'commander';
10
+ export declare function createLsCommand(): Command;
11
+ //# sourceMappingURL=ls.command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ls.command.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/repo/ls.command.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmBpC,wBAAgB,eAAe,IAAI,OAAO,CAuCzC"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Repo List Command
3
+ *
4
+ * List all tracked repositories in a formatted table.
5
+ *
6
+ * Usage:
7
+ * shep repo ls
8
+ */
9
+ import { Command } from 'commander';
10
+ import { container } from '../../../../../packages/core/src/infrastructure/di/container.js';
11
+ import { ListRepositoriesUseCase } from '../../../../../packages/core/src/application/use-cases/repositories/list-repositories.use-case.js';
12
+ import { colors, messages, renderListView } from '../../ui/index.js';
13
+ /** Format a duration in ms to a compact human-readable string. */
14
+ function formatDuration(ms) {
15
+ const seconds = Math.floor(ms / 1000);
16
+ if (seconds < 60)
17
+ return `${seconds}s`;
18
+ const minutes = Math.floor(seconds / 60);
19
+ if (minutes < 60)
20
+ return `${minutes}m`;
21
+ const hours = Math.floor(minutes / 60);
22
+ const remMin = minutes % 60;
23
+ if (hours < 24)
24
+ return remMin > 0 ? `${hours}h ${remMin}m` : `${hours}h`;
25
+ const days = Math.floor(hours / 24);
26
+ return `${days}d`;
27
+ }
28
+ export function createLsCommand() {
29
+ return new Command('ls').description('List tracked repositories').action(async () => {
30
+ try {
31
+ const useCase = container.resolve(ListRepositoriesUseCase);
32
+ const featureRepo = container.resolve('IFeatureRepository');
33
+ const repositories = await useCase.execute();
34
+ const now = Date.now();
35
+ const rows = await Promise.all(repositories.map(async (repo) => {
36
+ const features = await featureRepo.list({ repositoryPath: repo.path });
37
+ return [
38
+ repo.id.substring(0, 8),
39
+ repo.name,
40
+ String(features.length),
41
+ repo.path,
42
+ colors.muted(`${formatDuration(now - new Date(repo.createdAt).getTime())} ago`),
43
+ ];
44
+ }));
45
+ renderListView({
46
+ title: 'Repositories',
47
+ columns: [
48
+ { label: 'ID', width: 10 },
49
+ { label: 'Name', width: 28 },
50
+ { label: 'Features', width: 10 },
51
+ { label: 'Path', width: 36 },
52
+ { label: 'Created', width: 12 },
53
+ ],
54
+ rows,
55
+ emptyMessage: 'No repositories found',
56
+ });
57
+ }
58
+ catch (error) {
59
+ const err = error instanceof Error ? error : new Error(String(error));
60
+ messages.error('Failed to list repositories', err);
61
+ process.exitCode = 1;
62
+ }
63
+ });
64
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Shared helper: resolve a repository by exact or prefix ID.
3
+ */
4
+ import type { Repository } from '../../../../../packages/core/src/domain/generated/output.js';
5
+ export declare function resolveRepository(id: string): Promise<{
6
+ repository: Repository;
7
+ } | {
8
+ error: string;
9
+ }>;
10
+ //# sourceMappingURL=resolve-repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-repository.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/repo/resolve-repository.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAE/D,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,MAAM,GACT,OAAO,CAAC;IAAE,UAAU,EAAE,UAAU,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAqBzD"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Shared helper: resolve a repository by exact or prefix ID.
3
+ */
4
+ import { container } from '../../../../../packages/core/src/infrastructure/di/container.js';
5
+ export async function resolveRepository(id) {
6
+ const repo = container.resolve('IRepositoryRepository');
7
+ // Try exact match first
8
+ const exact = await repo.findById(id);
9
+ if (exact)
10
+ return { repository: exact };
11
+ // Try prefix match
12
+ if (id.length < 36) {
13
+ const all = await repo.list();
14
+ const matches = all.filter((r) => r.id.startsWith(id));
15
+ if (matches.length === 1)
16
+ return { repository: matches[0] };
17
+ if (matches.length > 1) {
18
+ return {
19
+ error: `Multiple repositories match prefix "${id}": ${matches.map((m) => m.id.substring(0, 8)).join(', ')}`,
20
+ };
21
+ }
22
+ }
23
+ return { error: `Repository not found: ${id}` };
24
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Repo Show Command
3
+ *
4
+ * Display details of a specific tracked repository.
5
+ *
6
+ * Usage:
7
+ * shep repo show <id>
8
+ */
9
+ import { Command } from 'commander';
10
+ export declare function createShowCommand(): Command;
11
+ //# sourceMappingURL=show.command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"show.command.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/repo/show.command.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoCpC,wBAAgB,iBAAiB,IAAI,OAAO,CAsD3C"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Repo Show Command
3
+ *
4
+ * Display details of a specific tracked repository.
5
+ *
6
+ * Usage:
7
+ * shep repo show <id>
8
+ */
9
+ import { Command } from 'commander';
10
+ import { container } from '../../../../../packages/core/src/infrastructure/di/container.js';
11
+ import { colors, messages, renderDetailView } from '../../ui/index.js';
12
+ import { resolveRepository } from './resolve-repository.js';
13
+ /** Color a lifecycle value to match feat ls status styling. */
14
+ function colorLifecycle(lifecycle) {
15
+ switch (lifecycle) {
16
+ case 'Started':
17
+ return colors.muted(lifecycle);
18
+ case 'Analyze':
19
+ case 'Requirements':
20
+ case 'Research':
21
+ case 'Planning':
22
+ return colors.info(lifecycle);
23
+ case 'Implementation':
24
+ return colors.warning(lifecycle);
25
+ case 'Review':
26
+ return colors.brand(lifecycle);
27
+ case 'Maintain':
28
+ return colors.success(lifecycle);
29
+ default:
30
+ return colors.muted(lifecycle);
31
+ }
32
+ }
33
+ function formatDate(date) {
34
+ if (!date)
35
+ return null;
36
+ try {
37
+ return new Date(date).toLocaleString();
38
+ }
39
+ catch {
40
+ return String(date);
41
+ }
42
+ }
43
+ export function createShowCommand() {
44
+ return new Command('show')
45
+ .description('Display details of a tracked repository')
46
+ .argument('<id>', 'Repository ID (or prefix)')
47
+ .action(async (id) => {
48
+ try {
49
+ const resolved = await resolveRepository(id);
50
+ if ('error' in resolved) {
51
+ messages.error(resolved.error);
52
+ process.exitCode = 1;
53
+ return;
54
+ }
55
+ const repository = resolved.repository;
56
+ const featureRepo = container.resolve('IFeatureRepository');
57
+ const features = await featureRepo.list({ repositoryPath: repository.path });
58
+ const featureFields = features.length > 0
59
+ ? features.map((f) => ({
60
+ label: `${colors.muted(f.id.slice(0, 8))} ${f.name}`,
61
+ value: colorLifecycle(f.lifecycle),
62
+ }))
63
+ : [{ label: 'No features found', value: '—' }];
64
+ renderDetailView({
65
+ title: 'Repository',
66
+ sections: [
67
+ {
68
+ fields: [
69
+ { label: 'ID', value: repository.id },
70
+ { label: 'Name', value: repository.name },
71
+ { label: 'Path', value: repository.path },
72
+ ],
73
+ },
74
+ {
75
+ title: 'Timestamps',
76
+ fields: [
77
+ { label: 'Created', value: formatDate(repository.createdAt) },
78
+ { label: 'Updated', value: formatDate(repository.updatedAt) },
79
+ { label: 'Deleted', value: formatDate(repository.deletedAt) },
80
+ ],
81
+ },
82
+ {
83
+ title: `Features (${features.length})`,
84
+ fields: featureFields,
85
+ },
86
+ ],
87
+ });
88
+ }
89
+ catch (error) {
90
+ const err = error instanceof Error ? error : new Error(String(error));
91
+ messages.error('Failed to show repository', err);
92
+ process.exitCode = 1;
93
+ }
94
+ });
95
+ }
@@ -14,6 +14,7 @@
14
14
  * shep run Run an AI agent workflow
15
15
  * shep agent Manage and view agent runs
16
16
  * shep feat Manage features through the SDLC lifecycle
17
+ * shep repo Manage tracked repositories
17
18
  * shep settings Configure Shep settings
18
19
  * shep upgrade Upgrade Shep CLI to the latest version
19
20
  * shep --version Display version number only
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/presentation/cli/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/presentation/cli/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,kBAAkB,CAAC"}
@@ -14,6 +14,7 @@
14
14
  * shep run Run an AI agent workflow
15
15
  * shep agent Manage and view agent runs
16
16
  * shep feat Manage features through the SDLC lifecycle
17
+ * shep repo Manage tracked repositories
17
18
  * shep settings Configure Shep settings
18
19
  * shep upgrade Upgrade Shep CLI to the latest version
19
20
  * shep --version Display version number only
@@ -31,6 +32,7 @@ import { createUiCommand } from './commands/ui.command.js';
31
32
  import { createRunCommand } from './commands/run.command.js';
32
33
  import { createAgentCommand } from './commands/agent/index.js';
33
34
  import { createFeatCommand } from './commands/feat/index.js';
35
+ import { createRepoCommand } from './commands/repo/index.js';
34
36
  import { createIdeOpenCommand } from './commands/ide-open.command.js';
35
37
  import { createInstallCommand } from './commands/install.command.js';
36
38
  import { createUpgradeCommand } from './commands/upgrade.command.js';
@@ -95,6 +97,7 @@ async function bootstrap() {
95
97
  program.addCommand(createRunCommand());
96
98
  program.addCommand(createAgentCommand());
97
99
  program.addCommand(createFeatCommand());
100
+ program.addCommand(createRepoCommand());
98
101
  program.addCommand(createIdeOpenCommand());
99
102
  program.addCommand(createInstallCommand());
100
103
  program.addCommand(createUpgradeCommand());
@@ -1 +1 @@
1
- {"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/app/page.tsx"],"names":[],"mappings":"AAcA,uFAAuF;AACvF,eAAO,MAAM,OAAO,kBAAkB,CAAC;AAqBvC,wBAA8B,QAAQ,qDA+GrC"}
1
+ {"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/app/page.tsx"],"names":[],"mappings":"AAcA,uFAAuF;AACvF,eAAO,MAAM,OAAO,kBAAkB,CAAC;AAqBvC,wBAA8B,QAAQ,qDAwHrC"}
@@ -93,19 +93,27 @@ export default async function HomePage() {
93
93
  ranksep: 200,
94
94
  nodesep: 15,
95
95
  });
96
- // Position "+ Add Repository" vertically centered with the bottom-most feature node,
97
- // mirroring how Dagre centers repo nodes with their connected features.
96
+ // Position "+ Add Repository" below the last repo node (no-features case) or
97
+ // vertically centered with a bottom feature node (features case).
98
98
  const repoNodes = laid.nodes.filter((n) => n.type === 'repositoryNode');
99
99
  const featureNodes = laid.nodes.filter((n) => n.type === 'featureNode');
100
- const sortedFeatures = [...featureNodes].sort((a, b) => a.position.y - b.position.y);
101
- // Pick the feature node just below center (one past the midpoint index)
102
- const centerIdx = Math.floor(sortedFeatures.length / 2);
103
- const targetFeature = sortedFeatures[centerIdx + 1] ?? sortedFeatures[sortedFeatures.length - 1];
104
100
  const repoX = repoNodes[0]?.position.x ?? 0;
105
- // Center the add-repo node (h=50) with the target feature node (h=140)
106
- const addRepoPosition = targetFeature
107
- ? { x: repoX, y: targetFeature.position.y + 70 - 25 }
108
- : { x: repoX, y: (repoNodes[0]?.position.y ?? 0) + 150 };
101
+ let addRepoPosition;
102
+ if (featureNodes.length > 0) {
103
+ // Mirror how dagre centers repo nodes with their connected features
104
+ const sortedFeatures = [...featureNodes].sort((a, b) => a.position.y - b.position.y);
105
+ const centerIdx = Math.floor(sortedFeatures.length / 2);
106
+ const targetFeature = sortedFeatures[centerIdx + 1] ?? sortedFeatures[sortedFeatures.length - 1];
107
+ // Center the add-repo node (h=50) with the target feature node (h=140)
108
+ addRepoPosition = { x: repoX, y: targetFeature.position.y + 70 - 25 };
109
+ }
110
+ else {
111
+ // No features — place below the bottom-most repo node with a gap
112
+ const lastRepoY = repoNodes.length > 0 ? Math.max(...repoNodes.map((n) => n.position.y)) : 0;
113
+ const repoHeight = 50;
114
+ const gap = 200;
115
+ addRepoPosition = { x: repoX, y: lastRepoY + repoHeight + gap };
116
+ }
109
117
  laid.nodes.push({
110
118
  id: 'add-repository',
111
119
  type: 'addRepositoryNode',
@@ -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;AAO1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAS5E,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,2CA8OzF"}
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;AAO1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAS5E,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,2CA+PzF"}
@@ -138,11 +138,15 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
138
138
  cancelled = true;
139
139
  };
140
140
  }, [techDecisionsFeatureId]);
141
+ const hasRepositories = nodes.some((n) => n.type === 'repositoryNode');
141
142
  // Listen for global "open create drawer" events from the sidebar
142
143
  useEffect(() => {
143
144
  const handler = () => handleAddFeature();
144
145
  window.addEventListener('shep:open-create-drawer', handler);
145
146
  return () => window.removeEventListener('shep:open-create-drawer', handler);
146
147
  }, [handleAddFeature]);
148
+ if (!hasRepositories) {
149
+ return (_jsxs(_Fragment, { children: [_jsx(NotificationPermissionBanner, {}), _jsx(ControlCenterEmptyState, { onRepositorySelect: handleAddRepository }), _jsx(FeatureCreateDrawer, { open: isCreateDrawerOpen, onClose: closeCreateDrawer, onSubmit: handleCreateFeatureSubmit, repositoryPath: pendingRepositoryPath })] }));
150
+ }
147
151
  return (_jsxs(_Fragment, { children: [_jsx(NotificationPermissionBanner, {}), _jsx(FeaturesCanvas, { nodes: nodes, edges: edges, onNodesChange: onNodesChange, onConnect: handleConnect, onAddFeature: handleAddFeature, onNodeAction: handleAddFeatureToFeature, onNodeClick: handleNodeClick, onPaneClick: clearSelection, onRepositoryAdd: handleAddFeatureToRepo, onRepositoryDelete: handleDeleteRepository, onRepositorySelect: handleAddRepository, emptyState: _jsx(ControlCenterEmptyState, { onRepositorySelect: handleAddRepository }) }), _jsx(FeatureDrawer, { selectedNode: showPrdDrawer || showTechDecisionsDrawer ? null : selectedNode, onClose: clearSelection, onDelete: handleDeleteFeature, isDeleting: isDeleting }), questionnaireData ? (_jsx(PrdQuestionnaireDrawer, { open: showPrdDrawer, onClose: clearSelection, featureName: selectedNode?.name ?? '', featureDescription: selectedNode?.description, featureId: selectedNode?.featureId, repositoryPath: selectedNode?.repositoryPath, branch: selectedNode?.branch, specPath: selectedNode?.specPath, data: questionnaireData, selections: prdSelections, onSelect: handlePrdSelect, onRefine: handlePrdRefine, onApprove: handlePrdApprove, onDelete: handleDeleteFeature, isDeleting: isDeleting, isProcessing: isPrdProcessing || isLoadingQuestionnaire })) : null, techDecisionsData ? (_jsx(TechDecisionsDrawer, { open: showTechDecisionsDrawer, onClose: clearSelection, featureName: selectedNode?.name ?? '', featureId: selectedNode?.featureId, repositoryPath: selectedNode?.repositoryPath, branch: selectedNode?.branch, specPath: selectedNode?.specPath, data: techDecisionsData, onRefine: handleTechDecisionsRefine, onApprove: handleTechDecisionsApprove, onDelete: handleDeleteFeature, isDeleting: isDeleting, isProcessing: isLoadingTechDecisions })) : null, _jsx(FeatureCreateDrawer, { open: isCreateDrawerOpen, onClose: closeCreateDrawer, onSubmit: handleCreateFeatureSubmit, repositoryPath: pendingRepositoryPath })] }));
148
152
  }
@@ -1 +1 @@
1
- {"version":3,"file":"layout-with-dagre.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/lib/layout-with-dagre.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,iEAAiE;IACjE,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,0DAA0D;IAC1D,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,qDAAqD;IACrD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAmCD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,IAAI,EAC5C,KAAK,EAAE,CAAC,EAAE,EACV,KAAK,EAAE,IAAI,EAAE,EACb,IAAI,GAAE,aAAkB,GACvB;IAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IAAC,KAAK,EAAE,IAAI,EAAE,CAAA;CAAE,CA+E/B"}
1
+ {"version":3,"file":"layout-with-dagre.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/lib/layout-with-dagre.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,iEAAiE;IACjE,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,0DAA0D;IAC1D,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,qDAAqD;IACrD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAmCD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,IAAI,EAC5C,KAAK,EAAE,CAAC,EAAE,EACV,KAAK,EAAE,IAAI,EAAE,EACb,IAAI,GAAE,aAAkB,GACvB;IAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IAAC,KAAK,EAAE,IAAI,EAAE,CAAA;CAAE,CAkF/B"}
@@ -83,6 +83,9 @@ export function layoutWithDagre(nodes, edges, opts = {}) {
83
83
  maxY = Math.max(maxY, n.position.y + size.height);
84
84
  minX = Math.min(minX, n.position.x);
85
85
  }
86
+ // When no graph nodes exist (all disconnected), default to origin
87
+ if (minX === Infinity)
88
+ minX = 0;
86
89
  for (let i = 0; i < disconnectedNodes.length; i++) {
87
90
  const node = disconnectedNodes[i];
88
91
  const size = getNodeSize(node, nodeSize);