@shepai/cli 1.23.2 → 1.24.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 (94) hide show
  1. package/dist/packages/core/src/infrastructure/di/populate-use-cases-bridge.d.ts +20 -0
  2. package/dist/packages/core/src/infrastructure/di/populate-use-cases-bridge.d.ts.map +1 -0
  3. package/dist/packages/core/src/infrastructure/di/populate-use-cases-bridge.js +27 -0
  4. package/dist/packages/core/src/infrastructure/di/use-cases-bridge.d.ts +34 -0
  5. package/dist/packages/core/src/infrastructure/di/use-cases-bridge.d.ts.map +1 -0
  6. package/dist/packages/core/src/infrastructure/di/use-cases-bridge.js +72 -0
  7. package/dist/packages/core/src/infrastructure/services/git/worktree.service.d.ts +2 -0
  8. package/dist/packages/core/src/infrastructure/services/git/worktree.service.d.ts.map +1 -1
  9. package/dist/packages/core/src/infrastructure/services/git/worktree.service.js +12 -1
  10. package/dist/src/presentation/cli/commands/upgrade.command.d.ts +17 -0
  11. package/dist/src/presentation/cli/commands/upgrade.command.d.ts.map +1 -0
  12. package/dist/src/presentation/cli/commands/upgrade.command.js +115 -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 +9 -8
  16. package/dist/src/presentation/web/app/page.d.ts +2 -0
  17. package/dist/src/presentation/web/app/page.d.ts.map +1 -1
  18. package/dist/src/presentation/web/app/page.js +20 -11
  19. package/dist/src/presentation/web/dev-server.js +2 -6
  20. package/dist/src/presentation/web/next.config.d.ts.map +1 -1
  21. package/dist/src/presentation/web/next.config.js +0 -4
  22. package/dist/tsconfig.build.tsbuildinfo +1 -1
  23. package/package.json +3 -3
  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 -27
  32. package/web/.next/required-server-files.js +1 -4
  33. package/web/.next/required-server-files.json +1 -4
  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/_not-found/page_client-reference-manifest.js +1 -1
  42. package/web/.next/server/app/_not-found.html +2 -2
  43. package/web/.next/server/app/_not-found.rsc +2 -2
  44. package/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  45. package/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  46. package/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  47. package/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  48. package/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  49. package/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  50. package/web/.next/server/app/page.js +2 -2
  51. package/web/.next/server/app/page.js.nft.json +1 -1
  52. package/web/.next/server/app/page_client-reference-manifest.js +1 -1
  53. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  54. package/web/.next/server/app/version.html +2 -2
  55. package/web/.next/server/app/version.rsc +2 -2
  56. package/web/.next/server/app/version.segments/_full.segment.rsc +2 -2
  57. package/web/.next/server/app/version.segments/_head.segment.rsc +1 -1
  58. package/web/.next/server/app/version.segments/_index.segment.rsc +2 -2
  59. package/web/.next/server/app/version.segments/_tree.segment.rsc +2 -2
  60. package/web/.next/server/app/version.segments/version/__PAGE__.segment.rsc +1 -1
  61. package/web/.next/server/app/version.segments/version.segment.rsc +1 -1
  62. package/web/.next/server/chunks/ssr/{[root-of-the-server]__71515c64._.js → [root-of-the-server]__80e5bed2._.js} +2 -2
  63. package/web/.next/server/chunks/ssr/[root-of-the-server]__f12b60be._.js +1 -1
  64. package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js +3 -0
  65. package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js.map +1 -0
  66. package/web/.next/server/pages/404.html +2 -2
  67. package/web/.next/server/pages/500.html +2 -2
  68. package/web/.next/server/server-reference-manifest.js +1 -1
  69. package/web/.next/server/server-reference-manifest.json +1 -1
  70. package/web/.next/static/chunks/74748b4d725c5c74.css +2 -0
  71. package/web/.next/trace +1 -1
  72. package/web/.next/trace-build +1 -1
  73. package/web/next.config.mjs +1 -1
  74. package/dist/src/presentation/web/lib/features.d.ts +0 -30
  75. package/dist/src/presentation/web/lib/features.d.ts.map +0 -1
  76. package/dist/src/presentation/web/lib/features.js +0 -50
  77. package/dist/src/presentation/web/lib/use-cases.d.ts +0 -29
  78. package/dist/src/presentation/web/lib/use-cases.d.ts.map +0 -1
  79. package/dist/src/presentation/web/lib/use-cases.js +0 -46
  80. package/web/.next/server/app/index.html +0 -11
  81. package/web/.next/server/app/index.meta +0 -14
  82. package/web/.next/server/app/index.rsc +0 -16
  83. package/web/.next/server/app/index.segments/__PAGE__.segment.rsc +0 -6
  84. package/web/.next/server/app/index.segments/_full.segment.rsc +0 -16
  85. package/web/.next/server/app/index.segments/_head.segment.rsc +0 -5
  86. package/web/.next/server/app/index.segments/_index.segment.rsc +0 -6
  87. package/web/.next/server/app/index.segments/_tree.segment.rsc +0 -2
  88. package/web/.next/server/chunks/ssr/[root-of-the-server]__8507ac68._.js +0 -3
  89. package/web/.next/server/chunks/ssr/[root-of-the-server]__8507ac68._.js.map +0 -1
  90. package/web/.next/static/chunks/66a2ada783ebc0d4.css +0 -2
  91. /package/web/.next/server/chunks/ssr/{[root-of-the-server]__71515c64._.js.map → [root-of-the-server]__80e5bed2._.js.map} +0 -0
  92. /package/web/.next/static/{3Y7OJekbogJm47SonWxv7 → LOfelu4nKYgB7PB1XlRqc}/_buildManifest.js +0 -0
  93. /package/web/.next/static/{3Y7OJekbogJm47SonWxv7 → LOfelu4nKYgB7PB1XlRqc}/_clientMiddlewareManifest.json +0 -0
  94. /package/web/.next/static/{3Y7OJekbogJm47SonWxv7 → LOfelu4nKYgB7PB1XlRqc}/_ssgManifest.js +0 -0
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Use Cases Bridge — Writer
3
+ *
4
+ * Resolves use cases from the DI container and places them on globalThis
5
+ * so the Next.js web layer can access them without importing DI infrastructure.
6
+ * Called by CLI bootstrap and dev-server after initializeContainer() completes.
7
+ *
8
+ * This file imports tsyringe and decorated use case classes — only import it
9
+ * from Node.js entry points, never from the Next.js web layer (Turbopack
10
+ * cannot bundle native addons or decorator metadata).
11
+ *
12
+ * @see use-cases-bridge.ts for the reader side (web-safe).
13
+ */
14
+ import type { DependencyContainer } from 'tsyringe';
15
+ /**
16
+ * Populate the globalThis bridge with resolved use cases from the DI container.
17
+ * Must be called after `initializeContainer()` completes.
18
+ */
19
+ export declare function populateUseCasesBridge(c: DependencyContainer): void;
20
+ //# sourceMappingURL=populate-use-cases-bridge.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Use Cases Bridge — Writer
3
+ *
4
+ * Resolves use cases from the DI container and places them on globalThis
5
+ * so the Next.js web layer can access them without importing DI infrastructure.
6
+ * Called by CLI bootstrap and dev-server after initializeContainer() completes.
7
+ *
8
+ * This file imports tsyringe and decorated use case classes — only import it
9
+ * from Node.js entry points, never from the Next.js web layer (Turbopack
10
+ * cannot bundle native addons or decorator metadata).
11
+ *
12
+ * @see use-cases-bridge.ts for the reader side (web-safe).
13
+ */
14
+ import { ListFeaturesUseCase } from '../../application/use-cases/features/list-features.use-case.js';
15
+ import { SHEP_USE_CASES_KEY } from './use-cases-bridge.js';
16
+ /**
17
+ * Populate the globalThis bridge with resolved use cases from the DI container.
18
+ * Must be called after `initializeContainer()` completes.
19
+ */
20
+ export function populateUseCasesBridge(c) {
21
+ const bridge = {
22
+ listFeatures: c.resolve(ListFeaturesUseCase),
23
+ agentRunRepo: c.resolve('IAgentRunRepository'),
24
+ };
25
+ globalThis[SHEP_USE_CASES_KEY] = bridge;
26
+ process[SHEP_USE_CASES_KEY] = bridge;
27
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Use Cases Bridge — Reader
3
+ *
4
+ * Why does this exist?
5
+ * Use cases (e.g. ListFeaturesUseCase) are DI-managed classes that depend on
6
+ * injected repositories, which in turn need a live SQLite connection. The web
7
+ * layer (Next.js / Turbopack) cannot run the DI container because it would
8
+ * pull in tsyringe, reflect-metadata, and better-sqlite3 (native C++ addon) —
9
+ * none of which Turbopack can bundle.
10
+ *
11
+ * How it works:
12
+ * 1. CLI bootstrap runs initializeContainer() and resolves fully-wired use cases.
13
+ * 2. The writer (populate-use-cases-bridge.ts) places those instances on globalThis.
14
+ * 3. This reader retrieves them — no DI, no native modules, no decorators.
15
+ *
16
+ * This file is safe to import from the Next.js web layer.
17
+ */
18
+ import type { Feature, AgentRun } from '../../domain/generated/output.js';
19
+ import type { FeatureListFilters } from '../../application/ports/output/repositories/feature-repository.interface.js';
20
+ export type { FeatureListFilters };
21
+ /** The globalThis key shared between writer and reader. */
22
+ export declare const SHEP_USE_CASES_KEY = "__shepUseCases";
23
+ /**
24
+ * List all features for the control center.
25
+ * Returns an empty array if the DI bridge is not initialized
26
+ * (e.g. during static builds or when running outside the CLI process).
27
+ */
28
+ export declare function getFeatures(filters?: FeatureListFilters): Promise<Feature[]>;
29
+ /**
30
+ * Find an agent run by ID.
31
+ * Returns null if the DI bridge is not initialized or the run is not found.
32
+ */
33
+ export declare function getAgentRun(id: string): Promise<AgentRun | null>;
34
+ //# sourceMappingURL=use-cases-bridge.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Use Cases Bridge — Reader
3
+ *
4
+ * Why does this exist?
5
+ * Use cases (e.g. ListFeaturesUseCase) are DI-managed classes that depend on
6
+ * injected repositories, which in turn need a live SQLite connection. The web
7
+ * layer (Next.js / Turbopack) cannot run the DI container because it would
8
+ * pull in tsyringe, reflect-metadata, and better-sqlite3 (native C++ addon) —
9
+ * none of which Turbopack can bundle.
10
+ *
11
+ * How it works:
12
+ * 1. CLI bootstrap runs initializeContainer() and resolves fully-wired use cases.
13
+ * 2. The writer (populate-use-cases-bridge.ts) places those instances on globalThis.
14
+ * 3. This reader retrieves them — no DI, no native modules, no decorators.
15
+ *
16
+ * This file is safe to import from the Next.js web layer.
17
+ */
18
+ /** The globalThis key shared between writer and reader. */
19
+ export const SHEP_USE_CASES_KEY = '__shepUseCases';
20
+ function isShepUseCases(value) {
21
+ if (!value || typeof value !== 'object')
22
+ return false;
23
+ const maybe = value;
24
+ const listFeatures = maybe.listFeatures;
25
+ const agentRunRepo = maybe.agentRunRepo;
26
+ return (!!listFeatures &&
27
+ typeof listFeatures.execute === 'function' &&
28
+ !!agentRunRepo &&
29
+ typeof agentRunRepo.findById === 'function');
30
+ }
31
+ function getUseCases() {
32
+ const globalBridge = globalThis[SHEP_USE_CASES_KEY];
33
+ if (isShepUseCases(globalBridge))
34
+ return globalBridge;
35
+ const processBridge = process[SHEP_USE_CASES_KEY];
36
+ if (isShepUseCases(processBridge))
37
+ return processBridge;
38
+ return undefined;
39
+ }
40
+ /**
41
+ * List all features for the control center.
42
+ * Returns an empty array if the DI bridge is not initialized
43
+ * (e.g. during static builds or when running outside the CLI process).
44
+ */
45
+ export async function getFeatures(filters) {
46
+ const useCases = getUseCases();
47
+ if (!useCases?.listFeatures)
48
+ return [];
49
+ try {
50
+ return await useCases.listFeatures.execute(filters);
51
+ }
52
+ catch (error) {
53
+ // eslint-disable-next-line no-console
54
+ console.error('Failed to load features:', error);
55
+ return [];
56
+ }
57
+ }
58
+ /**
59
+ * Find an agent run by ID.
60
+ * Returns null if the DI bridge is not initialized or the run is not found.
61
+ */
62
+ export async function getAgentRun(id) {
63
+ const useCases = getUseCases();
64
+ if (!useCases?.agentRunRepo)
65
+ return null;
66
+ try {
67
+ return await useCases.agentRunRepo.findById(id);
68
+ }
69
+ catch {
70
+ return null;
71
+ }
72
+ }
@@ -24,5 +24,7 @@ export declare class WorktreeService implements IWorktreeService {
24
24
  getWorktreePath(repoPath: string, branch: string): string;
25
25
  private parseWorktreeOutput;
26
26
  private parseGitError;
27
+ private arePathsEquivalent;
28
+ private normalizeWorktreePath;
27
29
  }
28
30
  //# sourceMappingURL=worktree.service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"worktree.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/git/worktree.service.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACb,MAAM,0EAA0E,CAAC;AAOlF;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,MAAM,KACb,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEjD,qBACa,eAAgB,YAAW,gBAAgB;IAClB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,YAAY;IAErE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAqBrF,MAAM,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ3C,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAO/C,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKhE,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAMzD,OAAO,CAAC,mBAAmB;IA2B3B,OAAO,CAAC,aAAa;CA+BtB"}
1
+ {"version":3,"file":"worktree.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/git/worktree.service.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACb,MAAM,0EAA0E,CAAC;AAOlF;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,MAAM,KACb,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEjD,qBACa,eAAgB,YAAW,gBAAgB;IAClB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,YAAY;IAErE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAqBrF,MAAM,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ3C,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAO/C,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKhE,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAMzD,OAAO,CAAC,mBAAmB;IA2B3B,OAAO,CAAC,aAAa;IAgCrB,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,qBAAqB;CAU9B"}
@@ -38,7 +38,7 @@ let WorktreeService = class WorktreeService {
38
38
  }
39
39
  // Get info about the created worktree
40
40
  const worktrees = await this.list(repoPath);
41
- const created = worktrees.find((w) => w.path === worktreePath);
41
+ const created = worktrees.find((w) => this.arePathsEquivalent(w.path, worktreePath));
42
42
  if (!created) {
43
43
  throw new WorktreeError('Worktree created but not found in list', WorktreeErrorCode.GIT_ERROR);
44
44
  }
@@ -103,6 +103,17 @@ let WorktreeService = class WorktreeService {
103
103
  }
104
104
  return new WorktreeError(message, WorktreeErrorCode.GIT_ERROR, error instanceof Error ? error : undefined);
105
105
  }
106
+ arePathsEquivalent(a, b) {
107
+ return this.normalizeWorktreePath(a) === this.normalizeWorktreePath(b);
108
+ }
109
+ normalizeWorktreePath(input) {
110
+ const normalized = path.normalize(input).replace(/\/+$/, '');
111
+ // On macOS, git can report /private/var/... while callers use /var/...
112
+ if (process.platform === 'darwin' && normalized.startsWith('/private/var/')) {
113
+ return normalized.slice('/private'.length);
114
+ }
115
+ return normalized;
116
+ }
106
117
  };
107
118
  WorktreeService = __decorate([
108
119
  injectable(),
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Upgrade Command
3
+ *
4
+ * Self-upgrades Shep CLI to the latest published version.
5
+ * Checks current vs latest version before running npm install.
6
+ *
7
+ * Usage: shep upgrade
8
+ */
9
+ import { Command } from 'commander';
10
+ import { spawn as defaultSpawn } from 'node:child_process';
11
+ type SpawnFn = typeof defaultSpawn;
12
+ /**
13
+ * Create the upgrade command
14
+ */
15
+ export declare function createUpgradeCommand(spawnFn?: SpawnFn): Command;
16
+ export {};
17
+ //# sourceMappingURL=upgrade.command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade.command.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/cli/commands/upgrade.command.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,KAAK,IAAI,YAAY,EAAqB,MAAM,oBAAoB,CAAC;AAK9E,KAAK,OAAO,GAAG,OAAO,YAAY,CAAC;AAyEnC;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,OAAsB,GAAG,OAAO,CAuC7E"}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Upgrade Command
3
+ *
4
+ * Self-upgrades Shep CLI to the latest published version.
5
+ * Checks current vs latest version before running npm install.
6
+ *
7
+ * Usage: shep upgrade
8
+ */
9
+ import { Command } from 'commander';
10
+ import { spawn as defaultSpawn } from 'node:child_process';
11
+ import { container } from '../../../../packages/core/src/infrastructure/di/container.js';
12
+ import { messages } from '../ui/index.js';
13
+ const VERSION_CHECK_TIMEOUT_MS = 10_000;
14
+ /**
15
+ * Get the latest published version of @shepai/cli from npm registry.
16
+ * Returns null if the check fails (fail-open).
17
+ */
18
+ function getLatestVersion(spawnFn) {
19
+ return new Promise((resolve) => {
20
+ let output = '';
21
+ let settled = false;
22
+ const child = spawnFn('npm', ['view', '@shepai/cli', 'version'], {
23
+ stdio: ['ignore', 'pipe', 'pipe'],
24
+ });
25
+ const timeout = setTimeout(() => {
26
+ if (!settled) {
27
+ settled = true;
28
+ child.kill();
29
+ messages.warning('Could not complete version check (timed out)');
30
+ resolve(null);
31
+ }
32
+ }, VERSION_CHECK_TIMEOUT_MS);
33
+ child.stdout?.on('data', (data) => {
34
+ output += data.toString();
35
+ });
36
+ child.on('close', (code) => {
37
+ if (!settled) {
38
+ settled = true;
39
+ clearTimeout(timeout);
40
+ if (code === 0 && output.trim()) {
41
+ resolve(output.trim());
42
+ }
43
+ else {
44
+ resolve(null);
45
+ }
46
+ }
47
+ });
48
+ child.on('error', () => {
49
+ if (!settled) {
50
+ settled = true;
51
+ clearTimeout(timeout);
52
+ messages.warning('Could not complete version check');
53
+ resolve(null);
54
+ }
55
+ });
56
+ });
57
+ }
58
+ /**
59
+ * Run npm i -g @shepai/cli@latest with inherited stdio.
60
+ * Returns the exit code, or rejects on spawn error.
61
+ */
62
+ function runNpmInstall(spawnFn) {
63
+ return new Promise((resolve, reject) => {
64
+ const child = spawnFn('npm', ['i', '-g', '@shepai/cli@latest'], {
65
+ stdio: 'inherit',
66
+ });
67
+ child.on('close', (code) => {
68
+ resolve(code ?? 1);
69
+ });
70
+ child.on('error', (err) => {
71
+ reject(err);
72
+ });
73
+ });
74
+ }
75
+ /**
76
+ * Create the upgrade command
77
+ */
78
+ export function createUpgradeCommand(spawnFn = defaultSpawn) {
79
+ return new Command('upgrade')
80
+ .description('Upgrade Shep CLI to the latest version')
81
+ .action(async () => {
82
+ try {
83
+ const versionService = container.resolve('IVersionService');
84
+ const { version: currentVersion } = versionService.getVersion();
85
+ // 1. Check latest version (fail-open with timeout)
86
+ const latestVersion = await getLatestVersion(spawnFn);
87
+ // 2. Compare — exit early if up to date
88
+ if (latestVersion && latestVersion === currentVersion) {
89
+ messages.success(`Already up to date (v${currentVersion})`);
90
+ return;
91
+ }
92
+ // 3. Show what we're doing
93
+ if (latestVersion) {
94
+ messages.info(`Upgrading from v${currentVersion} to v${latestVersion}`);
95
+ }
96
+ else {
97
+ messages.info(`Upgrading from v${currentVersion} to latest`);
98
+ }
99
+ // 4. Run npm i -g @shepai/cli@latest with inherited stdio
100
+ const exitCode = await runNpmInstall(spawnFn);
101
+ if (exitCode === 0) {
102
+ messages.success('Shep CLI upgraded successfully');
103
+ }
104
+ else {
105
+ messages.error('Upgrade failed');
106
+ process.exitCode = 1;
107
+ }
108
+ }
109
+ catch (error) {
110
+ const err = error instanceof Error ? error : new Error(String(error));
111
+ messages.error('Upgrade failed', err);
112
+ process.exitCode = 1;
113
+ }
114
+ });
115
+ }
@@ -15,6 +15,7 @@
15
15
  * shep agent Manage and view agent runs
16
16
  * shep feat Manage features through the SDLC lifecycle
17
17
  * shep settings Configure Shep settings
18
+ * shep upgrade Upgrade Shep CLI to the latest version
18
19
  * shep --version Display version number only
19
20
  *
20
21
  * Global Options:
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/presentation/cli/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/presentation/cli/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,kBAAkB,CAAC"}
@@ -15,6 +15,7 @@
15
15
  * shep agent Manage and view agent runs
16
16
  * shep feat Manage features through the SDLC lifecycle
17
17
  * shep settings Configure Shep settings
18
+ * shep upgrade Upgrade Shep CLI to the latest version
18
19
  * shep --version Display version number only
19
20
  *
20
21
  * Global Options:
@@ -32,12 +33,13 @@ import { createAgentCommand } from './commands/agent/index.js';
32
33
  import { createFeatCommand } from './commands/feat/index.js';
33
34
  import { createIdeOpenCommand } from './commands/ide-open.command.js';
34
35
  import { createInstallCommand } from './commands/install.command.js';
36
+ import { createUpgradeCommand } from './commands/upgrade.command.js';
35
37
  import { messages } from './ui/index.js';
36
38
  // DI container and settings
37
39
  import { initializeContainer, container } from '../../../packages/core/src/infrastructure/di/container.js';
38
40
  import { InitializeSettingsUseCase } from '../../../packages/core/src/application/use-cases/settings/initialize-settings.use-case.js';
39
- import { ListFeaturesUseCase } from '../../../packages/core/src/application/use-cases/features/list-features.use-case.js';
40
41
  import { initializeSettings } from '../../../packages/core/src/infrastructure/services/settings.service.js';
42
+ import { populateUseCasesBridge } from '../../../packages/core/src/infrastructure/di/populate-use-cases-bridge.js';
41
43
  /**
42
44
  * Bootstrap function - initializes all dependencies before CLI starts.
43
45
  * Performs async initialization (database, settings) before parsing commands.
@@ -64,13 +66,11 @@ async function bootstrap() {
64
66
  messages.error('Failed to initialize settings', err);
65
67
  throw error;
66
68
  }
67
- // Step 2b: Expose resolved use cases for the web layer via globalThis bridge
68
- // The Next.js web server runs in the same process, so globalThis is shared.
69
- // Web layer reads from this instead of importing CLI source (Turbopack incompatibility).
70
- globalThis.__shepUseCases = {
71
- listFeatures: container.resolve(ListFeaturesUseCase),
72
- agentRunRepo: container.resolve('IAgentRunRepository'),
73
- };
69
+ // Expose resolved use cases on globalThis for the web layer.
70
+ // The Next.js web UI (Turbopack) cannot import @shepai/core at runtime
71
+ // (ESM .js extensions, native C++ addons, tsyringe decorators), so the
72
+ // CLI places live instances on globalThis and the web reads them back.
73
+ populateUseCasesBridge(container);
74
74
  // Step 3: Set up Commander CLI
75
75
  const versionService = container.resolve('IVersionService');
76
76
  const { version, description } = versionService.getVersion();
@@ -90,6 +90,7 @@ async function bootstrap() {
90
90
  program.addCommand(createFeatCommand());
91
91
  program.addCommand(createIdeOpenCommand());
92
92
  program.addCommand(createInstallCommand());
93
+ program.addCommand(createUpgradeCommand());
93
94
  // Parse arguments (parseAsync needed for async command actions like init)
94
95
  await program.parseAsync();
95
96
  }
@@ -1,2 +1,4 @@
1
+ /** Force request-time rendering so the globalThis DI bridge is available. */
2
+ export declare const dynamic = "force-dynamic";
1
3
  export default function HomePage(): Promise<import("react/jsx-runtime").JSX.Element>;
2
4
  //# sourceMappingURL=page.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/app/page.tsx"],"names":[],"mappings":"AA2BA,wBAA8B,QAAQ,qDA2ErC"}
1
+ {"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/app/page.tsx"],"names":[],"mappings":"AAQA,6EAA6E;AAC7E,eAAO,MAAM,OAAO,kBAAkB,CAAC;AAqBvC,wBAA8B,QAAQ,qDAoFrC"}
@@ -1,8 +1,10 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { ControlCenter } from '../components/features/control-center/index.js';
3
- import { getFeatures } from '../lib/features.js';
3
+ import { getAgentRun, getFeatures } from '../../../../packages/core/src/infrastructure/di/use-cases-bridge.js';
4
4
  import { deriveState } from './derive-state.js';
5
5
  import { layoutWithDagre } from '../lib/layout-with-dagre.js';
6
+ /** Force request-time rendering so the globalThis DI bridge is available. */
7
+ export const dynamic = 'force-dynamic';
6
8
  /** Map domain SdlcLifecycle enum values to UI FeatureLifecyclePhase (1:1). */
7
9
  const lifecycleMap = {
8
10
  Requirements: 'requirements',
@@ -22,14 +24,23 @@ const nodeToLifecyclePhase = {
22
24
  };
23
25
  export default async function HomePage() {
24
26
  const features = await getFeatures();
27
+ const featuresWithRuns = await Promise.all(features.map(async (feature) => {
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
+ };
35
+ }));
25
36
  // Group features by repository path
26
37
  const featuresByRepo = {};
27
- features.forEach((f) => {
28
- const repoKey = f.repositoryPath;
38
+ featuresWithRuns.forEach((entry) => {
39
+ const repoKey = entry.feature.repositoryPath;
29
40
  if (!featuresByRepo[repoKey]) {
30
41
  featuresByRepo[repoKey] = [];
31
42
  }
32
- featuresByRepo[repoKey].push(f);
43
+ featuresByRepo[repoKey].push(entry);
33
44
  });
34
45
  const nodes = [];
35
46
  const edges = [];
@@ -42,11 +53,9 @@ export default async function HomePage() {
42
53
  position: { x: 0, y: 0 },
43
54
  data: { name: repoName },
44
55
  });
45
- repoFeatures.forEach((feature) => {
46
- const agentNode = feature.agentResult?.startsWith('node:')
47
- ? feature.agentResult.slice(5)
48
- : undefined;
49
- const lifecycle = feature.agentStatus === 'completed'
56
+ repoFeatures.forEach(({ feature, agentStatus, agentError, agentResult }) => {
57
+ const agentNode = agentResult?.startsWith('node:') ? agentResult.slice(5) : undefined;
58
+ const lifecycle = agentStatus === 'completed'
50
59
  ? 'maintain'
51
60
  : ((agentNode ? nodeToLifecyclePhase[agentNode] : undefined) ??
52
61
  lifecycleMap[feature.lifecycle] ??
@@ -56,8 +65,8 @@ export default async function HomePage() {
56
65
  description: feature.description ?? feature.slug,
57
66
  featureId: feature.id,
58
67
  lifecycle,
59
- ...deriveState(lifecycle, feature.agentStatus),
60
- ...(feature.agentError && { errorMessage: feature.agentError }),
68
+ ...deriveState(lifecycle, agentStatus),
69
+ ...(agentError && { errorMessage: agentError }),
61
70
  };
62
71
  const featureNodeId = `feat-${feature.id}`;
63
72
  nodes.push({
@@ -14,8 +14,8 @@ import next from 'next';
14
14
  import http from 'node:http';
15
15
  import { initializeContainer, container } from '../../../packages/core/src/infrastructure/di/container.js';
16
16
  import { InitializeSettingsUseCase } from '../../../packages/core/src/application/use-cases/settings/initialize-settings.use-case.js';
17
- import { ListFeaturesUseCase } from '../../../packages/core/src/application/use-cases/features/list-features.use-case.js';
18
17
  import { initializeSettings } from '../../../packages/core/src/infrastructure/services/settings.service.js';
18
+ import { populateUseCasesBridge } from '../../../packages/core/src/infrastructure/di/populate-use-cases-bridge.js';
19
19
  const DEFAULT_PORT = 3000;
20
20
  async function main() {
21
21
  const port = parseInt(process.env.PORT ?? '', 10) || DEFAULT_PORT;
@@ -26,11 +26,7 @@ async function main() {
26
26
  const initSettingsUseCase = container.resolve(InitializeSettingsUseCase);
27
27
  const settings = await initSettingsUseCase.execute();
28
28
  initializeSettings(settings);
29
- // Set globalThis bridge for the web layer (same as CLI bootstrap index.ts:74-76)
30
- globalThis.__shepUseCases = {
31
- listFeatures: container.resolve(ListFeaturesUseCase),
32
- agentRunRepo: container.resolve('IAgentRunRepository'),
33
- };
29
+ populateUseCasesBridge(container);
34
30
  console.log('[dev-server] DI bridge initialized');
35
31
  }
36
32
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"next.config.d.ts","sourceRoot":"","sources":["../../../../src/presentation/web/next.config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAgCvC,QAAA,MAAM,UAAU,EAAE,UAejB,CAAC;AAEF,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"next.config.d.ts","sourceRoot":"","sources":["../../../../src/presentation/web/next.config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAgCvC,QAAA,MAAM,UAAU,EAAE,UASjB,CAAC;AAEF,eAAe,UAAU,CAAC"}
@@ -28,14 +28,10 @@ function loadDevFallbacks() {
28
28
  }
29
29
  }
30
30
  const nextConfig = {
31
- // Enable Turbopack for faster development builds
32
- turbopack: {},
33
31
  // Enable typed routes (moved from experimental in Next.js 16)
34
32
  typedRoutes: true,
35
33
  // Configure the output directory
36
34
  distDir: '.next',
37
- // Native Node modules used by CLI infrastructure (server-side only)
38
- serverExternalPackages: ['better-sqlite3'],
39
35
  // Inject version info from package.json for the web UI
40
36
  env: loadDevFallbacks(),
41
37
  };