Skip to content

Key link profiles by git remote URL for cross-clone sharing - #18

Merged
rafa-thayto merged 7 commits into
mainfrom
rafa-thayto/add-new-folder
Mar 6, 2026
Merged

Key link profiles by git remote URL for cross-clone sharing#18
rafa-thayto merged 7 commits into
mainfrom
rafa-thayto/add-new-folder

Conversation

@rafa-thayto

@rafa-thayto rafa-thayto commented Mar 6, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Extract inline ANSI color helpers into a shared src/lib/color.ts module
  • Add src/lib/git.ts utility for resolving git repo root, common-dir, and normalized remote URL (e.g. github.com/org/repo)
  • resolveProfile now resolves by normalized remote URL → git common-dir → directory walk, enabling profiles to be shared across clones and worktrees of the same repository
  • Add moveProfile for migrating old directory-keyed profiles to remote-based keys, with an interactive upgrade prompt
  • Replace select with a searchable search picker in the link command (type-to-filter by app name)
  • Show auto-link notice when a profile is resolved via a remote URL from another clone

Test plan

  • Run bun test — all existing and new tests pass
  • clerk link in a repo with a remote stores the profile keyed by normalized remote URL
  • Clone the same repo elsewhere — clerk link auto-detects the existing link
  • clerk link in a repo without a remote falls back to git common-dir, then cwd
  • clerk unlink displays the repo root path in confirmation prompt
  • Searchable picker filters apps by name as you type
  • Existing directory-keyed profiles trigger an upgrade prompt to migrate to remote-based keys

Extract ANSI color helpers (dim, bold, cyan, green, blue, yellow) into
a reusable module so commands no longer define them inline.
Introduce helpers to resolve the git repo root, common-dir (for worktree
support), and a normalized remote URL (e.g. github.com/org/repo) used
as a stable cross-clone profile key.
Replace inline ANSI escape sequences with imports from lib/color.ts.
Also extract a capturedOutput helper in deploy tests.
resolveProfile now checks normalized remote URL first, then git
common-dir, then falls back to directory walking. Adds moveProfile
for migrating old directory-keyed profiles to remote-based keys.
- Key profiles by normalized remote URL for cross-clone sharing
- Replace select prompt with searchable picker (type-to-filter)
- Add auto-link notice when resolved via remote from another clone
- Offer upgrade prompt for directory-keyed profiles with an available remote
- Fall back to git-common-dir or cwd when no remote is configured
Show the git repo root instead of the raw profile key when
confirming and reporting an unlink operation.
@rafa-thayto
rafa-thayto requested a review from kylemac March 6, 2026 05:29
- Create src/test/stubs.ts with reusable mock objects (configStubs,
  credentialStoreStubs, gitStubs, promptsStubs, tokenExchangeStubs)
- Add stubFetch helper to fix Bun's globalThis.fetch type mismatch
  (missing preconnect method, unknown RequestInfo/HeadersInit types)
- Add capturedOutput helper for collecting spy output
- Refactor 14 test files to use shared stubs
- Fix env/pull.ts optional secret_key type error
- Add test script to package.json
- Add assertion for repo root path in unlink confirmation prompt
Comment thread src/lib/git.ts

@dmoerner dmoerner left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! One large-scale comment and one specific comment, but I don't see any blockers at this stage.

The large-scale comment is that I would have liked to see this broken up into 3 PRs. The tests stubbing, the color changes, and the git linking. I think smaller PRs can help improve review velocity and make it easier to focus on things.

dmoerner added a commit that referenced this pull request Mar 6, 2026
Implement `config schema` command to allow fetching the schema from
PLAPI. This will make Clerk CLI config self-documenting, to an extent.
The bare command dumps the whole schema, there is also a `--keys`
argument to dump only a specific key.

WIP commit, tests will need to be rebased to use the new patterns once
#18 is merged.
@rafa-thayto

Copy link
Copy Markdown
Contributor Author

noted your points here @dmoerner ty!

@rafa-thayto
rafa-thayto merged commit aad90b2 into main Mar 6, 2026
1 check passed
rafa-thayto added a commit that referenced this pull request Mar 24, 2026
- Extract utility helpers from index.ts into heuristics.ts (jfoshee #18)
- Extract text transformations into transformations.ts (jfoshee #19)
- Fix wrapBodyWithProvider whitespace/formatting bug (jfoshee #20)
- Refactor tests to use semantic path lookups instead of positional
  array indexing (jfoshee #13/#17)
- Add doc comment to helpers.ts clarifying shared usage (jfoshee #15)
- Update astro.md NEVER rules to start with "Never" (jfoshee #25)
- Remove section banner comments from init command files
rafa-thayto added a commit that referenced this pull request Mar 24, 2026
…mpts (#38)

* chore: add magicast dependency for AST-based code manipulation

Used by the init command's framework scaffolders to safely add imports
and modify config files without breaking existing code.

* refactor: fix framework SDK names and detection priority

- Fix @clerk/clerk-react → @clerk/react
- Fix @clerk/tanstack-start → @clerk/tanstack-react-start
- Remove standalone vite detection (covered by react)
- Reorder priority: scaffoldable frameworks first, then expo, react,
  express, fastify
- Export readDeps for use by init context module

* feat(init): add project context gathering

Detects framework, TypeScript, src/ directory convention, package
manager, Next.js router variant, middleware filename, layout path,
and existing Clerk SDK installation.

* feat(init): add scaffold system with framework-specific scaffolders

Add idempotent scaffolders for Next.js (App + Pages Router), React,
React Router, Nuxt, TanStack Start, Astro, and Vue. Each scaffolder
generates framework-appropriate boilerplate (middleware, providers,
auth pages) while preserving existing user code.

Shared helpers handle AST-based import injection (magicast with string
fallback), middleware composition, and auth page creation.

* feat(init): add scan, format, preview, and agent prompt modules

- scan: detect competing auth libraries pre-scaffold and scan for
  hardcoded keys/leftover imports post-scaffold
- format: run Prettier/Biome on generated files
- preview: show planned file changes and confirm before writing
- prompts: framework-specific agent mode prompts with exact code
  snippets and file paths

* feat(init): rewrite init command with scaffold and scan flow

Replace the minimal init (login + link + install + env pull) with a
full orchestrator that also scaffolds framework boilerplate and scans
for issues. Skips login/link when already authenticated and linked.

* docs(init): update README with scaffolding docs and framework table

- Add Agent Mode section
- Add env var column to framework detection table
- Add Expo, Express, Fastify to detection table
- Document scaffolding actions for all 8 supported frameworks
- Note that Expo/Express/Fastify are detected but not scaffolded

* refactor(init): restructure agent prompts into markdown templates

Replace monolithic prompts.ts with individual .md template files per
framework, matching the clerk-docs structured format (sections, rules,
deprecated patterns, verification checklists).

- Move prompts to src/commands/init/prompts/*.md with {{PLACEHOLDER}}
  interpolation
- Add prompts for expo, express, and fastify frameworks
- Merge DOCS_URLS + TEMPLATE_MAP into single FRAMEWORK_PROMPTS record
- Extract shared pmInstallCommand, remove duplicate pmAddCommand
- Fix formatter-escaped underscores (\_app, \_\_root) in template loader
- Add "After Setup" section to all prompts (matching clerk-docs)

* refactor(init): make FileAction a discriminated union and extend FrameworkScaffold

FileAction is now a proper discriminated union with a dedicated `skip`
variant that has no `content` field, enforced at the type level. The
FrameworkScaffold interface gains `dep`, `variant`, `minMajorVersion`,
`matches()`, and optional `enrichContext()` so each scaffolder is
self-describing. Enrichment fields (variant, layoutPath,
middlewareBasename) become optional on ProjectContext since they are
populated after gatherContext.

* refactor(init): extract Next.js context enrichment to frameworks/nextjs-context

Move parseNextMajorVersion, detectMiddlewareBasename, detectNextjsVariant,
and detectLayoutPath from context.ts into a dedicated nextjs-context.ts
module. This isolates framework-specific logic behind the enrichContext
hook on FrameworkScaffold rather than coupling it to gatherContext.
Parallelize dirExists calls with Promise.all in both files.

* refactor(init): replace scaffold key map with matches() dispatch and version check

Replace the Record<string, FrameworkScaffold> keyed map with an array
using satisfies and matches()-based lookup. Add enrichProjectContext()
that delegates to each scaffolder's enrichContext hook. Add minMajorVersion
guard that checks the framework dep version before scaffolding.

* refactor(init): add shared helpers and flatten scaffoldNextjsMiddleware

Add parseMajorVersion, insertAfterLastImport, wrapBodyWithProvider, and
resolveNextjsMiddlewareBasename to helpers. Use proper skip FileAction
in scaffoldAuthPage and scaffoldNextjsMiddleware. Flatten nested ifs
with early returns and store BunFile reference once. The middleware
fallback now resolves from the actual Next.js version in deps.

* refactor(init): update all scaffolders with skip actions, dep/matches, and minMajorVersion

Each scaffolder now exports dep, matches(), and minMajorVersion. All skip
cases use the proper FileAction skip variant. Flatten nested ifs with
early returns across scaffoldLayout, scaffoldApp, scaffoldMiddleware, and
scaffoldConfig. Use shared wrapBodyWithProvider (nextjs-app, tanstack-start),
insertAfterLastImport (react-router, vue), and extract wrapWithClerkProvider
in react.ts. Store BunFile references to avoid duplicate creation.

* refactor(init): remove findings cap, precompile regexes, and unexport internal types

Remove MAX_FINDINGS so no context is lost. Convert IGNORE_DIRS to a Set
for O(1) lookups. Precompile CODE_SCANS regexes once at module level.
Inline matchesFramework guard. Convert AuthLibraryScan and CodeScan from
exported interfaces to local types since they are not imported elsewhere.

* refactor(init): replace readFileSync with static text imports for compiled binaries

Use Bun's `import ... with { type: "text" }` to embed markdown prompt
templates at build time. This replaces the runtime readFileSync +
import.meta.dir approach that would break in compiled Bun binaries.
Remove the template cache since imports are already static.

* refactor(init): abstract formatter config into data-driven array

Replace inline if-checks for prettier/biome with a FormatterConfig type
and a FORMATTERS array. The runFormatters loop now iterates the config,
making it trivial to add new formatters.

* test(init): update tests for discriminated union, enrichContext, and version check

Update context tests to call enrichProjectContext separately from
gatherContext and import parseMajorVersion from helpers. Add tests for
scaffold version check (below minimum, meets minimum, Next.js 16 proxy).
Update nextjs-app tests to assert on the skip type instead of skipReason
field. Update scan test to verify all findings are returned without cap.

* chore: update bun.lock with magicast dependency

* refactor(init): extract shared auth and config scaffolding helpers

Add jsxAuthPageContent with type-safe JsxClerkPackage union,
scaffoldConfigFile generic for the find→check→modify pattern,
authComponentName, and inline capitalize. These shared helpers
eliminate duplication across framework scaffolders.

* refactor(init): use shared helpers in framework scaffolders

Replace duplicated authRouteContent/nextjsAuthPageContent with
jsxAuthPageContent, replace per-framework scaffoldConfig with
scaffoldConfigFile in astro, nuxt, and react-router. Remove
unused imports and add enableV8Middleware documentation.

* refactor(init): restructure preview, prompts, and scan modules

Update preview formatting to use switch on discriminated union,
restructure prompts/index.ts with typed template resolution and
variable building, and minor scan.ts cleanup.

* test(init): add framework scaffolder and scan tests

Add tests for nextjs-app (middleware composition, provider wrapping,
src/ paths, proxy basename), react-router (root modification, loader
merge), tanstack-start (base dir detection), and scan (no findings cap).

* chore(init): add markdown type declaration for static text imports

* feat(init): add framework lookup utility with aliases

Export FRAMEWORK_MAP and add lookupFramework() to resolve framework
names or aliases (e.g. "tanstack-start" → "@tanstack/react-start")
along with FRAMEWORK_NAMES for validation and display.

* feat(init): add i18nLocaleDir to ProjectContext type

Add optional i18nLocaleDir field to ProjectContext for frameworks
that detect locale-based routing directories (e.g. [locale], [lang]).

* refactor(init): extract previewPlan display function

Split preview logic into previewPlan() (display only) and
previewAndConfirm() (display + prompt) to support --yes mode
without duplicating the plan rendering code.

* feat(init): add shared scaffolding helpers for auth, env, and i18n

Add reusable helpers for all framework scaffolders:
- authFileSpecs() factory for sign-in/sign-up file generation
- scaffoldEnvVars() and SIGN_ROUTE_ENV_VARS for env file management
- htmlAuthComponentMarkup() and jsxAuthComponentMarkup() with
  Tailwind/plain CSS variants
- hasTailwindStyles() for style detection
- i18n middleware detection, composition, and routing helpers
  (detectI18nMiddlewareLib, composeWithI18nMiddleware, etc.)
- findFirstDirMatch() for generic directory scanning

* feat(init): detect i18n locale directory in Next.js context

Add detectI18nLocaleDir() to identify App Router locale directories
(e.g. [locale], [lang]) by checking for layout files inside dynamic
segments. Populate i18nLocaleDir in enrichNextjsContext() so
scaffolders can place auth pages inside locale-prefixed paths.

* feat(init): support framework override in context gathering

Accept optional frameworkOverride parameter in gatherContext() to
skip auto-detection when the user specifies --framework explicitly.

* feat(init): wire --framework, --yes, and --prompt CLI options

Register --framework <name>, -y/--yes, and --prompt options on the
init command. Wire them through to framework override resolution,
preview-only plan display, and agent prompt output respectively.

* refactor(init): migrate framework scaffolders to shared helpers

Update all six framework scaffolders (Astro, Next.js App/Pages,
Nuxt, React Router, TanStack Start) to use the shared helpers:
- authFileSpecs() factory for sign-in/sign-up generation
- scaffoldEnvVars() for .env file management
- hasTailwindStyles() for consistent style detection
- i18n-aware auth page placement using locale directory detection
- htmlAuthComponentMarkup()/jsxAuthComponentMarkup() for rendering

* test(init): add i18n detection and locale routing tests

Add tests for i18n support across frameworks:
- Context: locale directory detection ([locale], [lang]) with
  layout file validation and src/ convention support
- Next.js App Router: i18n auth page placement, composed Clerk +
  next-intl middleware, expression export handling, env var scaffolding
- React Router: ($locale) prefix detection for auth routes
- TanStack Start: {-$locale} directory detection for auth routes

* docs(init): document new CLI options and update help output

Add --framework, --yes, and --prompt options to init command README
with usage examples. Update root README help output with --verbose
flag documentation.

* refactor(init): address PR review comments

- Extract utility helpers from index.ts into heuristics.ts (jfoshee #18)
- Extract text transformations into transformations.ts (jfoshee #19)
- Fix wrapBodyWithProvider whitespace/formatting bug (jfoshee #20)
- Refactor tests to use semantic path lookups instead of positional
  array indexing (jfoshee #13/#17)
- Add doc comment to helpers.ts clarifying shared usage (jfoshee #15)
- Update astro.md NEVER rules to start with "Never" (jfoshee #25)
- Remove section banner comments from init command files

* test(init): add missing framework scaffolder tests

Add test coverage for the 5 framework scaffolders that were missing
tests (jfoshee #21/#22):
- astro.test.ts (10 tests)
- nuxt.test.ts (8 tests)
- vue.test.ts (6 tests)
- react.test.ts (7 tests)
- nextjs-pages.test.ts (10 tests)

All tests use semantic path-based lookups via findAction() helper.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants