Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,9 @@ jobs:
run: bun install
- name: Install local @capgo/cli package
working-directory: capgo
run: bun add -d file:..
run: |
bun remove @capgo/cli
bun add -d file:..
- name: Run CLI tests
working-directory: capgo
run: LOCAL_CLI_PATH="../../../dist/index.js" bun run test:cli
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- Put command implementation logic in dedicated modules/handlers instead of inline `.action(...)` bodies in `src/index.ts`.
- When adding or changing a CLI command, prefer an exported command handler function in a dedicated module and wire it from `src/index.ts`.
- When adding or changing a CLI command, command option, or CLI-facing workflow, update the TanStack Intent skill docs in `skills/` as part of the same change so the published skills stay aligned with `webdocs/` and `src/index.ts`.
- For end-customer-facing docs and skills in `skills/` and `webdocs/`, use generic command runners in examples (`npx @capgo/cli@latest ...`) instead of Bun-specific runners. Reserve `bun` and `bunx` for repo-local development and agent execution.
- Reuse shared option descriptions from `src/index.ts` when an option already exists instead of introducing slightly different wording.
- For CLI-facing output, use `@clack/prompts` (`log`, `spinner`, `intro`, `outro`, `confirm`, `select`) to stay consistent with the rest of the CLI UX.
- If a command may run in non-interactive mode, do not rely on spinner-only output; provide plain log output or a non-TTY fallback.
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,15 @@
"test:build-zip-filter": "bun test/test-build-zip-filter.mjs",
"test:checksum": "bun test/test-checksum-algorithm.mjs",
"test:ci-prompts": "bun test/test-ci-prompts.mjs",
"test:onboarding-recovery": "bun test/test-onboarding-recovery.mjs",
"test:prompt-preferences": "bun test/test-prompt-preferences.mjs",
"test:esm-sdk": "node test/test-sdk-esm.mjs",
"test:mcp": "node test/test-mcp.mjs",
"test:version-detection": "node test/test-get-installed-version.mjs",
"test:version-detection:setup": "./test/fixtures/setup-test-projects.sh",
"test:platform-paths": "bun test/test-platform-paths.mjs",
"test:payload-split": "bun test/test-payload-split.mjs",
"test": "bun run test:bundle && bun run test:functional && bun run test:semver && bun run test:version-edge-cases && bun run test:regex && bun run test:upload && bun run test:credentials && bun run test:credentials-validation && bun run test:build-zip-filter && bun run test:checksum && bun run test:ci-prompts && bun run test:prompt-preferences && bun run test:esm-sdk && bun run test:mcp && bun run test:version-detection && bun run test:platform-paths && bun run test:payload-split"
"test": "bun run test:bundle && bun run test:functional && bun run test:semver && bun run test:version-edge-cases && bun run test:regex && bun run test:upload && bun run test:credentials && bun run test:credentials-validation && bun run test:build-zip-filter && bun run test:checksum && bun run test:ci-prompts && bun run test:onboarding-recovery && bun run test:prompt-preferences && bun run test:esm-sdk && bun run test:mcp && bun run test:version-detection && bun run test:platform-paths && bun run test:payload-split"
},
"devDependencies": {
"@antfu/eslint-config": "^7.0.0",
Expand Down
2 changes: 2 additions & 0 deletions skills/native-builds/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ Use this skill for Capgo Cloud native iOS and Android build workflows.
- Progress persists in `~/.capgo-credentials/onboarding/<appId>.json` — safe to interrupt and resume.
- Saves credentials to the same `~/.capgo-credentials/credentials.json` used by `build request`.
- Optionally kicks off the first build at the end.
- If the native `ios/` folder is missing, onboarding can offer to run `cap add ios` automatically instead of exiting immediately.
- Unexpected failures now keep the user inside the recovery screen, show package-manager-aware commands, and save a support bundle under `~/.capgo-credentials/support/`.

#### What it automates (iOS)

Expand Down
4 changes: 2 additions & 2 deletions skills/usage/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ TanStack Intent skills should stay focused and under the validator line limit, s

## Shared invocation rules

- Prefer `npx @capgo/cli@latest ...` in user-facing examples to match existing docs and CLI output.
- Prefer `npx @capgo/cli@latest ...` in user-facing examples in this repo.
- Many commands can infer `appId` and related config from the current Capacitor project.
- Shared public flags commonly include `-a, --apikey <apikey>` and `--verbose` on commands that support verbose output.

## Use this skill for quick routing

### Project setup and diagnostics

- `init [apikey] [appId]`: guided first-time setup for Capgo in a Capacitor app. The interactive flow now runs as a real Ink-based fullscreen onboarding so it uses the same UI stack as `build init` (alias: `build onboarding`), with a persistent dashboard, phase roadmap, progress cards, shared log area, and resume support. When dependency auto-detection fails on macOS, the flow opens a native file picker for `package.json` before falling back to manual path entry. If the user reuses a pending app that was already created in the web onboarding flow, the CLI syncs that selected dashboard app ID back into `capacitor.config.*` before the remaining steps continue. Outside that reused pending-app path, the CLI keeps using the local Capacitor app ID. It can also offer a final `npx skills add https://github.com/Cap-go/capgo-skills -g -y` install step before the GitHub support prompt; if accepted, the support menu includes `Cap-go/capgo-skills` alongside the updater-only and all-Capgo choices. If iOS sync validation fails during onboarding, the CLI can offer to run a one-line native reset command, wait for you to type `ready` after a manual fix, and offer cancellation every third failed retry.
- `init [apikey] [appId]`: guided first-time setup for Capgo in a Capacitor app. The interactive flow now runs as a real Ink-based fullscreen onboarding so it uses the same UI stack as `build init` (alias: `build onboarding`), with a persistent dashboard, phase roadmap, progress cards, shared log area, and resume support. When dependency auto-detection fails on macOS, the flow opens a native file picker for `package.json` before falling back to manual path entry. If the user reuses a pending app that was already created in the web onboarding flow, the CLI syncs that selected dashboard app ID back into `capacitor.config.*` before the remaining steps continue. Outside that reused pending-app path, the CLI keeps using the local Capacitor app ID. It can also offer a final `npx skills add https://github.com/Cap-go/capgo-skills -g -y` install step before the GitHub support prompt; if accepted, the support menu includes `Cap-go/capgo-skills` alongside the updater-only and all-Capgo choices. If native platforms are missing, the onboarding can offer to run `cap add` for you. If iOS sync validation fails during onboarding, the CLI can offer to run a one-line native reset command, wait for you to type `ready` after a manual fix, surface `doctor`, and save a support bundle before you leave the flow.
- `login [apikey]`: store an API key locally.
- `doctor`: inspect installation health and gather troubleshooting details.
- `probe`: test whether the update endpoint would deliver an update.
Expand Down
117 changes: 117 additions & 0 deletions src/build/onboarding/recovery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import type { OnboardingStep } from './types.js'
import { formatRunnerCommand } from '../../runner-command.js'

export interface BuildOnboardingRecoveryAdvice {
summary: string[]
commands: string[]
docs: string[]
}

export function getBuildOnboardingRecoveryAdvice(
message: string,
step: OnboardingStep | null,
pmRunner: string,
appId: string,
): BuildOnboardingRecoveryAdvice {
const lower = message.toLowerCase()
const summary: string[] = []
const commands = new Set<string>()
const docs = new Set<string>()

const addIosCommand = formatRunnerCommand(pmRunner, ['cap', 'add', 'ios'])
const syncIosCommand = formatRunnerCommand(pmRunner, ['cap', 'sync', 'ios'])
const doctorCommand = formatRunnerCommand(pmRunner, ['@capgo/cli@latest', 'doctor'])
const buildInitCommand = formatRunnerCommand(pmRunner, ['@capgo/cli@latest', 'build', 'init'])
const buildRequestCommand = formatRunnerCommand(pmRunner, ['@capgo/cli@latest', 'build', 'request', appId, '--platform', 'ios'])
const loginCommand = formatRunnerCommand(pmRunner, ['@capgo/cli@latest', 'login'])

if (step === 'no-platform' || step === 'adding-platform' || lower.includes('no ios/ directory')) {
summary.push(
'This project does not have a generated native iOS folder yet.',
'Create the iOS platform, then sync native sources before resuming onboarding.',
)
commands.add(addIosCommand)
commands.add(syncIosCommand)
}

if (lower.includes('api key verification failed') || lower.includes('401') || lower.includes('403')) {
summary.push(
'Apple rejected the App Store Connect credentials.',
'Double-check the .p8 file, Key ID, Issuer ID, and that the key still has Admin or Developer access.',
)
docs.add('https://capgo.app/docs/cli/cloud-build/ios/')
docs.add('https://appstoreconnect.apple.com/access/integrations/api')
}

if (lower.includes('fetch failed') || lower.includes('network') || lower.includes('etimedout') || lower.includes('enotfound') || lower.includes('econnreset')) {
summary.push(
'The CLI could not reach Apple or Capgo over the network.',
'Check VPN, proxy, firewall, and DNS settings, then retry from the saved step.',
)
commands.add(doctorCommand)
}

if (lower.includes('429') || lower.includes('rate limit')) {
summary.push(
'Apple is rate-limiting the request right now.',
'Wait a minute, then retry from the saved step instead of restarting the whole flow.',
)
}

if (lower.includes('certificate limit')) {
summary.push('Apple has reached the maximum number of active distribution certificates for this team.')
}

if (lower.includes('duplicate profile')) {
summary.push(
'Apple still has conflicting provisioning profiles for this bundle identifier.',
'You can let onboarding delete the duplicates automatically, or clean them up in App Store Connect and resume.',
)
docs.add('https://appstoreconnect.apple.com/access/users')
}

if (lower.includes('bundle') && lower.includes('identifier')) {
summary.push(
'Apple reported a bundle identifier conflict or bundle registration issue.',
`Verify that ${appId} is the bundle ID you intend to build for in both Capgo and your Capacitor config.`,
)
commands.add(doctorCommand)
}

if (lower.includes('file not found') || lower.includes('could not read file') || lower.includes('need .p8 file')) {
summary.push(
'The onboarding flow could not read the API key file from disk.',
'Re-select the .p8 file or move it somewhere stable before retrying.',
)
}

if (lower.includes('no capgo api key found')) {
summary.push('Capgo login is missing, so the first cloud build cannot be requested automatically.')
commands.add(loginCommand)
commands.add(buildRequestCommand)
}

if (lower.includes('credentials are saved')) {
summary.push('Your signing material is already saved locally, so you only need to re-run the build request.')
commands.add(buildRequestCommand)
}

if (summary.length === 0) {
summary.push(
'The onboarding flow hit an unexpected error.',
'Retry the saved step first. If it still fails, capture diagnostics and keep the support bundle when you contact support.',
)
commands.add(doctorCommand)
commands.add(buildInitCommand)
docs.add('https://capgo.app/docs/cli/cloud-build/ios/')
}
else {
commands.add(buildInitCommand)
}

return {
summary,
commands: Array.from(commands),
docs: Array.from(docs),
}
}
3 changes: 3 additions & 0 deletions src/build/onboarding/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export type Platform = 'ios' | 'android'
export type OnboardingStep
= | 'welcome'
| 'platform-select'
| 'adding-platform'
| 'credentials-exist'
| 'backing-up'
| 'api-key-instructions'
Expand Down Expand Up @@ -66,6 +67,7 @@ export interface OnboardingProgress {
export const STEP_PROGRESS: Record<OnboardingStep, number> = {
'welcome': 0,
'platform-select': 0,
'adding-platform': 0,
'credentials-exist': 0,
'backing-up': 0,
'api-key-instructions': 5,
Expand All @@ -92,6 +94,7 @@ export function getPhaseLabel(step: OnboardingStep): string {
switch (step) {
case 'welcome':
case 'platform-select':
case 'adding-platform':
case 'credentials-exist':
case 'backing-up':
return ''
Expand Down
Loading
Loading