@shepai/cli 1.134.1 → 1.135.0-pr430.5b0b699
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/apis/json-schema/FeatureFlags.yaml +1 -1
- package/dist/packages/core/src/application/use-cases/features/create/create-feature-from-remote.use-case.d.ts +82 -0
- package/dist/packages/core/src/application/use-cases/features/create/create-feature-from-remote.use-case.d.ts.map +1 -0
- package/dist/packages/core/src/application/use-cases/features/create/create-feature-from-remote.use-case.js +96 -0
- package/dist/packages/core/src/domain/factories/settings-defaults.factory.js +1 -1
- package/dist/packages/core/src/infrastructure/di/container.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/di/container.js +5 -0
- package/dist/packages/core/src/infrastructure/services/git/git-pr.service.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/git/git-pr.service.js +9 -1
- package/dist/src/presentation/cli/commands/feat/new.command.d.ts +1 -0
- package/dist/src/presentation/cli/commands/feat/new.command.d.ts.map +1 -1
- package/dist/src/presentation/cli/commands/feat/new.command.js +88 -7
- package/dist/src/presentation/web/app/actions/create-feature-from-remote.d.ts +29 -0
- package/dist/src/presentation/web/app/actions/create-feature-from-remote.d.ts.map +1 -0
- package/dist/src/presentation/web/app/actions/create-feature-from-remote.js +72 -0
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.js +18 -4
- package/dist/src/presentation/web/components/common/feature-create-drawer/repository-combobox.stories.d.ts +6 -0
- package/dist/src/presentation/web/components/common/feature-create-drawer/repository-combobox.stories.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-create-drawer/repository-combobox.stories.js +35 -2
- package/dist/src/presentation/web/lib/feature-flags.js +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/build-manifest.json +2 -2
- package/web/.next/fallback-build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -3
- package/web/.next/required-server-files.js +3 -3
- package/web/.next/required-server-files.json +3 -3
- package/web/.next/server/app/(dashboard)/@drawer/adopt/page/server-reference-manifest.json +24 -24
- package/web/.next/server/app/(dashboard)/@drawer/adopt/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/adopt/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +24 -24
- package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page/server-reference-manifest.json +30 -30
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +30 -30
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +22 -22
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +24 -24
- package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page/server-reference-manifest.json +30 -30
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +30 -30
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +22 -22
- package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +22 -22
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
- 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/server-reference-manifest.json +3 -3
- package/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/api/attachments/preview/route.js.nft.json +1 -1
- package/web/.next/server/app/api/evidence/route.js.nft.json +1 -1
- package/web/.next/server/app/api/graph-data/route.js.nft.json +1 -1
- package/web/.next/server/app/settings/page/server-reference-manifest.json +8 -8
- package/web/.next/server/app/settings/page.js.nft.json +1 -1
- package/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/skills/page/server-reference-manifest.json +8 -8
- package/web/.next/server/app/skills/page.js.nft.json +1 -1
- package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/tools/page/server-reference-manifest.json +8 -8
- package/web/.next/server/app/tools/page.js.nft.json +1 -1
- package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/version/page/server-reference-manifest.json +3 -3
- package/web/.next/server/app/version/page.js.nft.json +1 -1
- package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
- package/web/.next/server/chunks/[root-of-the-server]__a402b567._.js +1 -1
- package/web/.next/server/chunks/ssr/744ca_web__next-internal_server_app_(dashboard)_@drawer_adopt_page_actions_4e741df2.js +1 -1
- package/web/.next/server/chunks/ssr/744ca_web__next-internal_server_app_(dashboard)_@drawer_adopt_page_actions_4e741df2.js.map +1 -1
- package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
- package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_026fdffa.js +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_026fdffa.js.map +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_8b91e197.js +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_8b91e197.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__2c791c42._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__357d99f9._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__3ef34e4c._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__43f51aa6._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__43f51aa6._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__755cfe02._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__755cfe02._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__815546bd._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__815546bd._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__8fae3a20._.js +3 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__8fae3a20._.js.map +1 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__96670392._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__a628fb91._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__a628fb91._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__bfba58e6._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__bfba58e6._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__c3ab8422._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__c3ab8422._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_02ec1aea._.js +1 -1
- package/web/.next/server/chunks/ssr/_02ec1aea._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_2e03c237._.js +3 -0
- package/web/.next/server/chunks/ssr/_2e03c237._.js.map +1 -0
- package/web/.next/server/chunks/ssr/_7acd7690._.js +9 -0
- package/web/.next/server/chunks/ssr/_7acd7690._.js.map +1 -0
- package/web/.next/server/chunks/ssr/_7dca1882._.js +1 -1
- package/web/.next/server/chunks/ssr/_7dca1882._.js.map +1 -1
- package/web/.next/server/chunks/ssr/{_a72b7c58._.js → _93a801f1._.js} +2 -2
- package/web/.next/server/chunks/ssr/{_a72b7c58._.js.map → _93a801f1._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/_a37f5f18._.js +1 -1
- package/web/.next/server/chunks/ssr/_a37f5f18._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_a9f57758._.js +1 -1
- package/web/.next/server/chunks/ssr/_a9f57758._.js.map +1 -1
- package/web/.next/server/chunks/ssr/{_2245e9a2._.js → _d1c40d87._.js} +2 -2
- package/web/.next/server/chunks/ssr/{_2245e9a2._.js.map → _d1c40d87._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/_d8575088._.js +1 -1
- package/web/.next/server/chunks/ssr/_d8575088._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_f39a1adb._.js +1 -1
- package/web/.next/server/chunks/ssr/_f39a1adb._.js.map +1 -1
- package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
- package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_7daab21a._.js +3 -0
- package/web/.next/server/chunks/ssr/{src_presentation_web_2deef0bb._.js.map → src_presentation_web_7daab21a._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_8e7fe0d1._.js +2 -2
- package/web/.next/server/chunks/ssr/src_presentation_web_8e7fe0d1._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_ca051134.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_ca051134.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1b176e3c.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1b176e3c.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_bd9f0dda.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_bd9f0dda.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_b9023017._.js +2 -2
- package/web/.next/server/chunks/ssr/src_presentation_web_b9023017._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
- 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 +38 -38
- package/web/.next/static/chunks/{0d402c720e467362.js → 49f9ce1e2953f293.js} +1 -1
- package/web/.next/static/chunks/{7bf6864106b4a492.js → 6c628cd7f8e54b37.js} +1 -1
- package/web/.next/static/chunks/9d55dd03daac8bb2.js +3 -0
- package/web/.next/static/chunks/9e35dfeb65fbb0ff.js +2 -0
- package/web/.next/static/chunks/a20470eee309dc12.js +1 -0
- package/web/.next/static/chunks/abbc9bc0ecff7cac.js +1 -0
- package/web/.next/static/chunks/{3a7297918dbc91a3.js → c0bc54eda21bbcf1.js} +1 -1
- package/web/.next/static/chunks/{f6a3616c124c7477.js → c99297cbb9c20255.js} +2 -2
- package/web/.next/static/chunks/cc54a5316b728fd9.js +1 -0
- package/web/.next/static/chunks/e95018ef71b4083d.js +11 -0
- package/web/.next/static/chunks/{57b2b14dd532178a.js → eb8bb6289b905648.js} +1 -1
- package/web/.next/static/chunks/ebe7f939b642b667.js +1 -0
- package/web/.next/static/chunks/{6873d45035d5a770.js → ecbbae4d5e4cfab2.js} +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__7894f27f._.js +0 -3
- package/web/.next/server/chunks/ssr/[root-of-the-server]__7894f27f._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_01046927._.js +0 -9
- package/web/.next/server/chunks/ssr/_01046927._.js.map +0 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_2deef0bb._.js +0 -3
- package/web/.next/static/chunks/0881a378255faa56.js +0 -1
- package/web/.next/static/chunks/0b7fe2ac4f078f54.js +0 -13
- package/web/.next/static/chunks/1198e6430b3d8d60.js +0 -1
- package/web/.next/static/chunks/295eda9920677a58.js +0 -2
- package/web/.next/static/chunks/3ca48946f52422b7.js +0 -1
- package/web/.next/static/chunks/78fcc26559384102.js +0 -1
- package/web/.next/static/chunks/b5a6eb27a1639a39.js +0 -1
- /package/web/.next/static/{OjdBKBsY7_vAITPyXhtKr → 5_A91ycYn-g6phGM-k_v-}/_buildManifest.js +0 -0
- /package/web/.next/static/{OjdBKBsY7_vAITPyXhtKr → 5_A91ycYn-g6phGM-k_v-}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{OjdBKBsY7_vAITPyXhtKr → 5_A91ycYn-g6phGM-k_v-}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create Feature From Remote Use Case
|
|
3
|
+
*
|
|
4
|
+
* Composite use case that orchestrates importing a GitHub repository via
|
|
5
|
+
* ImportGitHubRepositoryUseCase and then creating a feature via
|
|
6
|
+
* CreateFeatureUseCase. Provides both a single execute() method (for CLI)
|
|
7
|
+
* and two-phase createRecord() + initializeAndSpawn() (for Web UI).
|
|
8
|
+
*/
|
|
9
|
+
import type { Feature } from '../../../../domain/generated/output.js';
|
|
10
|
+
import type { CloneOptions } from '../../../ports/output/services/github-repository-service.interface.js';
|
|
11
|
+
import type { ApprovalGates, Attachment } from '../../../../domain/generated/output.js';
|
|
12
|
+
import { ImportGitHubRepositoryUseCase } from '../../repositories/import-github-repository.use-case.js';
|
|
13
|
+
import { CreateFeatureUseCase } from './create-feature.use-case.js';
|
|
14
|
+
import type { CreateFeatureResult, CreateRecordResult } from './types.js';
|
|
15
|
+
export interface CreateFeatureFromRemoteInput {
|
|
16
|
+
/** GitHub URL or shorthand (e.g. "owner/repo", HTTPS, SSH) */
|
|
17
|
+
remoteUrl: string;
|
|
18
|
+
/** Override clone destination directory */
|
|
19
|
+
cloneDest?: string;
|
|
20
|
+
/** Default base directory for clones (from settings) */
|
|
21
|
+
defaultCloneDir?: string;
|
|
22
|
+
/** Options for the clone subprocess (e.g. progress callback) */
|
|
23
|
+
cloneOptions?: CloneOptions;
|
|
24
|
+
/** Description of the feature to create */
|
|
25
|
+
userInput: string;
|
|
26
|
+
/** Approval gate settings */
|
|
27
|
+
approvalGates?: ApprovalGates;
|
|
28
|
+
/** Push branch to remote after creation */
|
|
29
|
+
push?: boolean;
|
|
30
|
+
/** Open a PR after creation */
|
|
31
|
+
openPr?: boolean;
|
|
32
|
+
/** Optional ID of the parent feature */
|
|
33
|
+
parentId?: string;
|
|
34
|
+
/** Pre-supplied name (skips AI metadata extraction for name) */
|
|
35
|
+
name?: string;
|
|
36
|
+
/** Pre-supplied description (skips AI metadata extraction for description) */
|
|
37
|
+
description?: string;
|
|
38
|
+
/** When true, skip SDLC phases and implement directly */
|
|
39
|
+
fast?: boolean;
|
|
40
|
+
/** When true, create feature in Pending state */
|
|
41
|
+
pending?: boolean;
|
|
42
|
+
/** Optional agent type override */
|
|
43
|
+
agentType?: string;
|
|
44
|
+
/** Optional model identifier forwarded to the agent executor */
|
|
45
|
+
model?: string;
|
|
46
|
+
/** Attachment records to persist with the feature */
|
|
47
|
+
attachments?: Attachment[];
|
|
48
|
+
/** Session ID for committing pending uploads (web UI flow) */
|
|
49
|
+
sessionId?: string;
|
|
50
|
+
/** Absolute file paths to attach (CLI --attach flow) */
|
|
51
|
+
attachmentPaths?: string[];
|
|
52
|
+
}
|
|
53
|
+
export declare class CreateFeatureFromRemoteUseCase {
|
|
54
|
+
private readonly importGitHubRepo;
|
|
55
|
+
private readonly createFeature;
|
|
56
|
+
constructor(importGitHubRepo: ImportGitHubRepositoryUseCase, createFeature: CreateFeatureUseCase);
|
|
57
|
+
/**
|
|
58
|
+
* Full execution: imports the repository (or finds existing), then creates
|
|
59
|
+
* the feature with all SDLC initialization. Used by the CLI.
|
|
60
|
+
*/
|
|
61
|
+
execute(input: CreateFeatureFromRemoteInput): Promise<CreateFeatureResult>;
|
|
62
|
+
/**
|
|
63
|
+
* Phase 1 (fast): Imports the repository, then creates the feature + agent
|
|
64
|
+
* run records in DB. Returns immediately with a real feature ID.
|
|
65
|
+
* Used by the Web UI for optimistic rendering.
|
|
66
|
+
*/
|
|
67
|
+
createRecord(input: CreateFeatureFromRemoteInput): Promise<CreateRecordResult>;
|
|
68
|
+
/**
|
|
69
|
+
* Phase 2 (slow): AI metadata generation, git worktree, spec init, agent spawn.
|
|
70
|
+
* Delegates directly to CreateFeatureUseCase since import is already complete.
|
|
71
|
+
*/
|
|
72
|
+
initializeAndSpawn(feature: Feature, input: CreateFeatureFromRemoteInput, shouldSpawn: boolean): Promise<{
|
|
73
|
+
warning?: string;
|
|
74
|
+
updatedFeature: Feature;
|
|
75
|
+
}>;
|
|
76
|
+
/**
|
|
77
|
+
* Maps CreateFeatureFromRemoteInput to CreateFeatureInput by injecting
|
|
78
|
+
* the repositoryPath derived from the import result.
|
|
79
|
+
*/
|
|
80
|
+
private toCreateFeatureInput;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=create-feature-from-remote.use-case.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-feature-from-remote.use-case.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/src/application/use-cases/features/create/create-feature-from-remote.use-case.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wCAAwC,CAAC;AACtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uEAAuE,CAAC;AAC1G,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AACxF,OAAO,EAAE,6BAA6B,EAAE,MAAM,yDAAyD,CAAC;AACxG,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,KAAK,EAAsB,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAE9F,MAAM,WAAW,4BAA4B;IAC3C,8DAA8D;IAC9D,SAAS,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wDAAwD;IACxD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gEAAgE;IAChE,YAAY,CAAC,EAAE,YAAY,CAAC;IAE5B,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,6BAA6B;IAC7B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,2CAA2C;IAC3C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,+BAA+B;IAC/B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,iDAAiD;IACjD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mCAAmC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qDAAqD;IACrD,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,8DAA8D;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wDAAwD;IACxD,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,qBACa,8BAA8B;IAGvC,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IAEjC,OAAO,CAAC,QAAQ,CAAC,aAAa;gBAFb,gBAAgB,EAAE,6BAA6B,EAE/C,aAAa,EAAE,oBAAoB;IAGtD;;;OAGG;IACG,OAAO,CAAC,KAAK,EAAE,4BAA4B,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAWhF;;;;OAIG;IACG,YAAY,CAAC,KAAK,EAAE,4BAA4B,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAWpF;;;OAGG;IACG,kBAAkB,CACtB,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,4BAA4B,EACnC,WAAW,EAAE,OAAO,GACnB,OAAO,CAAC;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,CAAC;IAQzD;;;OAGG;IACH,OAAO,CAAC,oBAAoB;CAsB7B"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create Feature From Remote Use Case
|
|
3
|
+
*
|
|
4
|
+
* Composite use case that orchestrates importing a GitHub repository via
|
|
5
|
+
* ImportGitHubRepositoryUseCase and then creating a feature via
|
|
6
|
+
* CreateFeatureUseCase. Provides both a single execute() method (for CLI)
|
|
7
|
+
* and two-phase createRecord() + initializeAndSpawn() (for Web UI).
|
|
8
|
+
*/
|
|
9
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
10
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
11
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
12
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
13
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
14
|
+
};
|
|
15
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
16
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
17
|
+
};
|
|
18
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
19
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
20
|
+
};
|
|
21
|
+
import { injectable, inject } from 'tsyringe';
|
|
22
|
+
import { ImportGitHubRepositoryUseCase } from '../../repositories/import-github-repository.use-case.js';
|
|
23
|
+
import { CreateFeatureUseCase } from './create-feature.use-case.js';
|
|
24
|
+
let CreateFeatureFromRemoteUseCase = class CreateFeatureFromRemoteUseCase {
|
|
25
|
+
importGitHubRepo;
|
|
26
|
+
createFeature;
|
|
27
|
+
constructor(importGitHubRepo, createFeature) {
|
|
28
|
+
this.importGitHubRepo = importGitHubRepo;
|
|
29
|
+
this.createFeature = createFeature;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Full execution: imports the repository (or finds existing), then creates
|
|
33
|
+
* the feature with all SDLC initialization. Used by the CLI.
|
|
34
|
+
*/
|
|
35
|
+
async execute(input) {
|
|
36
|
+
const repository = await this.importGitHubRepo.execute({
|
|
37
|
+
url: input.remoteUrl,
|
|
38
|
+
dest: input.cloneDest,
|
|
39
|
+
defaultCloneDir: input.defaultCloneDir,
|
|
40
|
+
cloneOptions: input.cloneOptions,
|
|
41
|
+
});
|
|
42
|
+
return this.createFeature.execute(this.toCreateFeatureInput(input, repository.path));
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Phase 1 (fast): Imports the repository, then creates the feature + agent
|
|
46
|
+
* run records in DB. Returns immediately with a real feature ID.
|
|
47
|
+
* Used by the Web UI for optimistic rendering.
|
|
48
|
+
*/
|
|
49
|
+
async createRecord(input) {
|
|
50
|
+
const repository = await this.importGitHubRepo.execute({
|
|
51
|
+
url: input.remoteUrl,
|
|
52
|
+
dest: input.cloneDest,
|
|
53
|
+
defaultCloneDir: input.defaultCloneDir,
|
|
54
|
+
cloneOptions: input.cloneOptions,
|
|
55
|
+
});
|
|
56
|
+
return this.createFeature.createRecord(this.toCreateFeatureInput(input, repository.path));
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Phase 2 (slow): AI metadata generation, git worktree, spec init, agent spawn.
|
|
60
|
+
* Delegates directly to CreateFeatureUseCase since import is already complete.
|
|
61
|
+
*/
|
|
62
|
+
async initializeAndSpawn(feature, input, shouldSpawn) {
|
|
63
|
+
return this.createFeature.initializeAndSpawn(feature, this.toCreateFeatureInput(input, feature.repositoryPath), shouldSpawn);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Maps CreateFeatureFromRemoteInput to CreateFeatureInput by injecting
|
|
67
|
+
* the repositoryPath derived from the import result.
|
|
68
|
+
*/
|
|
69
|
+
toCreateFeatureInput(input, repositoryPath) {
|
|
70
|
+
return {
|
|
71
|
+
userInput: input.userInput,
|
|
72
|
+
repositoryPath,
|
|
73
|
+
approvalGates: input.approvalGates,
|
|
74
|
+
push: input.push,
|
|
75
|
+
openPr: input.openPr,
|
|
76
|
+
parentId: input.parentId,
|
|
77
|
+
name: input.name,
|
|
78
|
+
description: input.description,
|
|
79
|
+
fast: input.fast,
|
|
80
|
+
pending: input.pending,
|
|
81
|
+
agentType: input.agentType,
|
|
82
|
+
model: input.model,
|
|
83
|
+
attachments: input.attachments,
|
|
84
|
+
sessionId: input.sessionId,
|
|
85
|
+
attachmentPaths: input.attachmentPaths,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
CreateFeatureFromRemoteUseCase = __decorate([
|
|
90
|
+
injectable(),
|
|
91
|
+
__param(0, inject(ImportGitHubRepositoryUseCase)),
|
|
92
|
+
__param(1, inject(CreateFeatureUseCase)),
|
|
93
|
+
__metadata("design:paramtypes", [ImportGitHubRepositoryUseCase,
|
|
94
|
+
CreateFeatureUseCase])
|
|
95
|
+
], CreateFeatureFromRemoteUseCase);
|
|
96
|
+
export { CreateFeatureFromRemoteUseCase };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/src/infrastructure/di/container.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/src/infrastructure/di/container.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AA0HrC;;;;;;GAMG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,SAAS,CAAC,CAuVrE;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAED;;;GAGG;AACH,OAAO,EAAE,SAAS,EAAE,CAAC"}
|
|
@@ -59,6 +59,7 @@ import { ApproveAgentRunUseCase } from '../../application/use-cases/agents/appro
|
|
|
59
59
|
import { RejectAgentRunUseCase } from '../../application/use-cases/agents/reject-agent-run.use-case.js';
|
|
60
60
|
import { ReviewFeatureUseCase } from '../../application/use-cases/agents/review-feature.use-case.js';
|
|
61
61
|
import { CreateFeatureUseCase } from '../../application/use-cases/features/create/create-feature.use-case.js';
|
|
62
|
+
import { CreateFeatureFromRemoteUseCase } from '../../application/use-cases/features/create/create-feature-from-remote.use-case.js';
|
|
62
63
|
import { MetadataGenerator } from '../../application/use-cases/features/create/metadata-generator.js';
|
|
63
64
|
import { SlugResolver } from '../../application/use-cases/features/create/slug-resolver.js';
|
|
64
65
|
import { ListFeaturesUseCase } from '../../application/use-cases/features/list-features.use-case.js';
|
|
@@ -280,6 +281,7 @@ export async function initializeContainer() {
|
|
|
280
281
|
container.registerSingleton(MetadataGenerator);
|
|
281
282
|
container.registerSingleton(SlugResolver);
|
|
282
283
|
container.registerSingleton(CreateFeatureUseCase);
|
|
284
|
+
container.registerSingleton(CreateFeatureFromRemoteUseCase);
|
|
283
285
|
container.registerSingleton(ListFeaturesUseCase);
|
|
284
286
|
container.registerSingleton(ShowFeatureUseCase);
|
|
285
287
|
container.registerSingleton(DeleteFeatureUseCase);
|
|
@@ -323,6 +325,9 @@ export async function initializeContainer() {
|
|
|
323
325
|
container.register('CreateFeatureUseCase', {
|
|
324
326
|
useFactory: (c) => c.resolve(CreateFeatureUseCase),
|
|
325
327
|
});
|
|
328
|
+
container.register('CreateFeatureFromRemoteUseCase', {
|
|
329
|
+
useFactory: (c) => c.resolve(CreateFeatureFromRemoteUseCase),
|
|
330
|
+
});
|
|
326
331
|
container.register('ListFeaturesUseCase', {
|
|
327
332
|
useFactory: (c) => c.resolve(ListFeaturesUseCase),
|
|
328
333
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-pr.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/git/git-pr.service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wEAAwE,CAAC;AAC5G,OAAO,KAAK,EACV,cAAc,EAGd,WAAW,EACX,QAAQ,EACR,aAAa,EACb,cAAc,EACd,YAAY,EACb,MAAM,wEAAwE,CAAC;AAUhF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,qBACa,YAAa,YAAW,aAAa;IACZ,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,YAAY;IAErE,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKxC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAgBjD,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAgF9C,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKnD,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKpD,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAWxD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAYvE,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAuClE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAE,aAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;YA6BjF,eAAe;IASvB,gBAAgB,CACpB,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,EACrB,SAAS,UAAQ,GAChB,OAAO,CAAC,IAAI,CAAC;IA4EV,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUnF,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAsBjE,OAAO,CACX,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"git-pr.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/git/git-pr.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -322,7 +322,15 @@ let GitPrService = class GitPrService {
|
|
|
322
322
|
const runId = String(runs[0].databaseId);
|
|
323
323
|
runUrl = runs[0].url;
|
|
324
324
|
const interval = intervalSeconds ?? 30;
|
|
325
|
-
const args = [
|
|
325
|
+
const args = [
|
|
326
|
+
'run',
|
|
327
|
+
'watch',
|
|
328
|
+
runId,
|
|
329
|
+
'--exit-status',
|
|
330
|
+
'--compact',
|
|
331
|
+
'--interval',
|
|
332
|
+
String(interval),
|
|
333
|
+
];
|
|
326
334
|
const { stdout } = await this.execFile('gh', args, {
|
|
327
335
|
cwd,
|
|
328
336
|
...(timeoutMs ? { timeout: timeoutMs } : {}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"new.command.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/feat/new.command.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"new.command.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/feat/new.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAoE5C;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAiP1C"}
|
|
@@ -8,15 +8,18 @@
|
|
|
8
8
|
* @example
|
|
9
9
|
* $ shep feat new "Add user authentication"
|
|
10
10
|
* $ shep feat new "Add login page" --repo /path/to/project
|
|
11
|
+
* $ shep feat new "Add dark mode" --remote owner/repo
|
|
11
12
|
*/
|
|
12
|
-
import { Command } from 'commander';
|
|
13
|
+
import { Command, Option } from 'commander';
|
|
13
14
|
import { createHash } from 'node:crypto';
|
|
14
15
|
import { existsSync } from 'node:fs';
|
|
15
16
|
import { join, resolve } from 'node:path';
|
|
16
17
|
import { container } from '../../../../../packages/core/src/infrastructure/di/container.js';
|
|
17
18
|
import { CreateFeatureUseCase } from '../../../../../packages/core/src/application/use-cases/features/create/create-feature.use-case.js';
|
|
19
|
+
import { CreateFeatureFromRemoteUseCase } from '../../../../../packages/core/src/application/use-cases/features/create/create-feature-from-remote.use-case.js';
|
|
18
20
|
import { SdlcLifecycle } from '../../../../../packages/core/src/domain/generated/output.js';
|
|
19
|
-
import {
|
|
21
|
+
import { GitHubAuthError, GitHubCloneError, GitHubUrlParseError, } from '../../../../../packages/core/src/application/ports/output/services/github-repository-service.interface.js';
|
|
22
|
+
import { colors, messages, symbols, spinner } from '../../ui/index.js';
|
|
20
23
|
import { getShepHomeDir } from '../../../../../packages/core/src/infrastructure/services/filesystem/shep-directory.service.js';
|
|
21
24
|
import { getSettings, hasSettings } from '../../../../../packages/core/src/infrastructure/services/settings.service.js';
|
|
22
25
|
import { CheckOnboardingStatusUseCase } from '../../../../../packages/core/src/application/use-cases/settings/check-onboarding-status.use-case.js';
|
|
@@ -47,6 +50,7 @@ export function createNewCommand() {
|
|
|
47
50
|
.description('Create a new feature')
|
|
48
51
|
.argument('<description>', 'Feature description')
|
|
49
52
|
.option('-r, --repo <path>', 'Repository path (defaults to current directory)')
|
|
53
|
+
.addOption(new Option('--remote <url>', 'GitHub URL or owner/repo shorthand to clone and create feature on').conflicts('repo'))
|
|
50
54
|
.option('--push', 'Push branch to remote after implementation')
|
|
51
55
|
.option('--pr', 'Open PR on implementation complete (implies --push)')
|
|
52
56
|
.option('--no-pr', 'Do not open PR on implementation complete')
|
|
@@ -68,8 +72,6 @@ export function createNewCommand() {
|
|
|
68
72
|
await onboardingWizard();
|
|
69
73
|
}
|
|
70
74
|
}
|
|
71
|
-
const useCase = container.resolve(CreateFeatureUseCase);
|
|
72
|
-
const repoPath = options.repo ?? process.cwd();
|
|
73
75
|
// Resolve openPr from CLI flags or settings defaults
|
|
74
76
|
const defaults = getWorkflowDefaults();
|
|
75
77
|
const openPr = options.pr ?? defaults.openPr;
|
|
@@ -107,9 +109,9 @@ export function createNewCommand() {
|
|
|
107
109
|
attachmentPaths.push(resolved);
|
|
108
110
|
}
|
|
109
111
|
}
|
|
110
|
-
|
|
112
|
+
// Common input fields shared between local and remote paths
|
|
113
|
+
const commonInput = {
|
|
111
114
|
userInput: description,
|
|
112
|
-
repositoryPath: repoPath,
|
|
113
115
|
approvalGates,
|
|
114
116
|
push,
|
|
115
117
|
openPr,
|
|
@@ -118,8 +120,70 @@ export function createNewCommand() {
|
|
|
118
120
|
...(options.fast && { fast: true }),
|
|
119
121
|
...(options.model !== undefined && { model: options.model }),
|
|
120
122
|
...(attachmentPaths.length > 0 && { attachmentPaths }),
|
|
121
|
-
}
|
|
123
|
+
};
|
|
124
|
+
let result;
|
|
125
|
+
if (options.remote) {
|
|
126
|
+
// Remote flow: import GitHub repo then create feature
|
|
127
|
+
const remoteUseCase = container.resolve(CreateFeatureFromRemoteUseCase);
|
|
128
|
+
// Read defaultCloneDir from settings
|
|
129
|
+
const settings = getSettings();
|
|
130
|
+
const defaultCloneDir = settings.environment?.defaultCloneDirectory;
|
|
131
|
+
// Dynamic spinner with clone progress updates
|
|
132
|
+
const frames = symbols.spinner;
|
|
133
|
+
let frameIdx = 0;
|
|
134
|
+
let spinnerLabel = 'Cloning repository';
|
|
135
|
+
let maxLabelLen = spinnerLabel.length;
|
|
136
|
+
const writeSpinner = () => {
|
|
137
|
+
const frame = frames[frameIdx % frames.length];
|
|
138
|
+
process.stderr.write(`\r${colors.muted(`${frame} ${spinnerLabel}...`)}`);
|
|
139
|
+
frameIdx++;
|
|
140
|
+
};
|
|
141
|
+
writeSpinner();
|
|
142
|
+
const spinnerInterval = setInterval(writeSpinner, 80);
|
|
143
|
+
const clearSpinner = () => {
|
|
144
|
+
clearInterval(spinnerInterval);
|
|
145
|
+
process.stderr.write(`\r${' '.repeat(maxLabelLen + 6)}\r`);
|
|
146
|
+
};
|
|
147
|
+
try {
|
|
148
|
+
result = await remoteUseCase.execute({
|
|
149
|
+
...commonInput,
|
|
150
|
+
remoteUrl: options.remote,
|
|
151
|
+
...(defaultCloneDir !== undefined && { defaultCloneDir }),
|
|
152
|
+
cloneOptions: {
|
|
153
|
+
onProgress: (data) => {
|
|
154
|
+
// Parse clone progress from gh CLI stderr
|
|
155
|
+
const progressMatch = data.match(/(?:Cloning|Receiving objects|Resolving deltas|Updating files)[^]*?(?:\d+%|\.{3})/);
|
|
156
|
+
if (progressMatch) {
|
|
157
|
+
// Sanitize: take the first meaningful line, trim whitespace
|
|
158
|
+
const line = progressMatch[0].split('\n')[0].trim();
|
|
159
|
+
if (line.length > 0) {
|
|
160
|
+
spinnerLabel = line;
|
|
161
|
+
if (spinnerLabel.length > maxLabelLen) {
|
|
162
|
+
maxLabelLen = spinnerLabel.length;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
clearSpinner();
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
clearSpinner();
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
// Local flow: use existing repository
|
|
178
|
+
const useCase = container.resolve(CreateFeatureUseCase);
|
|
179
|
+
const repoPath = options.repo ?? process.cwd();
|
|
180
|
+
result = await spinner('Thinking', () => useCase.execute({
|
|
181
|
+
...commonInput,
|
|
182
|
+
repositoryPath: repoPath,
|
|
183
|
+
}));
|
|
184
|
+
}
|
|
122
185
|
const { feature, warning } = result;
|
|
186
|
+
const repoPath = options.remote ? feature.repositoryPath : (options.repo ?? process.cwd());
|
|
123
187
|
const repoHash = createHash('sha256').update(repoPath).digest('hex').slice(0, 16);
|
|
124
188
|
const wtSlug = feature.branch.replace(/\//g, '-');
|
|
125
189
|
const worktreePath = join(getShepHomeDir(), 'repos', repoHash, 'wt', wtSlug);
|
|
@@ -166,6 +230,23 @@ export function createNewCommand() {
|
|
|
166
230
|
messages.newline();
|
|
167
231
|
}
|
|
168
232
|
catch (error) {
|
|
233
|
+
// Handle GitHub-specific errors with actionable messages
|
|
234
|
+
if (error instanceof GitHubAuthError) {
|
|
235
|
+
messages.error('GitHub CLI is not authenticated. Run `gh auth login` to sign in.');
|
|
236
|
+
process.exitCode = 1;
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
if (error instanceof GitHubUrlParseError) {
|
|
240
|
+
messages.error(`Invalid GitHub URL: ${error.message}`);
|
|
241
|
+
messages.info('Supported formats: https://github.com/owner/repo, git@github.com:owner/repo.git, or owner/repo');
|
|
242
|
+
process.exitCode = 1;
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
if (error instanceof GitHubCloneError) {
|
|
246
|
+
messages.error(`Clone failed: ${error.message}`);
|
|
247
|
+
process.exitCode = 1;
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
169
250
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
170
251
|
messages.error('Failed to create feature', err);
|
|
171
252
|
process.exitCode = 1;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Feature } from '../../../../../packages/core/src/domain/generated/output.js';
|
|
2
|
+
interface Attachment {
|
|
3
|
+
path: string;
|
|
4
|
+
name: string;
|
|
5
|
+
}
|
|
6
|
+
interface CreateFeatureFromRemoteInput {
|
|
7
|
+
remoteUrl: string;
|
|
8
|
+
description: string;
|
|
9
|
+
attachments?: Attachment[];
|
|
10
|
+
sessionId?: string;
|
|
11
|
+
approvalGates?: {
|
|
12
|
+
allowPrd: boolean;
|
|
13
|
+
allowPlan: boolean;
|
|
14
|
+
allowMerge?: boolean;
|
|
15
|
+
};
|
|
16
|
+
push?: boolean;
|
|
17
|
+
openPr?: boolean;
|
|
18
|
+
parentId?: string;
|
|
19
|
+
fast?: boolean;
|
|
20
|
+
pending?: boolean;
|
|
21
|
+
agentType?: string;
|
|
22
|
+
model?: string;
|
|
23
|
+
}
|
|
24
|
+
export declare function createFeatureFromRemote(input: CreateFeatureFromRemoteInput): Promise<{
|
|
25
|
+
feature?: Feature;
|
|
26
|
+
error?: string;
|
|
27
|
+
}>;
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=create-feature-from-remote.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-feature-from-remote.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/actions/create-feature-from-remote.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sCAAsC,CAAC;AAQpE,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAQD,UAAU,4BAA4B;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE;QACd,QAAQ,EAAE,OAAO,CAAC;QAClB,SAAS,EAAE,OAAO,CAAC;QACnB,UAAU,CAAC,EAAE,OAAO,CAAC;KACtB,CAAC;IACF,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA0FhD"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use server';
|
|
2
|
+
import { resolve } from '../../lib/server-container.js';
|
|
3
|
+
import { GitHubAuthError, GitHubUrlParseError, GitHubCloneError, } from '../../../../../packages/core/src/application/ports/output/services/github-repository-service.interface.js';
|
|
4
|
+
import { composeUserInput } from './compose-user-input.js';
|
|
5
|
+
export async function createFeatureFromRemote(input) {
|
|
6
|
+
const { remoteUrl, description, attachments, sessionId, approvalGates, push, openPr, parentId, fast, pending, agentType, model, } = input;
|
|
7
|
+
if (!remoteUrl?.trim()) {
|
|
8
|
+
return { error: 'GitHub URL is required' };
|
|
9
|
+
}
|
|
10
|
+
if (!description?.trim()) {
|
|
11
|
+
return { error: 'description is required' };
|
|
12
|
+
}
|
|
13
|
+
const userInput = composeUserInput(description, attachments);
|
|
14
|
+
const gates = {
|
|
15
|
+
allowPrd: approvalGates?.allowPrd ?? false,
|
|
16
|
+
allowPlan: approvalGates?.allowPlan ?? false,
|
|
17
|
+
allowMerge: approvalGates?.allowMerge ?? false,
|
|
18
|
+
};
|
|
19
|
+
try {
|
|
20
|
+
const useCase = resolve('CreateFeatureFromRemoteUseCase');
|
|
21
|
+
// Phase 1 (fast): import repo + create DB record — returns immediately
|
|
22
|
+
const { feature, shouldSpawn } = await useCase.createRecord({
|
|
23
|
+
remoteUrl,
|
|
24
|
+
userInput,
|
|
25
|
+
approvalGates: gates,
|
|
26
|
+
push: push ?? false,
|
|
27
|
+
openPr: openPr ?? false,
|
|
28
|
+
...(parentId ? { parentId } : {}),
|
|
29
|
+
description,
|
|
30
|
+
...(fast ? { fast } : {}),
|
|
31
|
+
...(pending ? { pending } : {}),
|
|
32
|
+
...(agentType ? { agentType } : {}),
|
|
33
|
+
...(model ? { model } : {}),
|
|
34
|
+
});
|
|
35
|
+
// Phase 2 (background): metadata generation, worktree, spec, agent spawn
|
|
36
|
+
// Fire-and-forget — the UI gets the real feature ID immediately
|
|
37
|
+
useCase
|
|
38
|
+
.initializeAndSpawn(feature, {
|
|
39
|
+
remoteUrl,
|
|
40
|
+
userInput,
|
|
41
|
+
approvalGates: gates,
|
|
42
|
+
push: push ?? false,
|
|
43
|
+
openPr: openPr ?? false,
|
|
44
|
+
...(parentId ? { parentId } : {}),
|
|
45
|
+
...(fast ? { fast } : {}),
|
|
46
|
+
...(pending ? { pending } : {}),
|
|
47
|
+
...(agentType ? { agentType } : {}),
|
|
48
|
+
...(model ? { model } : {}),
|
|
49
|
+
...(sessionId ? { sessionId } : {}),
|
|
50
|
+
}, shouldSpawn)
|
|
51
|
+
.catch((err) => {
|
|
52
|
+
// eslint-disable-next-line no-console
|
|
53
|
+
console.error('[createFeatureFromRemote] initializeAndSpawn failed:', err);
|
|
54
|
+
});
|
|
55
|
+
return { feature };
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
if (error instanceof GitHubAuthError) {
|
|
59
|
+
return {
|
|
60
|
+
error: 'GitHub CLI is not authenticated. Run `gh auth login` to sign in.',
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if (error instanceof GitHubUrlParseError) {
|
|
64
|
+
return { error: `Invalid GitHub URL: ${error.message}` };
|
|
65
|
+
}
|
|
66
|
+
if (error instanceof GitHubCloneError) {
|
|
67
|
+
return { error: `Clone failed: ${error.message}` };
|
|
68
|
+
}
|
|
69
|
+
const message = error instanceof Error ? error.message : 'Failed to create feature from remote';
|
|
70
|
+
return { error: message };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-create-drawer.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"feature-create-drawer.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.tsx"],"names":[],"mappings":"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"}
|
package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useCallback, useEffect, useRef } from 'react';
|
|
4
|
-
import { PaperclipIcon, ChevronsUpDown, CheckIcon, Zap, Clock, FolderPlus, Loader2, } from 'lucide-react';
|
|
4
|
+
import { PaperclipIcon, ChevronsUpDown, CheckIcon, Zap, Clock, FolderPlus, Loader2, Github, } from 'lucide-react';
|
|
5
5
|
import { cn } from '../../../lib/utils.js';
|
|
6
6
|
import { useSoundAction } from '../../../hooks/use-sound-action.js';
|
|
7
7
|
import { BaseDrawer } from '../../common/base-drawer/index.js';
|
|
@@ -22,6 +22,7 @@ import { pickFolder } from '../../common/add-repository-button/pick-folder.js';
|
|
|
22
22
|
import { ReactFileManagerDialog } from '../../common/react-file-manager-dialog/index.js';
|
|
23
23
|
import { useFeatureFlags } from '../../../hooks/feature-flags-context.js';
|
|
24
24
|
import { addRepository } from '../../../app/actions/add-repository.js';
|
|
25
|
+
import { GitHubImportDialog } from '../../common/github-import-dialog/index.js';
|
|
25
26
|
import { pickFiles } from './pick-files.js';
|
|
26
27
|
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB
|
|
27
28
|
const ALLOWED_EXTENSIONS = new Set([
|
|
@@ -495,8 +496,9 @@ export function RepositoryCombobox({ repositories, value, onChange, onAddReposit
|
|
|
495
496
|
const [isAdding, setIsAdding] = useState(false);
|
|
496
497
|
const [addError, setAddError] = useState(null);
|
|
497
498
|
const [showReactPicker, setShowReactPicker] = useState(false);
|
|
499
|
+
const [showGitHubImport, setShowGitHubImport] = useState(false);
|
|
498
500
|
const inputRef = useRef(null);
|
|
499
|
-
const { reactFileManager: useReactFileManager } = useFeatureFlags();
|
|
501
|
+
const { reactFileManager: useReactFileManager, githubImport } = useFeatureFlags();
|
|
500
502
|
const selectedRepo = repositories.find((r) => r.path === value);
|
|
501
503
|
const filtered = query.trim()
|
|
502
504
|
? repositories.filter((r) => r.name.toLowerCase().includes(query.toLowerCase()) ||
|
|
@@ -551,6 +553,18 @@ export function RepositoryCombobox({ repositories, value, onChange, onAddReposit
|
|
|
551
553
|
setIsAdding(false);
|
|
552
554
|
}
|
|
553
555
|
}, [isAdding, useReactFileManager, addRepoFromPath]);
|
|
556
|
+
const handleGitHubImportComplete = useCallback((repository) => {
|
|
557
|
+
const newRepo = {
|
|
558
|
+
id: repository.id,
|
|
559
|
+
name: repository.name,
|
|
560
|
+
path: repository.path,
|
|
561
|
+
};
|
|
562
|
+
onAddRepository?.(newRepo);
|
|
563
|
+
onChange(newRepo.path);
|
|
564
|
+
setOpen(false);
|
|
565
|
+
setQuery('');
|
|
566
|
+
setShowGitHubImport(false);
|
|
567
|
+
}, [onAddRepository, onChange]);
|
|
554
568
|
const handleReactPickerSelect = useCallback(async (path) => {
|
|
555
569
|
setShowReactPicker(false);
|
|
556
570
|
if (!path)
|
|
@@ -575,8 +589,8 @@ export function RepositoryCombobox({ repositories, value, onChange, onAddReposit
|
|
|
575
589
|
setQuery('');
|
|
576
590
|
}
|
|
577
591
|
}, [open]);
|
|
578
|
-
return (_jsxs(_Fragment, { children: [_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { type: "button", role: "combobox", "aria-expanded": open, "aria-label": "Repository", disabled: disabled, "data-testid": "repository-combobox", className: cn('border-input bg-background ring-offset-background focus:ring-ring flex h-9 w-full items-center justify-between rounded-md border px-3 py-2 text-sm focus:ring-2 focus:ring-offset-2 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50', !selectedRepo && 'text-muted-foreground'), children: [_jsx("span", { className: "truncate", children: selectedRepo ? selectedRepo.name : 'Select repository...' }), _jsx(ChevronsUpDown, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" })] }) }), _jsx(PopoverContent, { className: "w-80 p-0", align: "start", "data-testid": "repository-combobox-content", children: _jsxs("div", { className: "flex flex-col", children: [_jsx("div", { className: "border-b p-2", children: _jsx(Input, { ref: inputRef, placeholder: "Search repositories...", value: query, onChange: (e) => setQuery(e.target.value), className: "h-8 border-0 p-0 text-sm shadow-none focus-visible:ring-0", "data-testid": "repository-search" }) }), _jsx("div", { className: "max-h-48 overflow-y-auto py-1", role: "listbox", "aria-label": "Repositories", children: filtered.length === 0 ? (_jsx("p", { className: "text-muted-foreground px-3 py-2 text-sm", "data-testid": "repository-empty", children: "No repositories found." })) : (filtered.map((r) => (_jsxs("button", { type: "button", role: "option", "aria-selected": value === r.path, onClick: () => handleSelect(r.path), className: cn('hover:bg-accent hover:text-accent-foreground flex w-full items-center gap-2 px-3 py-2 text-sm', value === r.path && 'bg-accent/50'), "data-testid": `repository-option-${r.id}`, children: [_jsx(CheckIcon, { className: cn('h-4 w-4 shrink-0', value !== r.path && 'invisible') }), _jsxs("span", { className: "flex flex-col items-start truncate", children: [_jsx("span", { className: "truncate", children: r.name }), _jsx("span", { className: "text-muted-foreground truncate text-xs", children: r.path })] })] }, r.id)))) }), _jsx(Separator, {}), _jsxs("button", { type: "button", onClick: handleAddRepository, disabled: isAdding, className: "hover:bg-accent hover:text-accent-foreground flex w-full items-center gap-2 px-3 py-2 text-sm", "data-testid": "add-repository-item", children: [isAdding ? (_jsx(Loader2, { className: "h-4 w-4 shrink-0 animate-spin" })) : (_jsx(FolderPlus, { className: "h-4 w-4 shrink-0" })), _jsx("span", { children: "Add new repository..." })] }), addError ? (_jsx("p", { className: "px-3 pb-2 text-xs text-red-500", "data-testid": "add-repository-error", children: addError })) : null] }) })] }), _jsx(ReactFileManagerDialog, { open: showReactPicker, onOpenChange: (isOpen) => {
|
|
592
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { type: "button", role: "combobox", "aria-expanded": open, "aria-label": "Repository", disabled: disabled, "data-testid": "repository-combobox", className: cn('border-input bg-background ring-offset-background focus:ring-ring flex h-9 w-full items-center justify-between rounded-md border px-3 py-2 text-sm focus:ring-2 focus:ring-offset-2 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50', !selectedRepo && 'text-muted-foreground'), children: [_jsx("span", { className: "truncate", children: selectedRepo ? selectedRepo.name : 'Select repository...' }), _jsx(ChevronsUpDown, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" })] }) }), _jsx(PopoverContent, { className: "w-80 p-0", align: "start", "data-testid": "repository-combobox-content", children: _jsxs("div", { className: "flex flex-col", children: [_jsx("div", { className: "border-b p-2", children: _jsx(Input, { ref: inputRef, placeholder: "Search repositories...", value: query, onChange: (e) => setQuery(e.target.value), className: "h-8 border-0 p-0 text-sm shadow-none focus-visible:ring-0", "data-testid": "repository-search" }) }), _jsx("div", { className: "max-h-48 overflow-y-auto py-1", role: "listbox", "aria-label": "Repositories", children: filtered.length === 0 ? (_jsx("p", { className: "text-muted-foreground px-3 py-2 text-sm", "data-testid": "repository-empty", children: "No repositories found." })) : (filtered.map((r) => (_jsxs("button", { type: "button", role: "option", "aria-selected": value === r.path, onClick: () => handleSelect(r.path), className: cn('hover:bg-accent hover:text-accent-foreground flex w-full items-center gap-2 px-3 py-2 text-sm', value === r.path && 'bg-accent/50'), "data-testid": `repository-option-${r.id}`, children: [_jsx(CheckIcon, { className: cn('h-4 w-4 shrink-0', value !== r.path && 'invisible') }), _jsxs("span", { className: "flex flex-col items-start truncate", children: [_jsx("span", { className: "truncate", children: r.name }), _jsx("span", { className: "text-muted-foreground truncate text-xs", children: r.path })] })] }, r.id)))) }), _jsx(Separator, {}), githubImport ? (_jsxs("button", { type: "button", onClick: () => setShowGitHubImport(true), className: "hover:bg-accent hover:text-accent-foreground flex w-full items-center gap-2 px-3 py-2 text-sm", "data-testid": "import-github-item", children: [_jsx(Github, { className: "h-4 w-4 shrink-0" }), _jsx("span", { children: "Import from GitHub..." })] })) : null, _jsxs("button", { type: "button", onClick: handleAddRepository, disabled: isAdding, className: "hover:bg-accent hover:text-accent-foreground flex w-full items-center gap-2 px-3 py-2 text-sm", "data-testid": "add-repository-item", children: [isAdding ? (_jsx(Loader2, { className: "h-4 w-4 shrink-0 animate-spin" })) : (_jsx(FolderPlus, { className: "h-4 w-4 shrink-0" })), _jsx("span", { children: "Add new repository..." })] }), addError ? (_jsx("p", { className: "px-3 pb-2 text-xs text-red-500", "data-testid": "add-repository-error", children: addError })) : null] }) })] }), _jsx(ReactFileManagerDialog, { open: showReactPicker, onOpenChange: (isOpen) => {
|
|
579
593
|
if (!isOpen)
|
|
580
594
|
setShowReactPicker(false);
|
|
581
|
-
}, onSelect: handleReactPickerSelect })] }));
|
|
595
|
+
}, onSelect: handleReactPickerSelect }), _jsx(GitHubImportDialog, { open: showGitHubImport, onOpenChange: setShowGitHubImport, onImportComplete: handleGitHubImportComplete })] }));
|
|
582
596
|
}
|
|
@@ -11,4 +11,10 @@ export declare const EmptyList: Story;
|
|
|
11
11
|
export declare const PreSelected: Story;
|
|
12
12
|
/** Disabled state — combobox trigger button is non-interactive. */
|
|
13
13
|
export declare const Disabled: Story;
|
|
14
|
+
/** GitHub import enabled — "Import from GitHub..." button visible in dropdown. */
|
|
15
|
+
export declare const WithGitHubImport: Story;
|
|
16
|
+
/** GitHub import disabled — "Import from GitHub..." button hidden. */
|
|
17
|
+
export declare const WithGitHubImportDisabled: Story;
|
|
18
|
+
/** Empty list with GitHub import — shows import option alongside add repository. */
|
|
19
|
+
export declare const EmptyWithGitHubImport: Story;
|
|
14
20
|
//# sourceMappingURL=repository-combobox.stories.d.ts.map
|