Skip to content

fix(cli): publish under a scope we actually own - #253

Merged
oratis merged 3 commits into
mainfrom
feat/npm-oratis-scope
Aug 9, 2026
Merged

fix(cli): publish under a scope we actually own#253
oratis merged 3 commits into
mainfrom
feat/npm-oratis-scope

Conversation

@oratis

@oratis oratis commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Stacked on #252 — that PR fixes the pre-commit gate, which currently corrupts the repository of anyone who commits. Base retargets to main when it merges.

The problem 0.3.0 did not actually fix

0.3.0 renamed the CLI from deepcode-cli to @deepcode/cli because deepcode-cli on npm belongs to an unrelated Doubao-based project. @deepcode/cli is not ours either.

The leaf name is unpublished — which is exactly what made it look free:

GET https://registry.npmjs.org/@deepcode%2Fcli    → 404
GET https://registry.npmjs.org/-/org/deepcode/package
    → {"@deepcode/tsc":"write","@deepcode/dcignore":"write"}

@deepcode/tsc is "Typescript consumer of Deepcode public API" — Snyk's DeepCode, unrelated to this project. npm rejects a publish into a scope you do not own regardless of whether the individual name is available; a scope is not claimable by publishing into it. pnpm publish would have returned the same 403 the rename was meant to fix.

publish-cli needs: [validate, build-vscode, build-mac], and build-mac is skipped without Apple credentials — so the job has still never run. The second wrong name would have surfaced exactly the way the first did: at the moment someone finally configured enough secrets to reach it.

Change

@oratis/deepcode — the repository owner's personal npm scope, which needs no organisation to exist first. Binary unchanged (deepcode). Nothing was ever published under either old name, so no installed package changes and nobody's npm i breaks.

Every other workspace package is private: true; only apps/cli publishes, so nothing else moves.

Guard

scripts/package-name-consistency.test.ts:

  1. every npm i -g … in a current document or in CLI source names the package apps/cli/package.json publishes — both renames so far moved some install strings and left others behind;
  2. the published name is neither of the two that belong to somebody else;
  3. the binary is still deepcode.

Also

apps/cli/README.md is in the package files list, so it is the npm landing page. It said:

M0 骨架 — 命令入口存在但不能用。实际功能:onboarding + REPL:M2 …

for a CLI that has shipped for several versions. Rewritten to describe what the package does, including the 0.3.0 governance commands.

Historical snapshots (MORNING_REPORT, DEVELOPMENT_PLAN, HANDOFF, BEHAVIOR_PARITY) keep the names that were true when they were written, per the existing convention scripts/check-docs.mjs enforces.

Verification

pnpm typecheck, pnpm lint, pnpm format:check, pnpm docs:check clean; full suite passes through the pre-commit hook. Lockfile unaffected — pnpm keys workspace importers by path, not by name.

Still yours to do

NPM_TOKEN must be an automation token from the oratis account. Nothing else about the release graph changes.

🤖 Generated with Claude Code

@oratis

oratis commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

Review — approve, with one change pushed

The registry evidence is the part that matters and it holds up: the leaf name being 404 is exactly what makes a scope you do not own look available, and npm rejects the publish either way. Checking /-/org/deepcode/package rather than the package URL is the right check, and it is the one the previous rename skipped.

I grepped for stragglers. Everything still naming an old package is either a historical snapshot (MORNING_REPORT, DEVELOPMENT_PLAN, HANDOFF, BEHAVIOR_PARITY, release-artifacts/) or prose explaining why the name changed. Nothing installable points anywhere wrong.

Pushed: the guard scanned by allowlist

scanned was a hardcoded list of nine files, and this PR is itself the counter-example. It had to hand-edit install strings in:

  • packages/core/src/config/claude-compat.test.ts
  • apps/cli/src/parity-commands.test.ts

Neither is on the list. A third rename would have left both behind in precisely the way the first two left strings behind — which is the failure the test exists to prevent, reproduced inside the test. An allowlist has to be extended by whoever adds the next document and stays silent when they forget.

Inverted it: every .md/.ts/.tsx/.mjs/.sh in the repository is scanned except an explicit historical set — the same set scripts/check-docs.mjs already maintains, plus CHANGELOG.md, since entries for shipped releases are history too. Both files above are now covered without anyone having to think of them.

Added a second test pinning the walker itself. A skip rule that accidentally swallowed docs/ or apps/ would otherwise leave the main check passing over nothing at all, which is the same silent-success shape again.

Verified by pointing claude-compat.test.ts back at @deepcode/cli:

× is what every install instruction names
+   "packages/core/src/config/claude-compat.test.ts: npm i -g @deepcode/cli"

Note, not a request

The 0.3.0 CHANGELOG entry still says the CLI "is published as @deepcode/cli" and tells people to install it. That was never true — nothing shipped under it. Keep-a-Changelog says do not rewrite history, and the Unreleased entry corrects the record, so leaving it is defensible; just flagging that the released section now contains an install line that would 404.

@oratis oratis mentioned this pull request Aug 9, 2026
7 tasks
@oratis
oratis changed the base branch from fix/git-env-test-isolation to main August 9, 2026 15:38
oratis and others added 3 commits August 9, 2026 23:54
0.3.0 renamed the CLI away from `deepcode-cli` because that name belongs to
an unrelated project. `@deepcode/cli` was not ours either.

The leaf name is unpublished — which is exactly what made it look free. But
the `@deepcode` scope holds `@deepcode/tsc` and `@deepcode/dcignore`, and npm
rejects a publish into a scope you do not own regardless of whether the
individual name is available. A scope is not claimable by publishing into it,
so `pnpm publish` would have returned the same 403 the rename was meant to fix.
`publish-cli` needs both build jobs to succeed first, so it has still never run
— the second wrong name would have surfaced exactly the way the first did.

The new name is the repository owner's personal scope, which needs no
organisation to exist first. Nothing was ever published under either old name,
so no installed package changes and the binary is still `deepcode`.

Guard added: every `npm i -g …` in a current document or in CLI source must
name the package `apps/cli/package.json` publishes, and the published name may
not be either of the two that belong to somebody else. Both renames so far
moved some install strings and left others behind.

Also rewrites `apps/cli/README.md`, which ships in the package `files` list and
is therefore the npm landing page. It described the CLI as an "M0 骨架,命令
入口存在但不能用" and pointed at milestone numbers for when features would
arrive.

Historical snapshots keep the names that were true when they were written.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The guard listed nine files to check. That list has to be extended by
whoever adds the next document, and it says nothing when they forget —
the same silent-drift shape the check exists to catch.

Not hypothetical: this rename hand-edited install strings in
packages/core/src/config/claude-compat.test.ts and
apps/cli/src/parity-commands.test.ts, and neither was on the list. A
third rename would have left both behind exactly as the first two did.

Now every .md/.ts/.tsx/.mjs/.sh in the repository is scanned except an
explicit set of historical snapshots — the same set scripts/check-docs.mjs
already marks as such, plus CHANGELOG entries for shipped releases. A
second test pins the walker itself, so a skip rule that swallowed docs/
or apps/ cannot leave the check passing over nothing.

Verified by pointing claude-compat.test.ts at the old name: the check
names the file and fails.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The widened guard caught this the moment #251 landed on main: §7.1 of the
delivery report tells readers to run `npm i -g @deepcode/cli`, which
would 403 for the same reason the name it replaced did.

The report is a dated snapshot and §7.1 is a record of what was decided
at the time, so the decision stays on the page and the correction is
appended to it rather than rewritten over it. The file joins the
historical set for the same reason the other snapshots are in it.

That the check found this by itself, on a document nobody would have
thought to add to an allowlist, is the argument for scanning by
exclusion.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@oratis
oratis force-pushed the feat/npm-oratis-scope branch from da7f50b to f0c9832 Compare August 9, 2026 15:55
@oratis

oratis commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

Follow-up after rebase onto main

The widened check immediately earned itself. #251 landed while this was in flight, and docs/V0.3.0_REPORT.md §7.1 tells readers to run:

npm i -g @deepcode/cli

which would 403 for exactly the reason this PR documents. The old nine-file allowlist would not have contained that document — nobody adds a delivery report to a package-name allowlist — so the rename would have shipped alongside a fresh set of install instructions for somebody else's scope. Third time.

Resolved by keeping the record and appending the correction: §7.1 is a dated account of what was decided at the time, so the decision stays and a > 后续更正(#253) note points at what was found afterwards. The file joins the historical set for the same reason the other snapshots are in it.

Also rebased onto main (#252, #254, #257, #258 are in). Full gate green.

@oratis
oratis merged commit c80cf0e into main Aug 9, 2026
5 checks passed
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.

1 participant