Skip to content

feat(gamut-agent-tools): add the gamut-scope-swap skill - #45

Merged
dreamwasp merged 42 commits into
mainfrom
cass-gmt-1790-scope-swap-skill
Oct 28, 2026
Merged

dreamwasp merged 42 commits into
mainfrom
cass-gmt-1790-scope-swap-skill

Conversation

@dreamwasp

@dreamwasp dreamwasp commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Overview

Last PR in the stack. It adds the gamut-scope-swap agent skill and the fixtures that run every migration together.

Note

This branch is a merge of the Video/Markdown, deep imports, ESLint, and Next.js PRs, because the skill and the combined fixtures need all of them. Its base is the scope-rename PR, so until those four merge, this diff includes their changes. Review only the last commit (feat(gamut-agent-tools): add the gamut-scope-swap skill), and rebase onto main once the others land.

Stack (split from #28, merge in order):

  1. feat(gamut-codemods): add the codemods CLI and an empty scope-swap preset #38 Scaffold, based on main
  2. feat(gamut-codemods): rename packages and replace gamut-kit in scope-swap #39 Package scope rename and gamut-kit removal, based on feat(gamut-codemods): add the codemods CLI and an empty scope-swap preset #38
  3. feat(gamut-codemods): move Video imports and add Markdown video overrides #40 Video and Markdown, based on feat(gamut-codemods): rename packages and replace gamut-kit in scope-swap #39
  4. feat(gamut)!: promote deep imports to the root, renaming Menu and SelectDropdown elements #41 Deep imports and Menu/SelectDropdown renames, based on feat(gamut-codemods): rename packages and replace gamut-kit in scope-swap #39
  5. feat(gamut-codemods): rename the ESLint plugin prefix in scope-swap #42 ESLint plugin prefix, based on feat(gamut-codemods): rename packages and replace gamut-kit in scope-swap #39
  6. feat(gamut-codemods): warn about esmExternals: false in next.config #43 Next.js esmExternals warning, based on feat(gamut-codemods): rename packages and replace gamut-kit in scope-swap #39
  7. build(deps): make @skillsoft packages easier to install #44 Simpler dependencies (independent), based on main
  8. feat(gamut-agent-tools): add the gamut-scope-swap skill #45 gamut-scope-swap skill (open last), based on feat(gamut-codemods): rename packages and replace gamut-kit in scope-swap #39, includes feat(gamut-codemods): move Video imports and add Markdown video overrides #40–feat(gamut-codemods): warn about esmExternals: false in next.config #43 👈 this PR
  • gamut-scope-swap skill in @skillsoft/gamut-agent-tools. It works alongside the codemod: it reads the warnings, leftovers, and next steps, and helps with what the codemod can't rewrite. That covers deep imports, Video, the renamed Menu exports, tsconfig dom, jest mocks, ESLint plugin keys, Module Federation config, and preview builds. It doesn't run the codemod unless asked.
  • BREAKING_CHANGES.md is the short list of every change in the move, with the manual ones marked.
  • NEXT_EMOTION_INTEROP.md is the fallback for Next.js apps that can't drop esmExternals: false yet. The codemods README now links to it.
  • READMEs: the gamut-agent-tools README and the codemods README point to the skill.
  • Fixtures: restores the end-to-end and MDX fixtures that cover the rename, Video, deep imports, ESLint, and gamut-kit in one file.
  • Merge fix: fixes the cli.test.ts merge between the sibling branches.

PR Checklist

  • Related to designs: N/A
  • Related to JIRA ticket: GMT-1790
  • Changeset added
  • I have run this code to verify it works
  • This PR includes unit tests for the code change
  • This PR includes testing instructions for the code change
  • Package previews from this PR are passing end-to-end tests in all relevant consuming repositories

Testing Instructions

  1. yarn nx test gamut-codemods. Expect 100 tests to pass, including the combined end-to-end and MDX fixtures.

  2. Run the whole preset on a consumer repo (or a copy of one), then install the skill from the app repo:

    node <repo>/packages/gamut-codemods/dist/bin.js scope-swap .
    gamut plugin install claude   # or: gamut plugin install cursor
  3. Ask the agent to work through the output. It should use the warnings and leftovers, refer to BREAKING_CHANGES.md for anything it can't rewrite, and not rerun the codemod unless you ask it to.

  4. Read BREAKING_CHANGES.md against the PRs in the stack. Every breaking change should be listed.

PR Links and Envs

Repository PR Link
Mono N/A

🤖 Generated with Claude Code

dreamwasp and others added 10 commits October 20, 2026 10:07
…eset

Adds @skillsoft/gamut-codemods: the CLI (clean-tree guard, --dry, --only,
--no-report, --force), the runner for ast, splice, and file migrations,
the shared helpers (module-string finder, leftovers report, version-range
checks, flat ESLint config detection), the preset registry, and the
fixture harness.

The scope-swap preset has its package map but no migrations yet, so a run
only reports the lines that still name an old package. Each breaking
change lands its migration in its own PR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…swap

scope-rename swaps @Codecademy/gamut* and eslint-plugin-gamut for their
@SkillSoft names in every module position and in comments. package-json,
mdx-imports, and yarnrc cover dependencies, MDX ESM blocks, and
.yarnrc.yml lists.

@codecademy/gamut-kit has no replacement, so package-json and mf-shared
expand it into the packages it bundled. In Module Federation config that
makes Gamut a real singleton, which the checklist calls out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ides

moved-exports moves Video and VideoProps from the root to
@Codecademy/gamut/Video (scope-rename then swaps the scope).
markdown-video-overrides adds explicit iframeOverride/videoOverride to
each <Markdown>, since it no longer renders video through Video by
default, and drops the removed skipDefaultOverrides keys. tsconfig-dom
warns about tsconfigs without "dom", which Video used to supply.

On the Gamut side, a Video/package.json stub makes the subpath resolve
under node10, MarkdownVideo no longer passes NaN sizes, and a one-line
<video> is no longer wrapped in a <p>.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ectDropdown elements

Exports formFieldStyles, formFieldPaddingStyles, conditionalStyles,
ButtonBaseProps, HTMLToReactNode, MenuToolTipWrapper, and Menu's list
elements from the root. The list elements and SelectDropdown's option
component are renamed (MenuList*, MenuListButton, IconOptionComponent) so
they don't collide with the public List component and IconOption type.

The deep-imports codemod rewrites known /dist/ paths to the root, keeps
local names across renames, merges into existing imports, and warns on
paths with no public replacement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
eslint-comments rewrites gamut/ directives to @skillsoft/gamut/, and
eslint-config updates the plugin name, plugin: extends, and rule keys in
legacy .eslintrc files. Under a flat config the prefix is the plugins key,
so eslint-comments skips itself and the checklist explains why.

Also types eslint-plugin-gamut's recommended severities as literals, so the
plugin fits ESLint's Plugin type in a typed flat config.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@skillsoft/gamut ships strict-ESM .mjs. With experimental.esmExternals
off, Next require()s server externals and default imports from
@emotion/styled and react-select break while it collects page data.
next-config is report-only: dropping the flag can take other config with
it, so it warns and adds a checklist item instead of rewriting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ap-skill

# Conflicts:
#	packages/gamut-codemods/README.md
#	packages/gamut-codemods/src/presets/scope-swap/index.ts
# Conflicts:
#	packages/gamut-codemods/README.md
#	packages/gamut-codemods/src/__tests__/lib.test.ts
#	packages/gamut-codemods/src/presets/scope-swap/index.ts
…p-skill

# Conflicts:
#	packages/gamut-codemods/README.md
#	packages/gamut-codemods/src/__tests__/cli.test.ts
#	packages/gamut-codemods/src/presets/scope-swap/index.ts
The skill works alongside the scope-swap codemod: it reads the warnings,
leftovers, and next steps, and helps with what the codemod can't rewrite.
BREAKING_CHANGES.md lists every change in the move, and
NEXT_EMOTION_INTEROP.md covers the Next.js esmExternals fallback.

Also restores the end-to-end and MDX fixtures that run every migration
together, and fixes the cli.test merge between the sibling branches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dreamwasp and others added 13 commits October 20, 2026 11:10
CodeQL read `migration.match(file)` in cli.ts as String.prototype.match
on a command-line argument and flagged js/regex-injection. Nothing builds
a regex from the path; the method just had the same name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…migrations

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…migrations

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…migrations

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…migrations

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dreamwasp and others added 17 commits October 20, 2026 11:11
The package-json warning already names each range that's newer than the
fork point, so the extra next step only repeated it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rkdown

# Conflicts:
#	packages/gamut-codemods/src/presets/scope-swap/index.ts
# Conflicts:
#	packages/gamut-codemods/src/presets/scope-swap/index.ts
# Conflicts:
#	packages/gamut-codemods/src/presets/scope-swap/index.ts
…swap-skill

# Conflicts:
#	packages/gamut-codemods/src/presets/scope-swap/index.ts
# Conflicts:
#	packages/gamut-codemods/src/presets/scope-swap/index.ts
…p-skill

# Conflicts:
#	packages/gamut-codemods/src/presets/scope-swap/index.ts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dreamwasp
dreamwasp marked this pull request as ready for review October 26, 2026 14:35
@dreamwasp
dreamwasp requested a review from a team as a code owner October 26, 2026 14:35

@LinKCoding LinKCoding 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.

Ran against this:

REPO=/Users/kenny.lin/Documents/eng/codecademy-eng/skillsoft-gamut
rm -rf /tmp/swap-app && mkdir -p /tmp/swap-app/src && cd /tmp/swap-app && git init -q

cat > package.json <<'EOF'
{
  "name": "swap-app",
  "version": "1.0.0",
  "dependencies": {
    "@codecademy/gamut": "^1.0.0",
    "@codecademy/gamut-styles": "^1.0.0",
    "@codecademy/gamut-icons": "^1.0.0",
    "@codecademy/gamut-kit": "^1.0.0",
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  }
}
EOF
cat > tsconfig.json <<'EOF'
{ "compilerOptions": { "lib": ["es2020"], "jsx": "react" } }
EOF
cat > next.config.js <<'EOF'
module.exports = { experimental: { esmExternals: false } };
EOF
echo '{ "plugins": ["gamut"], "extends": ["plugin:gamut/recommended"] }' > .eslintrc.json
cat > src/App.tsx <<'EOF'
import { Box, Video } from '@codecademy/gamut';
import { theme } from '@codecademy/gamut-styles';
import { ArrowIcon } from '@codecademy/gamut-icons';

// eslint-disable-next-line gamut/no-inline-style
export const App = () => <Box style={{}}><Video /><ArrowIcon /></Box>;
EOF
git add -A && git commit -qm init

node $REPO/packages/gamut-codemods/dist/bin.js scope-swap .
git diff --stat

node $REPO/packages/gamut/bin/gamut.mjs plugin install claude
node $REPO/packages/gamut/bin/gamut.mjs plugin list

And then prompted the agent to use the skill and it worked through the issues and had gave me:

What the codemod changed
- src/App.tsx: the imports now use @skillsoft/*, and Video now comes from @skillsoft/gamut/Video. The eslint-disable directive was renamed to @skillsoft/gamut/no-inline-style.
- .eslintrc.json: the plugin and the extends entry now use @skillsoft/gamut. They match the directive prefix, so the directive will still work.
- package.json: gamut-kit was replaced with the individual @skillsoft packages.

What I fixed
- next.config.js: I removed experimental.esmExternals: false. It was the only setting in the file, so the config is now {}. The failure only shows up when Next collects page data, so you need a full next build to confirm. There were no @emotion aliases or transpilePackages entries to remove.
- tsconfig.json: I added "dom" to lib. It's harmless to keep. If type-checking passes without it, you can take it back out.
- package.json, Video peers: I added @vidstack/react@~1.12.12 and react-player@^3.4.0. Gamut supports only 1.12.x of @vidstack/react, so don't use the npm latest tag.
- package.json, @skillsoft/gamut-tests: I moved it from dependencies to devDependencies, since the codemod put it in the same field gamut-kit was in.

What's left for you
1. Run yarn. If it warns about missing @emotion/cache or stylis peers for @skillsoft/gamut-styles, add them.
2. Run prettier --write and eslint --fix, then type-check, test and build.
3. The ranges are ^0.0.1, which can resolve to a release older than the codemod expects. If @skillsoft/gamut/Video or MenuList isn't found after install, check node_modules/@skillsoft/gamut/package.json and its exports.
4. The repo has no sync-gamut-deps script, so the gamut-kit cleanup needs nothing.
5. src/App.tsx imports theme but never uses it. It was already like that before the swap, but lint will flag it.

I haven't committed anything. Say if you want me to run yarn and the checks.

Base automatically changed from cass-gmt-1790-scope-rename to main October 28, 2026 12:47
Take main's codemods work, which already includes #38-#43. This branch's
own addition is the gamut-scope-swap skill and its changesets.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 28, 2026

Copy link
Copy Markdown

⚠️ JUnit XML file not found

The CLI was unable to find any JUnit XML files to upload.
For more help, visit our troubleshooting guide.

@pkg-pr-new

pkg-pr-new Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@skillsoft/eslint-plugin-gamut

npm i https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/eslint-plugin-gamut@45
yarn add https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/eslint-plugin-gamut@45.tgz

@skillsoft/gamut

npm i https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut@45
yarn add https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut@45.tgz

@skillsoft/gamut-agent-tools

npm i https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-agent-tools@45
yarn add https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-agent-tools@45.tgz

@skillsoft/gamut-codemods

npm i https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-codemods@45
yarn add https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-codemods@45.tgz

@skillsoft/gamut-icons

npm i https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-icons@45
yarn add https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-icons@45.tgz

@skillsoft/gamut-illustrations

npm i https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-illustrations@45
yarn add https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-illustrations@45.tgz

@skillsoft/gamut-patterns

npm i https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-patterns@45
yarn add https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-patterns@45.tgz

@skillsoft/gamut-styles

npm i https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-styles@45
yarn add https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-styles@45.tgz

@skillsoft/gamut-tests

npm i https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-tests@45
yarn add https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-tests@45.tgz

@skillsoft/variance

npm i https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/variance@45
yarn add https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/variance@45.tgz

commit: 8b47b2d

Drop the stale empty-preset changeset and the per-PR codemods and
Video changesets. Their details now live in gamut-codemods-scope-rename,
and the duplicate Video bullets in gamut-video-markdown are already in
move-video-out-of-barrel.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
@dreamwasp
dreamwasp merged commit 320a368 into main Oct 28, 2026
11 checks passed
@dreamwasp
dreamwasp deleted the cass-gmt-1790-scope-swap-skill branch October 28, 2026 17:33
dreamwasp added a commit that referenced this pull request Oct 28, 2026
#45 landed as a squash with later edits, so the color-roles preset is
reapplied on main's codemod framework.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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