Skip to content

refactor: move the TypeScript packages into languages/typescript - #1000

Merged
auxesis merged 9 commits into
mainfrom
refactor/typescript-to-languages
Oct 2, 2026
Merged

auxesis merged 9 commits into
mainfrom
refactor/typescript-to-languages

Conversation

@auxesis

@auxesis auxesis commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

This PR moves every npm package except EQL from packages/ to languages/typescript/packages/, and moves examples/ to languages/typescript/examples/. It changes folders and the paths that name them, and renames no package. The move leaves only EQL in packages/, so PR B can put the Rust crates there.

This is PR A of 6 in the stack crates import. The import moves six Rust crates, their bindings and the Go module here from the private cipherstash/cipherstash-suite repository, with their git history. The stack crates import plan in Linear lists every step, and Linear issue CIP-4274 tracks the work.

PR A is the first of the six, so its base is main, and each later PR is built on the one before it. All six stay drafts until the freeze on Friday 2 October 2026, Pacific time. The freeze is the window in which they merge in order: #1000, #1001, #1003, #1002, #1009, then #1010. The steps for that day are in §9.1 of the plan.

Spot-check commits 1 and 2, and read commits 3 to 9 line by line

Scripts produced commits 1 and 2, and both are very large. Check their shape rather than reading every line:

  • Commit 1 only moves files. git show --stat -M 71099940 reports 1,017 files changed, with 0 insertions and 0 deletions.
  • Commit 2 rewrites old paths to new ones in 177 files. It then runs Biome, the formatter and linter, which rewraps some lines that grew longer. Open a few files and confirm that each change is a path or a rewrapped line.
  • In pnpm-lock.yaml, commit 2 changes paths only. pnpm also re-sorts the lockfile's list of workspace folders, so the EQL block moves position, but no version changes.

Commits 3 to 9 are written by hand. They fix what the scripts cannot, so they need a full review.

The scripts are in stack-migration/pr-a/ on the migration machine, not in this repository. If main moves before the merge, redo.sh there rebuilds this PR on the newer main. It re-runs the scripts for commits 1 and 2, then replays commits 3 to 9 on top.

Each commit makes one kind of change

  1. 71099940 moves 1,017 files with git mv and changes nothing inside them, so git log --follow keeps each file's history. The repository does not build at this commit, because the paths still point at the old folders.
  2. 61960da4 rewrites every path that names a moved package. It covers workflows, scripts, configs, docs, pnpm-lock.yaml and the globs in biome.json, Biome's config. It also covers repository.directory, the package.json field that tells npm which folder holds a published package.
  3. 5867232b fixes relative paths that climb out of a moved package. Each package now sits two folders deeper, so a path such as ../../skills needs two more ../. Some of these fail without an error. With the old path, the stash and @cipherstash/wizard builds succeed but ship no skills. The commit also fixes protect-ffi's path dependency on eql-bindings.
  4. 6df068d0 updates five repository checks that name a package folder as a plain string, such as 'packages'. A path rewrite cannot see a bare folder name. Two of these checks skip a missing folder silently, so after the move they passed without checking anything.
  5. ea5cf1a9 keeps EQL in the root test and dev scripts. Both now select packages in languages/typescript/packages/ and in packages/, so pnpm test still runs EQL's tests.
  6. 1b18f0fb describes the new layout in AGENTS.md, CONTRIBUTING.md, SECURITY.md and the other current docs. It fixes paths that the rewrite missed, for example a path that ends at a backtick.
  7. bafbfc72 points the end-to-end Dependabot test at the new folder. The test uses a made-up Dependabot entry for /packages/*, which now matches only EQL. Only turbo run test:e2e --filter @cipherstash/e2e runs this test.
  8. 7162d957 redraws the folder diagrams in CONTRIBUTING.md and docs/agents/domain.md. Each diagram draws a folder on its own line, so no path rewrite could match it.
  9. b3ffccd4 adds repository.directory to the @cipherstash/protect-ffi wrapper. It was the one published package without the field, so npm could not link its package page to its source folder.

History files keep the old paths on purpose

docs/plans/, docs/superpowers/, every CHANGELOG.md and every pending .changeset/*.md file keep the old paths. A changeset is a file that describes a change, and the next release copies it into a changelog. These files record what was true when someone wrote them. scripts/lint-no-dead-package-paths.mjs, the check that finds paths to missing folders, exempts them for that reason.

The checks pass locally and in CI

  • pnpm install --frozen-lockfile passes. A frozen install fails if the lockfile does not match the package manifests.
  • pnpm run code:check passes. It runs biome check.
  • pnpm test:scripts passes 937 tests. These test the repository's own scripts and checks.
  • turbo run typecheck passes in 16 of 16 packages.
  • With no credentials set, the end-to-end tests pass 48 and skip 16.
  • The stash and @cipherstash/wizard tarballs each contain all 14 skills.
  • CI: 41 checks pass and 1 is skipped.

A deliberate break makes each changed check fail

Commit 4 changes five repository checks. For each one, a deliberate break of the thing it checks now makes it fail:

  • lint-typecheck-scope: a moved package's tsconfig compiles dist/.
  • lint-no-hardcoded-runners: a bare 'npx' in stack/src.
  • lint-no-dead-package-paths: an old packages/stack path in CONTRIBUTING.md.
  • package-readmes: --eql-version in the stack README.
  • turbo-skills-inputs: the skills input removed from stash#build.

Before commit 4, lint-typecheck-scope and lint-no-hardcoded-runners passed with their breaks in place, because they checked nothing.

Links in published READMEs return 404 until this PR merges

The READMEs that npm publishes now link to tree/main/languages/typescript/... on GitHub. Those links return 404 until this PR merges, because the new folders exist on main only after it.

Merge this PR with a merge commit

Merge it with a merge commit, which keeps all 9 commits, and never with a squash or a rebase. PR B is built on these exact commits. A squash or a rebase would leave PR B based on commits that are not in main.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PaY5xYydZUWhv8Nex9Sw8a

@changeset-bot

changeset-bot Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: b3ffccd

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

auxesis and others added 9 commits October 2, 2026 17:45
Pure rename, no content changes, so git log --follow keeps each file's
history. Every package under packages/ except EQL moves to
languages/typescript/packages/, and examples/ moves to
languages/typescript/examples/. packages/ is left for Rust crates and
EQL, which stays whole at packages/eql.

The tree does not build at this commit; the next commits update the
references to the old paths.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PaY5xYydZUWhv8Nex9Sw8a
Mechanical rewrite of every root-relative reference to a moved package
or to examples/: workflows, the build-ffi-binding action, Dependabot,
scripts, turbo.json, biome.json, vitest.shared.ts, .gitignore, e2e/,
skills/, the agent guides and current docs, each package's own files
(including repository.directory in every published manifest), and the
importer keys and link: paths in pnpm-lock.yaml.

The lock change is path-only. pnpm then puts the importers in its own
order, which moves the packages/eql/packages/eql block; regenerating
the lock changes no version.

Left alone because they record history: docs/plans/,
docs/superpowers/, every CHANGELOG.md and pending .changeset/*.md
files, as scripts/lint-no-dead-package-paths.mjs already requires.

This commit is generated, then formatted with biome format. Relative
paths that climb out of a moved package, bare-string package roots in
the linters, and prose follow in separate commits.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PaY5xYydZUWhv8Nex9Sw8a
Each moved package now sits two levels deeper, so every relative path
that climbs to the repository root gains two `..`. A rewrite of
`packages/` cannot see these, and several fail silently:

- cli and wizard tsup configs copy ../../../../skills. The copy is
  guarded by existsSync, so the old path built tarballs without
  skills/ and no error.
- seven vitest configs import ../../../../vitest.shared, and the cli
  scaffold tsconfig extends ../../../../tsconfig.json.
- protect-ffi's eql-bindings path dependency becomes
  ../../../../../../packages/eql/crates/eql-bindings. The comments in
  workflows, the build-ffi-binding action and scripts that quote it
  follow.
- repository-root helpers built from resolve(x, '../..') or
  join(..., '..', '..') in the cli tests, test-kit/src/install.ts,
  the stack-prisma tests and the cli's bundled-folder dev fallback.
- protect-ffi tests that read ../../.github and ../../turbo.json
  relative to process.cwd(), the package root.
- e2e/tests imports of ../../packages/cli/src/*.js, which name .ts
  files, the dist/ and node_modules/ mappings in e2e/wasm/deno.json,
  and scripts/__tests__ imports of package vitest configs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PaY5xYydZUWhv8Nex9Sw8a
A path rewrite does not touch a package root held as a bare string,
because it has no trailing slash. Two checks skip a missing root
without an error, so after the move they passed while checking
nothing:

- lint-typecheck-scope walked `packages` and `examples`, so it
  checked only EQL. With a migrate tsconfig that compiles dist/, it
  exited 0. It now walks languages/typescript/packages and
  languages/typescript/examples as well, and reports the break.
- lint-no-hardcoded-runners scanned only `packages`. With a bare
  'npx' in stack/src it exited 0. It now scans
  languages/typescript/packages and packages, and reports it.

Three failed loudly and needed the same change:

- lint-no-dead-package-paths took its live set from packages/ alone.
  It now checks each reference against the root it names, so a
  leftover root-level path to a moved package is reported dead. Its
  fixtures name the new root.
- lib/package-readmes.mjs selects README pathspecs from both roots.
  The unshipped EQL subtree root README (packages/eql/README.md) is
  no longer selected, because packages/* is no longer a workspace
  glob; the guards that use it run three fewer cases (912 to 909).
- turbo-skills-inputs reads languages/typescript/packages and matches
  the four-level cpSync('../../../../skills').

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PaY5xYydZUWhv8Nex9Sw8a
The JavaScript root stays at the repository root; only its member
paths move.

- Root test and dev filter both ./languages/typescript/packages/** and
  ./packages/**. The second filter is what runs @cipherstash/eql#test;
  a filter naming only languages/typescript drops it, as the one-level
  ./packages/* filter once did. build stays one level deep on
  ./languages/typescript/packages/*, and consumers pull EQL's build
  through ^build.
- pnpm-workspace.yaml: the comments that explained the old packages/*
  glob in terms of EQL no longer described the file after the rewrite.
- turbo.json: the comment quoting the tsup skills copy names the new
  four-level path.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PaY5xYydZUWhv8Nex9Sw8a
AGENTS.md gains a layout paragraph, and its notes on the root test
filter name both package roots, since a filter naming only
languages/typescript drops @cipherstash/eql#test. CONTRIBUTING.md,
SECURITY.md, e2e/README.md and docs/query-api-walkthrough.md name
examples/ and packages/ in prose the rewrite could not see, because
the path ends at a backtick.

Left alone on purpose: docs/plans/, docs/superpowers/, CHANGELOG.md
files and pending changesets. They record history, and
lint-no-dead-package-paths already exempts them for that reason.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PaY5xYydZUWhv8Nex9Sw8a
supply-chain.e2e.test.ts exercises the `directories` glob branch with a
synthetic `/packages/*` entry. After the move that glob matches only
packages/eql, which has no package.json, so the assertion failed.
Neither `pnpm test` nor `test:scripts` runs this suite; it fails only
under `turbo run test:e2e --filter @cipherstash/e2e`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PaY5xYydZUWhv8Nex9Sw8a
The ASCII trees in CONTRIBUTING.md and docs/agents/domain.md draw
packages/ and examples/ as tree branches, which no path rewrite
matches. They now show languages/typescript/packages/,
languages/typescript/examples/ and EQL at packages/eql/.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PaY5xYydZUWhv8Nex9Sw8a
Every other published manifest, including the six protect-ffi platform
packages, names its folder in repository.directory. The wrapper did
not, so npm and GitHub could not link its package page to its source,
and no test could check every published manifest at once.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PaY5xYydZUWhv8Nex9Sw8a

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

Looks like all structural changes. LGTM

Reviewed with GPT-6-Luna medium

@auxesis
auxesis marked this pull request as ready for review October 2, 2026 17:55
@auxesis
auxesis requested a review from a team as a code owner October 2, 2026 17:55
@auxesis
auxesis merged commit d6afb89 into main Oct 2, 2026
43 checks passed
@auxesis
auxesis deleted the refactor/typescript-to-languages branch October 2, 2026 18:01

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

I reviewed 23e9af3f...b3ffccd4. The code is good. One process item blocks: the PR has no changeset.

The blocker: add changesets

  1. skills/ changes need a stash patch changeset. This PR edits skills/stash-edge/SKILL.md and skills/stash-supply-chain-security/SKILL.md. AGENTS.md says a skills-only change is not internal, because skills/ ships in the stash tarball, so it needs a stash patch changeset. The changeset-bot comment also flags that this PR has no changeset.
  2. The published package pages break when this merges, and stay broken until each package releases again. The npm READMEs and repository.directory values that are published now point at tree/main/packages/<name>. After the merge, those GitHub paths return 404. The PR body describes the opposite case (new links that 404 before the merge), but not this one. A package that does not release often (for example @cipherstash/nextjs or @cipherstash/migrate) can keep a broken source link for months. A patch changeset for each published package whose package.json or README.md this PR changes makes the next release correct all of them together. Those packages are stash, @cipherstash/wizard, @cipherstash/stack, stack-drizzle, stack-supabase, stack-prisma, migrate, nextjs, and protect-ffi with its six platform packages.

I know that a new commit on this PR causes a restack of #1001 to #1010. If you put the changeset at the top of the stack instead, I will accept that. Tell me in this PR and I will approve.

What I checked, and found correct

  • Commit 3 (relative paths). I counted the .. segments for each changed path against its new depth. They are all correct: the eql-bindings path from crates/protect-ffi (6 levels to the root), the bundled-paths.ts dev fallback (8), the test-kit REPO_ROOT (5), the stack-prisma test/v3 root (6), the readme-sync URL (5), the tsup cpSync('../../../../skills'), the vitest.shared imports, and the stack prebuild README copy.
  • Leftover old paths. At the PR head, I found no root-relative or ../-relative reference to a moved package outside docs/plans, docs/superpowers, CHANGELOG.md and .changeset/. The pnpm-lock.yaml link: depths are correct for each importer. .gitignore (the protect-ffi WASM re-include chain), biome.json, dependabot.yml, vitest.shared.ts and every workflow working-directory/hashFiles path follow the move.
  • Commit 4 (lint-no-dead-package-paths). The optional languages/typescript/ prefix is part of the match. Because the regex engine starts each match at the leftmost position, a full new path matches as one reference, and a leftover bare packages/stack is reported dead. That is the correct result.
  • Commit 5. The two --filter flags give a union, so @cipherstash/eql#test stays in root pnpm test. AGENTS.md describes the new filter correctly.
  • vendored-space-parity.test.ts. The '..', '..', 'examples' path still resolves, because examples/ moved with the packages.

@freshtonic

Copy link
Copy Markdown
Contributor

This PR merged at 18:01 UTC, two minutes before I submitted my review. Thus, my "changes requested" cannot stop the merge, and the restack note in it no longer applies.

The changeset item is still open. Please add the changesets in a later PR of the stack or in a follow-up PR before the next release. Without them, two problems continue:

  • The skills/ edits do not get a stash patch changeset, which AGENTS.md requires.
  • The published npm pages keep their tree/main/packages/<name> source links, which now return 404, until each package releases again.

All the other items in my review are confirmations. They need no action.

@auxesis

auxesis commented Oct 2, 2026

Copy link
Copy Markdown
Contributor Author

@freshtonic, thank you for the review. Both changeset items are right, and nothing later in the stack covers them: the later PRs add changesets for @cipherstash/auth only.

The changesets are in a draft PR

#1014 adds two changesets:

  • a patch release of the 15 published packages whose source links this PR broke, so their READMEs and repository.directory name the new folders under languages/typescript/;
  • a stash patch release for the two bundled skills this PR edited.

The next release takes the stack packages to 1.2.1, protect-ffi and its platforms to 0.33.1, @cipherstash/nextjs to 4.4.1 and @cipherstash/migrate to 1.0.1. None of those versions is on npm yet.

It merges after the cutover, not in the stack

The stack crates import is mid-cutover today, Friday 2 October 2026. Its next release, in step 11 of the runbook, publishes @cipherstash/auth only. Adding these changesets to the stack would put protect-ffi into that release too. protect-ffi's last release, from #938, hit a race in release.yml that failed the Release job, and CIP-4276 fixes it.

So the order is:

  1. Finish the cutover, which releases @cipherstash/auth alone.
  2. Merge the CIP-4276 fix to release.yml.
  3. Merge chore(changeset): release the packages whose links #1000 moved #1014. The Version Packages PR that follows republishes the 15 packages with working links.

Until then, the source links on the published npm pages return 404. The packages themselves work as before.

🤖 Generated with Claude Code

auxesis added a commit that referenced this pull request Oct 3, 2026
#1000 moved every TypeScript package to languages/typescript/. The stack
family has since released 1.2.1 from main, so its links and skills are
current. Nine published packages still link to their old folders under
packages/, which return 404:

- @cipherstash/migrate 1.0.0
- @cipherstash/nextjs 4.4.0
- @cipherstash/protect-ffi 0.33.0 and its six platform packages. The
  wrapper has no repository.directory at all.

This changeset releases those nine as patch versions. The stack family
moves to 1.2.2 only through its dependency on @cipherstash/protect-ffi.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PaY5xYydZUWhv8Nex9Sw8a
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.

3 participants