feat(domain): add unified remote repository support with auto-fork#531
feat(domain): add unified remote repository support with auto-fork#531
Conversation
adds three related capabilities for working with remote github repos: - shep repo init-remote creates a github repo from a local one - shep feat new --remote clones (or auto-forks) then creates a feature - web ui shows fork badge on forked repositories in the repo picker repository entity gains isFork and upstreamUrl fields backed by migration 055. importgithubrepositoryusecase now checks push access and auto-forks when the user lacks write permission, tracking both the fork and upstream urls for deduplication. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Code Review — Unified Remote Repository SupportReviewed 10 key files across domain, use cases, infrastructure, CLI, and Web layers. SummaryThe PR successfully unifies three feature streams into a coherent slice. Architecture is mostly clean, tests are present, CI-visible checks pass. But there are three correctness issues worth fixing before merge, a handful of architectural smells, and several minor cleanups. Critical issues (fix before merge)1. Fork path never configures the
|
Fixes identified in the PR #531 code review: - Fork path now configures the `upstream` git remote after cloning so that `git fetch upstream` and upstream-based PR workflows work out of the box. Previously the fork was isolated with no knowledge of its upstream repo. - InitRemoteRepositoryUseCase throws a typed GitPrError(REMOTE_ALREADY_EXISTS) instead of a generic Error so callers can programmatically detect and recover from the remote-already-configured case. - createGitHubRepo parses the actual github.com URL out of `gh repo create` stdout/stderr rather than returning the raw multi-line blob, falling back to `gh repo view --json url` if no URL is found in the output. - CreateFeatureFromRemoteUseCase.initializeAndSpawn now forwards feature.repositoryPath instead of an empty string placeholder. - Repository mapper's fromDatabase returns isFork as a clean boolean (row.is_fork === 1) instead of the asymmetric `true | undefined`. - Destination paths in the import use case are normalised to forward slashes before being stored, matching the cross-platform rules in packages/CLAUDE.md. Adds unit tests for every fix, including upstream remote configuration, URL parsing fallbacks, REMOTE_ALREADY_EXISTS error code, and boolean isFork round-tripping.
On Windows, node:path `join()` returns backslash-separated paths while the use case normalises destinations to forward slashes for cross-platform storage. The test was asserting the native `join()` output which caused Unit Tests (windows-latest) to fail. Assert the normalised forward-slash form directly and drop the now-unused `join` import.
Review Feedback AddressedAll 6 blocking issues from the review have been addressed in commits 590cf61 and 3b2b4f5. Both CI runs are now green on Linux and Windows. Blocking Fixes#1 — Fork flow was missing the #2 — #3 — #5 — #6 — #15 — Destination paths used platform-native separators Windows CI Follow-up (3b2b4f5)The initial fix commit passed Linux CI but failed VerificationFull local suite on this branch:
CI on commit 3b2b4f5:
Not Addressed (Non-blocking)The following lower-priority review items were flagged but are out of scope for this fix pass — happy to tackle in a follow-up if desired:
|
Summary
Adds three related capabilities for working with remote GitHub repositories, unifying the work from PRs #409, #430, and #434 into a single feature on current main:
shep repo init-remote [name]— creates a GitHub repository from a local one with no remote (viagh repo create --source=. --remote=origin --push)shep feat new --remote <url>— clones (or auto-forks) a GitHub repo then creates a feature on it, conflicts with--repogithubImportfeature flag now defaults totrueKey changes
Domain
Repositoryentity gainsisFork?: booleanandupstreamUrl?: string(TypeSpec + generated output)055-add-repository-fork-fieldsaddsis_forkandupstream_urlcolumns +idx_repositories_upstream_urlindex (idempotent)Auto-fork detection
IGitHubRepositoryServicegainsgetAuthenticatedUser(),checkPushAccess(),forkRepository()+GitHubForkErrorImportGitHubRepositoryUseCasenow checks push access before cloning. When the user lacks write permission, it forks viagh repo fork, clones the fork, setsupstreamremote, and persists fork metadataIRepositoryRepository.findByUpstreamUrl()for fork deduplicationInit remote
IGitPrServicegainscreateGitHubRepo(),addRemote()+REMOTE_ALREADY_EXISTS,REPO_CREATE_FAILEDerror codesInitRemoteRepositoryUseCaseguards against existing remotes and delegates togh repo createFeature creation from remote
CreateFeatureFromRemoteUseCase— composite that chains import → create featureexecute()for CLI,createRecord()+initializeAndSpawn()for Web (fast optimistic UI)i18n
repo.initRemote.*andfeat.new.{remoteOption,remoteConflict,forkedInfo}added to all 8 localesTest plan
pnpm typecheck— zero errorspnpm lint:fix— cleanpnpm test:unit— 388 files, 5585 tests passing (includes 44 new tests)pnpm test:int— 50 files, 595 tests passing (migration 055 runs cleanly)pnpm build— clean CLI buildpnpm build:storybook— clean storybook build with newWithForkBadgesstoryshep repo init-remote my-test-repoon a fresh local reposhep feat new --remote https://github.com/someone/public-repo "test feature"(triggers auto-fork path)Notes
IGitForkService(PR feat(agents): add gh repo create fallback when fork fails with no remotes #525, merged) which handles fork-and-PR within worktrees. The new fork methods onIGitHubRepositoryServicehandle auto-fork at import time — these are different code paths and do not conflict.MigrationParamssignature with idempotent column/index checks, consistent with migration 054.🤖 Generated with Claude Code