feat(cli): add upgrade command with @tailor-platform/sdk-codemod package#893
feat(cli): add upgrade command with @tailor-platform/sdk-codemod package#893
Conversation
🦋 Changeset detectedLatest commit: 4941b02 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
9d4fcd9 to
23c6684
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
⚡ pkg.pr.new
@tailor-platform/sdk@tailor-platform/create-sdk |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
📖 Docs Consistency Check✅ No inconsistencies found between documentation and implementation. Checked areas: CLI Command Implementation vs Documentation
CLI Reference
CLI Documentation Configuration
Implementation Details
Codemod Package
Notes
|
📖 Docs Consistency Check
|
| File | Issue | Suggested Fix |
|---|---|---|
CLAUDE.md |
Lines 43, 79 reference defineGenerators() but example/tailor.config.ts uses definePlugins() |
Update CLAUDE.md to reflect current pattern or clarify both are supported |
packages/sdk/docs/cli/upgrade.md |
Missing workflow explanation and context | Add manual section explaining version auto-detection and typical workflow |
Details
1. CLAUDE.md References Out-of-Date Pattern
Location: CLAUDE.md:43, 79
Issue: The documentation describes defineGenerators() as the pattern used in the example, but the actual implementation has moved to definePlugins():
-
CLAUDE.md line 43 says:
- example/tailor.config.ts - Configuration with defineConfig, defineAuth, defineIdp, defineStaticWebSite, defineGenerators
-
CLAUDE.md lines 77-79 has a "Generators" section explaining
defineGenerators()and referencingexample/tailor.config.ts -
Actual example/tailor.config.ts uses
definePlugins()with explicit plugin imports (lines 1-11)
The codemod define-generators-to-plugins migrates FROM defineGenerators() TO definePlugins() for v1 → v2 upgrades, confirming that definePlugins() is the current/recommended pattern.
Recommendation: Update CLAUDE.md to:
- Change line 43 to list
definePluginsinstead ofdefineGenerators - Update the "Generators" section (lines 77-79) to document
definePlugins()as the primary pattern
2. upgrade.md Missing Workflow Explanation
Location: packages/sdk/docs/cli/upgrade.md
Issue: The documentation contains only auto-generated command reference with no explanation of:
- How the target version is determined (auto-detected from
node_modules/@tailor-platform/sdk) - Typical upgrade workflow
- What codemods do or when to use them
Current state: The file contains only politty-generated markers with options table - no manual content sections.
Comparison: Other commands like apply have manual sections after the auto-generated content (e.g., "Migration Handling" section in packages/sdk/docs/cli/application.md).
Implementation details (from packages/sdk/src/cli/commands/upgrade/service.ts:63-79):
The upgrade service auto-detects the target SDK version from node_modules rather than requiring a --to flag.
Recommendation: Add a manual section to upgrade.md after the auto-generated sections explaining:
- Target version is auto-detected from installed
@tailor-platform/sdkinnode_modules - Typical workflow: upgrade SDK packages → run
tailor-sdk upgrade --from <old-version> - What codemods do (automated code transformations for breaking changes)
- Example usage scenario
Recommended Actions
- Update
CLAUDE.mdto reflect the currentdefinePlugins()pattern used in examples - Add workflow explanation section to
packages/sdk/docs/cli/upgrade.mdafter the auto-generated content
062df86 to
cb02595
Compare
Introduce `tailor-sdk upgrade` command that delegates code transformations to codemod.com's jssg runtime instead of a custom codemod engine. - CLI command with --to, --dry-run, --interactive, --path options - Thin orchestrator: version detection → codemod selection → jssg execution - Codemod registry with semver-based version filtering - First codemod: defineGenerators → definePlugins migration - Integration tests via `npx codemod jssg test`
…efinePlugins was introduced
…x Windows npx - Build transform scripts to dist/codemods/ via tsdown so Node 18/20 can import them without --experimental-strip-types - Use @tailor-platform/sdk-codemod@latest instead of locking to the SDK version, since the codemod registry filters by --from/--to internally - Resolve npx.cmd on Windows (same pattern as skills-installer)
…use file URLs for import - Return null when any defineGenerators argument is not in PLUGIN_MAP, preventing generation of invalid mixed tuple/plugin syntax - Use pathToFileURL for dynamic import in runner to support Windows paths - Document known limitations: aliased imports and same-file reference rename
…ename, walk parent node_modules - Use fileURLToPath instead of URL.pathname for readPackageJSON to handle Windows paths and percent-encoded characters correctly - Remove generators->plugins variable rename: loadConfig() discovers exports by value not name, so the rename is unnecessary and breaks same-file references - Walk up directory tree in detectInstalledVersion to find hoisted deps in workspace setups
… replace Node 22 glob - Skip files that don't import from @tailor-platform/sdk - Skip files that already contain definePlugins to avoid duplicate identifiers - Replace Node 22-only fs.glob with recursive readdir + picomatch for Node 18+ compatibility
…rface npx failures - Remove blanket definePlugins early return so mixed generator+plugin configs can be migrated; deduplicate import specifiers instead - Restrict step 2 identifier rename to call expressions only to prevent import-level duplicates - Emit warnings in CodemodRunResult when files contain defineGenerators but were not migrated (custom generators, unsupported patterns) - Check spawnSync exit status before JSON.parse to surface npm/registry failures with actionable error messages
…tions - Check if import path already exists in the file before adding plugin import lines, preventing duplicate declarations in mixed configs - Add TODO for --json structured output support in upgrade command
… bound, add structured output - Use regex matching on function name in import statements instead of import path to avoid suppressing needed imports when a different symbol is already imported from the same module - Add gte(fromVersion, codemod.since) to getApplicableCodemods filter so codemods are not applied to source versions older than their declared range - Emit logger.out(output) for structured stdout data alongside the human summary on stderr, honoring the CLI --json contract
… cwd, capture stderr - Check all SDK import statements globally for existing definePlugins instead of per-statement, fixing duplicate bindings when definePlugins is imported in a separate statement - Set cwd to projectRoot for spawnSync so npm reads local .npmrc config - Pipe stderr from sdk-codemod to capture actual error messages (e.g. invalid semver) instead of showing misleading network error suggestions
…ess path The upgrade command pipes sdk-codemod's stderr so it can surface failures via CLIError, but the success path never replayed the captured output. That silently swallowed the colorized unified diff emitted by `printDiff` in dry-run mode and the `Running: ...` / `N file(s) modified` progress messages. Write `result.stderr` to `process.stderr` after the error checks so users see the diagnostic output they rely on.
The import_statement query used an unanchored regex that could match subpath imports like @tailor-platform/sdk/plugin/kysely-type, potentially causing incorrect specifier handling.
…nning Drop the Node 18-compatible readdir walk and use fs.promises.glob directly, since the SDK requires Node 22+.
cb02595 to
55d39d3
Compare
This comment has been minimized.
This comment has been minimized.
…odemodPackage.legacyPatterns
Code Metrics Report (packages/sdk)
Details | | main (cc2a290) | #893 (91a9703) | +/- |
|--------------------|----------------|----------------|-------|
+ | Coverage | 57.0% | 57.2% | +0.1% |
| Files | 337 | 340 | +3 |
| Lines | 11043 | 11106 | +63 |
+ | Covered | 6299 | 6353 | +54 |
+ | Code to Test Ratio | 1:0.4 | 1:0.4 | +0.0 |
| Code | 67313 | 67817 | +504 |
+ | Test | 27541 | 27858 | +317 |Code coverage of files in pull request scope (33.3% → 71.2%)
SDK Configure Bundle Size
Runtime Performance
Type Performance (instantiations)
Reported by octocov |
10b4d2e to
c132bd5
Compare
|
Recreating with clean commit history |
| const sdkImportRegex = /^(import\s+.*from\s+["']@tailor-platform\/sdk["'];?)$/m; | ||
| const match = sdkImportRegex.exec(result); | ||
| if (match) { | ||
| const insertPos = (match.index ?? 0) + match[0].length; | ||
| result = result.slice(0, insertPos) + "\n" + importLines.join("\n") + result.slice(insertPos); |
There was a problem hiding this comment.
🟡 SDK import regex fails to match multi-line imports, causing plugin imports to be prepended at file top
The sdkImportRegex at line 205 uses ^(import\s+.*from\s+["']@tailor-platform\/sdk["'];?)$ with the /m flag. Since .* does not match newlines, this regex cannot match multi-line SDK imports such as:
import {
defineConfig,
defineGenerators,
} from "@tailor-platform/sdk";
When the SDK import spans multiple lines, the regex returns no match and the fallback at line 212 prepends the new plugin imports at the very top of the file — before all other imports. This produces valid but poorly ordered output (e.g., import { kyselyTypePlugin } ... appearing above import * as path from "node:path").
Example of incorrect output for multi-line import
Input:
import {
defineConfig,
defineGenerators,
} from "@tailor-platform/sdk";
import config from "./tailor.config";Actual output (plugin import prepended at top):
import { kyselyTypePlugin } from "@tailor-platform/sdk/plugin/kysely-type";
import {
defineConfig,
definePlugins,
} from "@tailor-platform/sdk";
import config from "./tailor.config";Expected output (plugin import after SDK import):
import {
defineConfig,
definePlugins,
} from "@tailor-platform/sdk";
import { kyselyTypePlugin } from "@tailor-platform/sdk/plugin/kysely-type";
import config from "./tailor.config";Prompt for agents
In packages/sdk-codemod/codemods/v2/define-generators-to-plugins/scripts/transform.ts, the sdkImportRegex on line 205 only matches single-line SDK imports because .* does not cross newlines even with the /m flag. When the SDK import spans multiple lines (e.g., import { defineConfig, definePlugins } from "@tailor-platform/sdk" spread across lines), the regex fails to match and the fallback prepends plugin imports at the top of the file.
To fix this, use a regex that can match multi-line imports. One approach is to use [\s\S] instead of .* to allow matching across newlines:
const sdkImportRegex = /^(import\s+[\s\S]*?from\s+["']@tailor-platform\/sdk["'];?)$/m;
However, this could be fragile with greedy matching across lines. A more robust approach would be to find the last line containing 'from "@tailor-platform/sdk"' (or the SDK import path) and insert after that line, using a simple string search rather than a single-line regex. For example, find the index of the closing of the SDK import statement by searching for the from clause pattern line-by-line.
Was this helpful? React with 👍 or 👎 to provide feedback.
Summary
tailor-sdk upgrade --from <version>command for automated SDK migration@tailor-platform/sdk-codemodpackage: standalone codemod runner using@ast-grep/napidirectlyreduce()pattern — multiple codemods see each other's output, dry-run is accurate even with sequential transforms on the same filenode_modules, invokesnpx @tailor-platform/sdk-codemodwith--from/--todefineGenerators()→definePlugins()with explicit plugin importsArchitecture
Key design: in-memory transform chaining
Unlike codemod.com's workflow engine (which breaks dry-run for sequential transforms on the same file), this implementation uses
@ast-grep/napidirectly:This ensures dry-run produces correct diffs even when codemod B depends on codemod A's output.
New Package:
@tailor-platform/sdk-codemod