@shepai/cli 1.23.1 → 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.
- package/dist/packages/core/src/infrastructure/di/populate-use-cases-bridge.d.ts +20 -0
- package/dist/packages/core/src/infrastructure/di/populate-use-cases-bridge.d.ts.map +1 -0
- package/dist/packages/core/src/infrastructure/di/populate-use-cases-bridge.js +27 -0
- package/dist/packages/core/src/infrastructure/di/use-cases-bridge.d.ts +34 -0
- package/dist/packages/core/src/infrastructure/di/use-cases-bridge.d.ts.map +1 -0
- package/dist/packages/core/src/infrastructure/di/use-cases-bridge.js +72 -0
- package/dist/packages/core/src/infrastructure/services/git/worktree.service.d.ts +2 -0
- package/dist/packages/core/src/infrastructure/services/git/worktree.service.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/git/worktree.service.js +12 -1
- package/dist/packages/core/src/infrastructure/services/web-server.service.d.ts +1 -0
- package/dist/packages/core/src/infrastructure/services/web-server.service.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/web-server.service.js +4 -0
- package/dist/src/presentation/cli/commands/feat/show.command.d.ts.map +1 -1
- package/dist/src/presentation/cli/commands/feat/show.command.js +28 -14
- package/dist/src/presentation/cli/commands/ui.command.d.ts.map +1 -1
- package/dist/src/presentation/cli/commands/ui.command.js +3 -0
- package/dist/src/presentation/cli/commands/upgrade.command.d.ts +17 -0
- package/dist/src/presentation/cli/commands/upgrade.command.d.ts.map +1 -0
- package/dist/src/presentation/cli/commands/upgrade.command.js +115 -0
- package/dist/src/presentation/cli/index.d.ts +1 -0
- package/dist/src/presentation/cli/index.d.ts.map +1 -1
- package/dist/src/presentation/cli/index.js +9 -8
- package/dist/src/presentation/web/app/layout.d.ts +1 -0
- package/dist/src/presentation/web/app/layout.d.ts.map +1 -1
- package/dist/src/presentation/web/app/layout.js +1 -0
- package/dist/src/presentation/web/app/page.d.ts +2 -0
- package/dist/src/presentation/web/app/page.d.ts.map +1 -1
- package/dist/src/presentation/web/app/page.js +20 -11
- package/dist/src/presentation/web/dev-server.js +2 -6
- package/dist/src/presentation/web/next.config.d.ts.map +1 -1
- package/dist/src/presentation/web/next.config.js +0 -4
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/build-manifest.json +2 -2
- package/web/.next/cache/.previewinfo +1 -1
- package/web/.next/cache/.rscinfo +1 -1
- package/web/.next/cache/.tsbuildinfo +1 -1
- package/web/.next/cache/config.json +3 -3
- package/web/.next/fallback-build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -27
- package/web/.next/required-server-files.js +1 -4
- package/web/.next/required-server-files.json +1 -4
- 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_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_not-found.html +2 -2
- package/web/.next/server/app/_not-found.rsc +2 -2
- package/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/web/.next/server/app/page.js +2 -2
- 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_client-reference-manifest.js +1 -1
- package/web/.next/server/app/version.html +2 -2
- package/web/.next/server/app/version.rsc +2 -2
- package/web/.next/server/app/version.segments/_full.segment.rsc +2 -2
- package/web/.next/server/app/version.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/version.segments/_index.segment.rsc +2 -2
- package/web/.next/server/app/version.segments/_tree.segment.rsc +2 -2
- package/web/.next/server/app/version.segments/version/__PAGE__.segment.rsc +1 -1
- package/web/.next/server/app/version.segments/version.segment.rsc +1 -1
- package/web/.next/server/chunks/ssr/{[root-of-the-server]__71515c64._.js → [root-of-the-server]__80e5bed2._.js} +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__f12b60be._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_372075f9._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js +3 -0
- package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js.map +1 -0
- 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/74748b4d725c5c74.css +2 -0
- package/web/.next/trace +1 -1
- package/web/.next/trace-build +1 -1
- package/web/next.config.mjs +1 -1
- package/dist/src/presentation/web/lib/features.d.ts +0 -30
- package/dist/src/presentation/web/lib/features.d.ts.map +0 -1
- package/dist/src/presentation/web/lib/features.js +0 -50
- package/dist/src/presentation/web/lib/use-cases.d.ts +0 -29
- package/dist/src/presentation/web/lib/use-cases.d.ts.map +0 -1
- package/dist/src/presentation/web/lib/use-cases.js +0 -46
- package/web/.next/server/app/index.html +0 -11
- package/web/.next/server/app/index.meta +0 -14
- package/web/.next/server/app/index.rsc +0 -16
- package/web/.next/server/app/index.segments/__PAGE__.segment.rsc +0 -6
- package/web/.next/server/app/index.segments/_full.segment.rsc +0 -16
- package/web/.next/server/app/index.segments/_head.segment.rsc +0 -5
- package/web/.next/server/app/index.segments/_index.segment.rsc +0 -6
- package/web/.next/server/app/index.segments/_tree.segment.rsc +0 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__8507ac68._.js +0 -3
- package/web/.next/server/chunks/ssr/[root-of-the-server]__8507ac68._.js.map +0 -1
- package/web/.next/static/chunks/49488cbe99543598.css +0 -1
- /package/web/.next/server/chunks/ssr/{[root-of-the-server]__71515c64._.js.map → [root-of-the-server]__80e5bed2._.js.map} +0 -0
- /package/web/.next/static/{GicjIYHwLgPQhRVKOwkGc → LOfelu4nKYgB7PB1XlRqc}/_buildManifest.js +0 -0
- /package/web/.next/static/{GicjIYHwLgPQhRVKOwkGc → LOfelu4nKYgB7PB1XlRqc}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{GicjIYHwLgPQhRVKOwkGc → 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;
|
|
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
|
|
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(),
|
|
@@ -44,6 +44,7 @@ export declare class WebServerService implements IWebServerService {
|
|
|
44
44
|
start(port: number, dir: string, dev?: boolean): Promise<void>;
|
|
45
45
|
/**
|
|
46
46
|
* Gracefully stop the server.
|
|
47
|
+
* Destroys active connections to avoid hanging on keep-alive sockets.
|
|
47
48
|
*/
|
|
48
49
|
stop(): Promise<void>;
|
|
49
50
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web-server.service.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/src/infrastructure/services/web-server.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yEAAyE,CAAC;AAIjH,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,OAAO,IAAI,CAAC;IAC3B,gBAAgB,EAAE,OAAO,IAAI,CAAC,YAAY,CAAC;CAC5C;AAOD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,IAAI;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,OAAO,CAAA;CAAE,CAsB7D;AAED,qBACa,gBAAiB,YAAW,iBAAiB;IACxD,OAAO,CAAC,GAAG,CAAwB;IACnC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgB;gBAEzB,IAAI,GAAE,OAAO,CAAC,aAAa,CAAM;IAI7C;;;;;OAKG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,UAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BjE
|
|
1
|
+
{"version":3,"file":"web-server.service.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/src/infrastructure/services/web-server.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yEAAyE,CAAC;AAIjH,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,OAAO,IAAI,CAAC;IAC3B,gBAAgB,EAAE,OAAO,IAAI,CAAC,YAAY,CAAC;CAC5C;AAOD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,IAAI;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,OAAO,CAAA;CAAE,CAsB7D;AAED,qBACa,gBAAiB,YAAW,iBAAiB;IACxD,OAAO,CAAC,GAAG,CAAwB;IACnC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgB;gBAEzB,IAAI,GAAE,OAAO,CAAC,aAAa,CAAM;IAI7C;;;;;OAKG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,UAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BjE;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAuB5B"}
|
|
@@ -92,6 +92,7 @@ let WebServerService = class WebServerService {
|
|
|
92
92
|
}
|
|
93
93
|
/**
|
|
94
94
|
* Gracefully stop the server.
|
|
95
|
+
* Destroys active connections to avoid hanging on keep-alive sockets.
|
|
95
96
|
*/
|
|
96
97
|
async stop() {
|
|
97
98
|
if (this.isShuttingDown)
|
|
@@ -99,6 +100,9 @@ let WebServerService = class WebServerService {
|
|
|
99
100
|
this.isShuttingDown = true;
|
|
100
101
|
try {
|
|
101
102
|
if (this.server) {
|
|
103
|
+
// Destroy all active connections so server.close() resolves immediately
|
|
104
|
+
// Without this, HTTP keep-alive connections keep the server hanging
|
|
105
|
+
this.server.closeAllConnections();
|
|
102
106
|
await new Promise((resolve) => {
|
|
103
107
|
this.server.close(() => resolve());
|
|
104
108
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"show.command.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/feat/show.command.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuEpC,wBAAgB,iBAAiB,IAAI,OAAO,
|
|
1
|
+
{"version":3,"file":"show.command.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/feat/show.command.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuEpC,wBAAgB,iBAAiB,IAAI,OAAO,CA4I3C"}
|
|
@@ -103,24 +103,38 @@ export function createShowCommand() {
|
|
|
103
103
|
content: last5,
|
|
104
104
|
});
|
|
105
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Helper function to render a single phase timing bar.
|
|
108
|
+
* Handles three states: completed (green bar), waiting (yellow text), running (blue bar).
|
|
109
|
+
*/
|
|
110
|
+
const renderPhaseTimingBar = (timing, maxDurationMs, isWaiting, MAX_BAR) => {
|
|
111
|
+
const label = (NODE_TO_PHASE[timing.phase] ?? timing.phase).padEnd(16);
|
|
112
|
+
// Completed phase - green bar with duration
|
|
113
|
+
if (timing.completedAt && timing.durationMs != null) {
|
|
114
|
+
const ms = Number(timing.durationMs);
|
|
115
|
+
const secs = (ms / 1000).toFixed(1);
|
|
116
|
+
const barLen = maxDurationMs > 0 ? Math.max(1, Math.round((ms / maxDurationMs) * MAX_BAR)) : 1;
|
|
117
|
+
const bar = `${colors.success('█'.repeat(barLen))}${colors.muted('░'.repeat(MAX_BAR - barLen))}`;
|
|
118
|
+
return `${label} ${bar} ${secs}s`;
|
|
119
|
+
}
|
|
120
|
+
// Waiting for approval - yellow warning text
|
|
121
|
+
if (isWaiting) {
|
|
122
|
+
return `${label} ${colors.warning('awaiting review')}`;
|
|
123
|
+
}
|
|
124
|
+
// Running phase - blue bar with elapsed time
|
|
125
|
+
const elapsedMs = Math.max(0, Date.now() - timing.startedAt.getTime());
|
|
126
|
+
const secs = (elapsedMs / 1000).toFixed(1);
|
|
127
|
+
const barLen = maxDurationMs > 0
|
|
128
|
+
? Math.min(MAX_BAR, Math.max(1, Math.round((elapsedMs / maxDurationMs) * MAX_BAR)))
|
|
129
|
+
: 1;
|
|
130
|
+
const bar = `${colors.info('█'.repeat(barLen))}${colors.muted('░'.repeat(MAX_BAR - barLen))}`;
|
|
131
|
+
return `${label} ${bar} ${secs}s (running)`;
|
|
132
|
+
};
|
|
106
133
|
if (timings.length > 0) {
|
|
107
134
|
const isWaiting = run?.status === 'waiting_approval';
|
|
108
135
|
const maxDurationMs = Math.max(...timings.map((t) => (t.durationMs != null ? Number(t.durationMs) : 0)));
|
|
109
136
|
const MAX_BAR = 20;
|
|
110
|
-
const lines = timings.map((t) =>
|
|
111
|
-
const label = (NODE_TO_PHASE[t.phase] ?? t.phase).padEnd(16);
|
|
112
|
-
if (t.completedAt && t.durationMs != null) {
|
|
113
|
-
const ms = Number(t.durationMs);
|
|
114
|
-
const secs = (ms / 1000).toFixed(1);
|
|
115
|
-
const barLen = maxDurationMs > 0 ? Math.max(1, Math.round((ms / maxDurationMs) * MAX_BAR)) : 1;
|
|
116
|
-
const bar = `${colors.success('█'.repeat(barLen))}${colors.muted('░'.repeat(MAX_BAR - barLen))}`;
|
|
117
|
-
return `${label} ${bar} ${secs}s`;
|
|
118
|
-
}
|
|
119
|
-
if (isWaiting) {
|
|
120
|
-
return `${label} ${colors.warning('awaiting review')}`;
|
|
121
|
-
}
|
|
122
|
-
return `${label} ${colors.info('running...')}`;
|
|
123
|
-
});
|
|
137
|
+
const lines = timings.map((t) => renderPhaseTimingBar(t, maxDurationMs, isWaiting, MAX_BAR));
|
|
124
138
|
textBlocks.push({ title: 'Phase Timing', content: lines.join('\n') });
|
|
125
139
|
}
|
|
126
140
|
if (run?.status === 'waiting_approval') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.command.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/cli/commands/ui.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,OAAO,EAAwB,MAAM,WAAW,CAAC;AAiB1D;;GAEG;AACH,wBAAgB,eAAe,IAAI,OAAO,
|
|
1
|
+
{"version":3,"file":"ui.command.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/cli/commands/ui.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,OAAO,EAAwB,MAAM,WAAW,CAAC;AAiB1D;;GAEG;AACH,wBAAgB,eAAe,IAAI,OAAO,CA0DzC"}
|
|
@@ -64,6 +64,9 @@ Examples:
|
|
|
64
64
|
isShuttingDown = true;
|
|
65
65
|
messages.newline();
|
|
66
66
|
messages.info('Shutting down...');
|
|
67
|
+
// Force exit after 5s if graceful shutdown stalls
|
|
68
|
+
const forceExit = setTimeout(() => process.exit(0), 5000);
|
|
69
|
+
forceExit.unref();
|
|
67
70
|
await service.stop();
|
|
68
71
|
process.exit(0);
|
|
69
72
|
};
|
|
@@ -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
|
|
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
|
-
//
|
|
68
|
-
// The Next.js web
|
|
69
|
-
//
|
|
70
|
-
globalThis
|
|
71
|
-
|
|
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 +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,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;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 +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":"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 '
|
|
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
|
-
|
|
28
|
-
const repoKey =
|
|
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(
|
|
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 =
|
|
47
|
-
|
|
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,
|
|
60
|
-
...(
|
|
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
|
-
|
|
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) {
|