Source-control providers as packages: one private workspace package per provider, one contract, one registry
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 25/100
- Issue type
- Refactor
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- github, gitlab, node.js, typescript
- Domain
- backend, build-system, devtools, testing
Research direction
Start with the workspace and package rules in blocked issue #1715, then read src/providers/types.ts, src/providers/registry.ts, src/utils/, and docs/dev/adding-a-provider.md. The issue outlines the target packages and acceptance checks, but this is a cross-cutting migration across providers, boundaries, tests, and docs. Done means all providers use the shared contract and registry, the stated checks pass, and the real CLI run is recorded in the PR.
Written by the indexing model from the issue text.
Description
Blocked by #1715. Open to contributions: once the workspace and the package rules from that issue are in, any provider can move on its own.
Problem Statement
teamai's source-control providers (git, github, gitlab, and also cnb, tgit, gitcode) live in src/providers/<name>/. They implement one interface (GitProvider in src/providers/types.ts) and are registered by hand in two maps (HOST_MAP and PROVIDERS in src/providers/registry.ts), plus a third set of known names (KNOWN_PROVIDERS). Nothing stops a provider from importing another provider's files, or the app from reaching into a provider's internals. The CLI lookup they share (cli-path) sits in src/utils/, and the provider tests sit in src/__tests__/, away from the code they test. Adding a provider means editing three tables and knowing which ones; docs/dev/adding-a-provider.md lists them by hand.
The tool packages issue, #1715, adds the npm workspace, teamai/package-boundaries and @teamai/shared. Providers can use all three without new infrastructure.
Solution
Each provider moves into its own private workspace package under packages/provider-<name>. Every provider implements one Provider contract from provider-core, and provider-definitions lists them in a typed BUILT_IN_PROVIDERS. The app reads providers only through one registry built from it. Hosts, detection and the CLI each provider drives are part of its definition, so adding a provider touches one package and one line in the registry.
packages/
├── provider-core/ # the contract and what several providers share
│ └── src/
│ ├── provider.ts # Provider, ProviderDefinition, RepoInfo, PrCreateOptions, PullRequestLookup
│ ├── repo-url.ts # URL parsing shared by every forge
│ ├── registry.ts # makeProviderRegistry(definitions) → byName, byHost, detect
│ └── errors.ts
├── provider-git/ # plain git: clone and push only, no API
│ └── src/
│ ├── index.ts # export const provider = { … } as const satisfies ProviderDefinition
│ ├── provider.ts provider.test.ts
│ └── fixtures/
├── provider-github/ # drives `gh`
│ └── src/
│ ├── index.ts
│ ├── provider.ts provider.test.ts
│ ├── gh-cli.ts gh-org.ts mr-fetch.ts
│ └── fixtures/ # recorded `gh` stdout
├── provider-gitlab/ # REST API, self-hosted host detection
│ └── src/
│ ├── index.ts
│ ├── provider.ts provider.test.ts
│ ├── gitlab-api.ts probe.ts org.ts mr-fetch.ts
│ └── fixtures/ # recorded API responses
├── provider-cnb/ provider-tgit/ provider-gitcode/
├── provider-definitions/
│ └── src/registry.ts # BUILT_IN_PROVIDERS: the ordered, literal list of every provider
└── provider-testing/
└── src/
├── fake-cli.ts # a fake `gh` / `cnb`: records argv, prints recorded stdout
├── fake-api.ts # replays recorded HTTP responses
└── conformance.ts # the suite every provider runs
Allowed imports: src/ → provider-definitions, provider-core, @teamai/shared; each provider → provider-core, @teamai/shared; never one provider → another, never src/ → a provider directly. teamai/package-boundaries, from #1715, enforces the general rules; it gains the provider pair.
The contract (shape, not final code)
export interface ProviderDefinition {
readonly name: string; // 'github'; kept literal by `as const satisfies`
readonly hosts: readonly string[]; // 'github.com'; replaces HOST_MAP rows
detect?(host: string): boolean; // self-hosted GitLab probe
readonly cli?: string; // 'gh', 'cnb'; resolved with @teamai/shared/cli-path
create(options: ProviderOptions): Provider;
}
export interface Provider { // today's GitProvider, whole
readonly name: string;
parseRepoInput(input: string): RepoInfo;
isAuthenticated(): boolean;
authenticate(): Promise<string>;
ensureInstalled(): Promise<void>;
cloneRepo(repo: string, localPath: string): void;
createRepo(owner: string, repo: string): Promise<void>;
createPullRequest(options: PrCreateOptions): Promise<string>;
readonly pullRequests: PullRequestLookup | null;
fetchMergeRequest?(url: string): Promise<MRData>;
listOrgRepos?(org: string, options?: { maxRepos?: number }): Promise<OrgRepoInfo[]>;
organizationExists?(org: string): boolean;
getOrganizationCreateUrl?(): string | null;
}
What a provider package looks like from the outside:
// packages/provider-gitlab/src/index.ts
export const provider = {
name: 'gitlab',
hosts: ['gitlab.com'],
detect: isSelfHostedGitLab,
create: (options) => new GitLabProvider(options),
} as const satisfies ProviderDefinition;
Behaviour a user sees does not change: teamai init <url> --provider <name> accepts the same names, host detection gives the same answer for the same URL, and unknown names give the same message, "Unknown git provider: "". Available: …".
Acceptance Criteria and Definition of Done
- Every provider lives in
packages/provider-<name>, andsrc/providers/is gone. -
HOST_MAPandPROVIDERSare derived fromBUILT_IN_PROVIDERS. Adding a provider to the registry without its hosts fails to compile. - Default-override eligibility is kept apart from registration:
KNOWN_PROVIDERS, the namesTEAMAI_DEFAULT_PROVIDERmay pick, is derived from the registered forge definitions only, never the genericgit, soTEAMAI_DEFAULT_PROVIDER=gitis still ignored and bareowner/repoinput resolves as before. The fallback from the CLI package name (@tencent/teamai-cli→tgit, elsegithub) does not change. -
teamai/package-boundariesrejects a provider importing another provider, andsrc/importing a provider directly. - Every provider passes the conformance suite: it parses its recorded repo inputs, builds its CLI or API calls for clone, create and pull request from recorded fixtures, and reports a missing CLI with exit 127 and the reason on stderr.
- Host detection and the default provider give the same answer as before for a table of inputs: URLs (self-hosted GitLab included), bare
owner/repo, andTEAMAI_DEFAULT_PROVIDERset togit,gitlaband an unknown name. -
npm run build,npm run typecheck,npm run lint,npx vitest runand the provider E2E files pass.node scripts/check-docs.mjs --writeshows no diff. - One real-CLI run after
npm run build(teamai init <url> --provider git,pull,push), recorded in the PR body. Other providers are left to CI where they cannot run locally, and the PR says so. -
docs/dev/adding-a-provider.mddescribes the package and the one registry line.CONTRIBUTINGnames the provider packages. The user-facing guide (docs/guide/providers.md) does not change.
Out of Scope
- New providers. Each one is its own issue, and becomes one package plus one registry line.
- Changing what any provider does.
- Providers loaded from outside the bundle.
Further Notes
- Migrate in bounded batches, each green on its own: the registry and the lint rule first, then
git, then one provider per change. cli-pathlives in@teamai/sharedonce #1715 lands. Providers keep usingresolveCliPathfor their own CLIs.
- Dominant language
- TypeScript
- Stars
- 5.7k
- Forks
- 486
- Avg merge
- 13h 3m
- Merged PRs (30d)
- 474
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from Tencent/teamai-cli
-
Lint rule: require `windowsHide: true` on every child_process call in production sourcePossibly taken @SaulMoro claimed this today. Openenhancement help wanted
Difficulty 4/5 3-5 days Newbie friendliness 25/100
Tencent/teamai-cli#1763 · 2 comments ·
Maintainers usually reply within 1 day
-
enhancement help wanted
Difficulty 5/5 Over a week Newbie friendliness 38/100
Tencent/teamai-cli#1753 · 2 comments ·
Maintainers usually reply within 1 day
-
[bug] Stop-hook updater reports skipped or failed updates as ok and repeats unsupported-install warningsPossibly taken @YogeLiu claimed this 1 day ago. Open
Difficulty 4/5 3-5 days Newbie friendliness 35/100
Tencent/teamai-cli#1752 · 1 comment ·
Maintainers usually reply within 1 day
-
bug help wanted
Difficulty 5/5 Over a week Newbie friendliness 32/100
Tencent/teamai-cli#1743 · 2 comments ·
Maintainers usually reply within 1 day
-
enhancement help wanted
Difficulty 5/5 Over a week Newbie friendliness 38/100
Tencent/teamai-cli#1721 ·
Maintainers usually reply within 1 day
All issues in Tencent/teamai-cli
Similar issues
-
Add: ESP 2 ROOpenapproved check:passed streams:add
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
Maintainers usually reply within 1 day
-
ipaas: Configure form shows "Deploy to apply." and "Overrides selected Link" when they don't applyOpen
Difficulty 2/5 1-3 hours Newbie friendliness 67/100
wso2/integration-control-plane#1118 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
UTDNebula/utd-notebook#311 ·
Maintainers usually reply within 2 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day