Skip to content

Fully Open Source Claude Code#41518

Open
BH3GEI wants to merge 1 commit intoanthropics:mainfrom
BH3GEI:feat/add-source-code
Open

Fully Open Source Claude Code#41518
BH3GEI wants to merge 1 commit intoanthropics:mainfrom
BH3GEI:feat/add-source-code

Conversation

@BH3GEI
Copy link
Copy Markdown

@BH3GEI BH3GEI commented Mar 31, 2026

Summary

  • Extracted 1906 TypeScript source files from the cli.js.map (57MB) shipped in the @anthropic-ai/claude-code npm package
  • Added build configuration (Bun bundler) with bun:bundle shim, MACRO.* defines, and missing module stubs
  • Successfully builds and runs --version / --help

Build & Run

bun install
bun run build.ts
node dist/cli.js --version  # → 2.0.74-src (Claude Code)
node dist/cli.js --help     # → full CLI help output

What's Included

Item Count
Anthropic source files 1906
Third-party deps (in source map) 2850
Feature flags found 90+
Build shims needed 3 (bun:bundle, MACRO.*, missing modules)

What's NOT Included

  • ANALYSIS.md / ANALYSIS_CN.md (analysis documents)
  • README_CN.md (Chinese README)
  • node_modules/ / dist/ (build artifacts)

Extraction Method

const map = JSON.parse(fs.readFileSync('package/cli.js.map', 'utf8'));
for (let i = 0; i < map.sources.length; i++) {
  if (map.sources[i].includes('node_modules')) continue;
  const content = map.sourcesContent?.[i];
  if (!content) continue;
  fs.writeFileSync(outPath, content);
}

The source map with full sourcesContent is shipped in every @anthropic-ai/claude-code npm release.

Co-authored-by: Claude Code claude-code@anthropic.com

1906 source files extracted from the source map (cli.js.map, 57MB)
shipped in the @anthropic-ai/claude-code npm package.

Includes:
- Full src/ directory with original TypeScript source
- build.ts with bun:bundle shim, MACRO.* defines, and stub plugins
- tsconfig.json with path aliases
- src-package.json with 312 dependencies extracted from source map

Build: bun install && bun run build.ts
Run: node dist/cli.js --version

Co-authored-by: Claude Code <claude-code@anthropic.com>
Copilot AI review requested due to automatic review settings March 31, 2026 14:39
@juliogc
Copy link
Copy Markdown

juliogc commented Mar 31, 2026

Duplicated #41447

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR reconstructs and builds a source-based version of the Claude Code CLI from the published source map, adding missing runtime/build shims and implementing key CLI/bridge transport utilities so the resulting dist/cli.js can run and provide --version/--help.

Changes:

  • Added/ported CLI command handlers and utilities (update flow, auth, agents, auto-mode) plus NDJSON-safe serialization.
  • Added transport infrastructure for Remote Control / CCR v2 (SSE/Hybrid transport selection, uploaders, replBridge v2 adapter, bridge helpers).
  • Introduced a Bun-based build pipeline and large dependency manifest to bundle the CLI with macro defines and stubbed modules/assets.

Reviewed changes

Copilot reviewed 60 out of 1911 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/cli/update.ts Adds a comprehensive update flow supporting native installs, npm installs, and package-manager guidance.
src/cli/transports/transportUtils.ts Centralizes transport selection logic (SSE/Hybrid/WS) based on URL + env flags.
src/cli/transports/WorkerStateUploader.ts Adds a coalescing PUT uploader with retry/backoff for worker state updates.
src/cli/transports/SerialBatchEventUploader.ts Adds a serial batched event uploader with retry/backoff and optional drop policy.
src/cli/transports/HybridTransport.ts Implements WS-read/HTTP-write transport with batching, ordering, and close draining.
src/cli/remoteIO.ts Updates SDK remote IO to use transport selection + CCR v2 client initialization.
src/cli/ndjsonSafeStringify.ts Ensures NDJSON lines aren’t split by JS line terminators (U+2028/U+2029).
src/cli/handlers/autoMode.ts Adds auto-mode defaults/config dump and rules critique command.
src/cli/handlers/auth.ts Adds/ports auth login/status/logout flows and token install handling.
src/cli/handlers/agents.ts Adds claude agents handler to list configured agents.
src/cli/exit.ts Introduces centralized helper functions for CLI exit paths.
src/buddy/types.ts Defines buddy/companion types and species list with build-output string avoidance.
src/buddy/sprites.ts Adds companion sprite rendering and animation frames.
src/buddy/prompt.ts Adds companion intro attachment injection gated by build-time feature flag.
src/buddy/companion.ts Adds deterministic companion roll logic and config merge behavior.
src/bridge/workSecret.ts Adds work secret decoding and URL/session ID compatibility helpers; worker registration.
src/bridge/types.ts Defines bridge protocol types and shared constants/messages.
src/bridge/trustedDevice.ts Adds trusted device token enrollment + header injection (feature-gated).
src/bridge/sessionIdCompat.ts Adds cse_/session_ retag helpers with injectable gate to avoid heavy imports.
src/bridge/replBridgeTransport.ts Adds replBridge transport abstraction and v2 adapter (SSE + CCRClient).
src/bridge/replBridgeHandle.ts Adds global handle pointer utilities for replBridge access by other modules.
src/bridge/pollConfigDefaults.ts Extracts bridge poll defaults to avoid GrowthBook dependency chain in some callers.
src/bridge/pollConfig.ts Adds schema-validated GrowthBook-backed poll interval configuration.
src/bridge/jwtUtils.ts Adds JWT decoding and token refresh scheduler for bridge tokens.
src/bridge/inboundMessages.ts Adds inbound message extraction with image block normalization.
src/bridge/inboundAttachments.ts Adds inbound attachment fetching/writing and @path prefix injection.
src/bridge/flushGate.ts Adds a state machine for gating writes during initial history flush.
src/bridge/envLessBridgeConfig.ts Adds GrowthBook-backed config for env-less (v2) REPL bridge behavior.
src/bridge/debugUtils.ts Adds debug helpers for truncation and secret redaction in logs.
src/bridge/createSession.ts Adds Sessions API wrappers: create, fetch, archive, update title (org-scoped).
src/bridge/codeSessionApi.ts Adds CCR v2 code-session API wrappers for session creation and /bridge credentials.
src/bridge/capacityWake.ts Adds shared wake/sleep primitive for at-capacity polling loops.
src/bridge/bridgeStatusUtil.ts Adds bridge status labeling, URL building, and shimmer rendering helpers.
src/bridge/bridgePointer.ts Adds crash-recovery pointer persistence and worktree-aware lookup.
src/bridge/bridgePermissionCallbacks.ts Adds typed permission callback contracts and response predicate.
src/bridge/bridgeMessaging.ts Extracts pure bridge message parsing/routing/dedup/control-request handling.
src/bridge/bridgeEnabled.ts Adds feature-gated bridge entitlement checks + env-less bridge flag + shims.
src/bridge/bridgeDebug.ts Adds ant-only bridge fault injection wrapper for manual recovery testing.
src/bridge/bridgeConfig.ts Centralizes bridge base URL/token resolution with ant-only overrides.
src/assistant/sessionHistory.ts Adds session event pagination helpers with prepared auth context.
src/Task.ts Adds task ID generation and task base typing utilities.
src-package.json Adds dependency manifest for the extracted source build.
build.ts Adds Bun build script with macro defines and module/asset stubbing shims.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

"MACRO.BUILD_TIME": JSON.stringify(new Date().toISOString()),
"MACRO.ISSUES_EXPLAINER": JSON.stringify("https://github.com/anthropics/claude-code/issues"),
"MACRO.FEEDBACK_CHANNEL": JSON.stringify("https://github.com/anthropics/claude-code/issues"),
"MACRO.PACKAGE_URL": JSON.stringify("https://www.npmjs.com/package/@anthropic-ai/claude-code"),
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

MACRO.PACKAGE_URLis being defined as a website URL, butsrc/cli/update.tsuses it innpm view ${MACRO.PACKAGE_URL}@${tag} version, which expects a package spec/name (e.g. ``@anthropic-ai/claude-code``). This will cause update checks/commands to fail; define MACRO.PACKAGE_URL` as the npm package name (or introduce a separate macro for the human-facing npmjs.com URL and keep this one as the package spec).

Suggested change
"MACRO.PACKAGE_URL": JSON.stringify("https://www.npmjs.com/package/@anthropic-ai/claude-code"),
"MACRO.PACKAGE_URL": JSON.stringify("@anthropic-ai/claude-code"),
"MACRO.PACKAGE_NPM_URL": JSON.stringify("https://www.npmjs.com/package/@anthropic-ai/claude-code"),

Copilot uses AI. Check for mistakes.
if (typeof msg !== 'object' || msg === null || !('file_attachments' in msg)) {
return []
}
const parsed = attachmentsArraySchema().safeParse(msg.file_attachments)
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

After the guard, TypeScript narrows msg to object, but object does not permit property access, so msg.file_attachments is a type error under noImplicitAny/strict settings. Assign msg to a Record<string, unknown> (or similar) after the guard and pass that property to Zod.

Suggested change
const parsed = attachmentsArraySchema().safeParse(msg.file_attachments)
const msgRecord = msg as Record<string, unknown>
const parsed = attachmentsArraySchema().safeParse(msgRecord.file_attachments)

Copilot uses AI. Check for mistakes.
Comment on lines +112 to +128
for (const [key, value] of Object.entries(overlay)) {
if (
(key === 'external_metadata' || key === 'internal_metadata') &&
merged[key] &&
typeof merged[key] === 'object' &&
typeof value === 'object' &&
value !== null
) {
// RFC 7396 merge — overlay keys win, nulls preserved for server
merged[key] = {
...(merged[key] as Record<string, unknown>),
...(value as Record<string, unknown>),
}
} else {
merged[key] = value
}
}
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

The typeof x === 'object' checks will treat arrays as objects; spreading arrays into {...} will convert indices to string keys and can silently corrupt metadata payloads if an array ever appears under external_metadata/internal_metadata. Add an !Array.isArray(...) guard (for both merged[key] and value) before applying the RFC7396-style merge.

Copilot uses AI. Check for mistakes.
Comment on lines +91 to +117
// Handle missing .md and .txt files (return empty string)
build.onResolve({ filter: /.*/ }, (args) => {
const missingPatterns = [
'connectorText', 'TungstenTool', 'WorkflowTool', 'REPLTool',
'SuggestBackgroundPRTool', 'VerifyPlanExecutionTool',
'cachedMicrocompact', 'devtools.js', 'protectedNamespace',
'coreTypes.generated', 'snipCompact',
'agents-platform', 'assistant/assistant',
'SnapshotUpdateDialog', 'AssistantSessionChooser',
'sdk/runtimeTypes', 'sdk/toolTypes', 'global.d.ts',
'contextCollapse',
'filePersistence/types.js',
];
// .md and .txt files → return as text
if (/\.(md|txt)$/.test(args.path)) {
return { path: args.path, namespace: "text-stub" };
}
if (missingPatterns.some(p => args.path.includes(p))) {
return { path: "empty-module", namespace: "empty" };
}
return undefined;
});

build.onLoad({ filter: /.*/, namespace: "text-stub" }, () => ({
contents: `export default "";`,
loader: "js",
}));
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

Despite the comment saying "missing .md and .txt files", the resolver stubs all .md/.txt imports to empty strings unconditionally, even when the file exists. If any runtime behavior depends on embedded markdown/text (help text, prompts, templates, license text, etc.), this will silently change behavior. Consider limiting this stub to known-missing assets (or conditionally reading the file contents when present and only stubbing on ENOENT).

Copilot uses AI. Check for mistakes.
Comment on lines +69 to +71
build.onResolve({ filter: /^bun:bundle$/ }, () => ({
path: new URL("./src/shims/bun-bundle.ts", import.meta.url).pathname,
}));
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

Using new URL(...).pathname for a filesystem path is not portable on Windows (it can produce a leading /C:/... and doesn’t handle URL decoding consistently). Prefer converting the file URL to a proper path (e.g., via fileURLToPath) so the build works cross-platform.

Copilot uses AI. Check for mistakes.
"@anthropic-ai/sandbox-runtime": "*",
"@anthropic-ai/sdk": "*",
"@anthropic-ai/vertex-sdk": "*",
"@aws-crypto/crc32": "*",
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

Depending on "*" for a large number of packages makes builds non-reproducible and increases supply-chain risk (any new major can land and break runtime semantics). Pin versions (at least major/minor ranges) and rely on a lockfile for deterministic installs.

Suggested change
"@aws-crypto/crc32": "*",
"@aws-crypto/crc32": "^5.2.0",

Copilot uses AI. Check for mistakes.
Comment on lines +200 to +207
* and on permanent failures (4xx non-429, no token) so the uploader moves on.
*/
private async postOnce(events: StdoutMessage[]): Promise<void> {
const sessionToken = getSessionIngressAuthToken()
if (!sessionToken) {
logForDebugging('HybridTransport: No session token available for POST')
logForDiagnosticsNoPII('warn', 'cli_hybrid_post_no_token')
return
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

When getSessionIngressAuthToken() is missing, postOnce() returns normally, which makes SerialBatchEventUploader treat the send as successful and permanently drops the batch while write()/flush() may still resolve. To avoid silent data loss, treat missing auth as a failure signal (e.g., throw a retryable error or a clearly permanent error that increments a drop counter/telemetry), depending on the intended semantics.

Suggested change
* and on permanent failures (4xx non-429, no token) so the uploader moves on.
*/
private async postOnce(events: StdoutMessage[]): Promise<void> {
const sessionToken = getSessionIngressAuthToken()
if (!sessionToken) {
logForDebugging('HybridTransport: No session token available for POST')
logForDiagnosticsNoPII('warn', 'cli_hybrid_post_no_token')
return
* and on permanent failures (4xx non-429) so the uploader moves on.
*/
private async postOnce(events: StdoutMessage[]): Promise<void> {
const sessionToken = getSessionIngressAuthToken()
if (!sessionToken) {
logForDebugging('HybridTransport: No session token available for POST')
logForDiagnosticsNoPII('warn', 'cli_hybrid_post_no_token')
throw new Error('HybridTransport: No session token available for POST')

Copilot uses AI. Check for mistakes.
Comment on lines +108 to +111
// chain is not broken.
logForDebugging(
`[${label}:token] Could not decode JWT expiry for sessionId=${sessionId}, token prefix=${token.slice(0, 15)}…, keeping existing timer`,
)
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

If schedule() is called first with an opaque/non-JWT token and there is no “existing timer” yet for that sessionId, this returns without scheduling any refresh, potentially breaking the proactive refresh chain entirely. Consider scheduling a fallback refresh (similar to the follow-up timer) when there is no existing timer registered for the session.

Suggested change
// chain is not broken.
logForDebugging(
`[${label}:token] Could not decode JWT expiry for sessionId=${sessionId}, token prefix=${token.slice(0, 15)}…, keeping existing timer`,
)
// chain is not broken. If there is no existing timer yet, schedule a
// conservative fallback refresh so the proactive refresh chain is not
// broken for opaque/non-JWT tokens.
const existing = timers.get(sessionId)
if (existing) {
logForDebugging(
`[${label}:token] Could not decode JWT expiry for sessionId=${sessionId}, token prefix=${token.slice(0, 15)}…, keeping existing timer`,
)
return
}
const gen = nextGeneration(sessionId)
const fallbackDelayMs = refreshBufferMs > 0 ? refreshBufferMs : 60_000
logForDebugging(
`[${label}:token] Could not decode JWT expiry for sessionId=${sessionId}, token prefix=${token.slice(0, 15)}…, no existing timer; scheduling fallback refresh in ${formatDuration(fallbackDelayMs)}`,
)
const fallbackTimer = setTimeout(doRefresh, fallbackDelayMs, sessionId, gen)
timers.set(sessionId, fallbackTimer)

Copilot uses AI. Check for mistakes.
@samuelrizzo
Copy link
Copy Markdown

LGTM

@0xneobyte
Copy link
Copy Markdown

AYO

@AnkanMisra
Copy link
Copy Markdown

Anthropic is never recovering from this 😭

@andjelicgg
Copy link
Copy Markdown

Bold move 😁

@lumen-rot
Copy link
Copy Markdown

looks good lets get this merged @anthropic-xabi

@alexg-sh
Copy link
Copy Markdown

do it Anthropic

@Aero-Ex
Copy link
Copy Markdown

Aero-Ex commented Mar 31, 2026

merge it 😗

@iosifnicolae2
Copy link
Copy Markdown

LGTM!

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.