A browser-first JavaScript port of gptdiff, scoped to the two core APIs — generateDiff and smartapply — wired to NanoGPT for LLM completions (including "Sign in with NanoGPT" OAuth PKCE).
Everything runs in the browser (and in Node 18+): no filesystem, no build step, zero runtime dependencies. The diff engine is a faithful port of the Python implementation; it operates on an in-memory { path: content } file map instead of a directory on disk.
See it in action: live browser demos → (source in gptdiff-js-examples).
The gptdiff family — gptdiff (CLI + Python API) · gptdiff-js (you are here) · gptdiff-js-examples (live browser demos)
import { generateDiff, smartapply, buildEnvironment } from 'gptdiff-js';
const files = { 'greet.py': 'def greet():\n print("hello")\n' };
// 1. Ask the model for a unified diff
const diff = await generateDiff(buildEnvironment(files), 'Say goodbye instead of hello');
// 2. Apply it with AI-powered conflict resolution
const updated = await smartapply(diff, files);
console.log(updated['greet.py']);It's plain ESM — point an import at src/index.js, or open index.html from a static server:
npx serve . # then visit the printed URL and try the demo
npm test # run the unit suite (Node's built-in test runner)
npm run test:live # hit NanoGPT for real (requires env vars, see below)Configuration comes from environment variables (Node) or setEnv(...) overrides (browser). OAuth sign-in sets the API key override for you.
| Variable | Purpose | Default |
|---|---|---|
GPTDIFF_LLM_API_KEY |
NanoGPT API key (sk-nano-…) |
— (required for live calls) |
GPTDIFF_LLM_BASE_URL |
OpenAI-compatible base URL | https://nano-gpt.com/api/v1/ |
GPTDIFF_MODEL |
Model id | xiaomi/mimo-v2.5-pro-ultraspeed |
import { setEnv } from 'gptdiff-js';
setEnv('GPTDIFF_LLM_API_KEY', 'sk-nano-…'); // browser, no process.envsrc/oauth.js implements the NanoGPT OAuth PKCE flow using Web Crypto. In a browser:
import { registerClient, beginSignIn, completeSignIn } from 'gptdiff-js/oauth';
// On page load — finishes the redirect and stores the access token as
// the GPTDIFF_LLM_API_KEY override automatically:
await completeSignIn();
// To start sign-in (clientId from a one-time dynamic registration):
const { client_id } = await registerClient({
clientName: 'My App',
redirectUri: location.origin + location.pathname,
});
await beginSignIn({ clientId: client_id }); // redirects to NanoGPTLow-level helpers are also exported: generatePkce, buildAuthorizeUrl, exchangeCodeForToken, generateCodeChallenge.
Builds the prompt, calls the LLM, and returns the unified diff extracted from the ```diff block(s) of the response.
opts.model,opts.temperature,opts.maxTokens,opts.apiKey,opts.baseUrl,opts.prepend,opts.imagesopts.callLlm— inject a custom/mock completion client (used heavily in tests).
Applies a diff to an in-memory file map with per-file, LLM-assisted conflict resolution (runs files concurrently). Handles creation, modification, and deletion; <think>…</think> and reasoning preambles are stripped automatically. Returns a new map; deleted files are omitted.
opts.model,opts.apiKey,opts.baseUrl,opts.maxTokensopts.callLlmForApply— inject a custom/mock single-file applier.
Deterministic, no-LLM patch application (the strict "basic" applier). changed is true iff something was created, modified, or deleted; files is the updated map (equal to the input on failure — no partial writes).
parseDiffPerFile, buildEnvironment, colorCodeDiff, swallowReasoning, stripBadOutput, extractDiffBlocks, callLlm, resolveApiKey, resolveBaseUrl, getEnv, setEnv, and the oauth namespace.
- No filesystem.
applyDiff/smartapplytake and return a{ path: content }map rather than reading/writing a project directory. - Scope. Only
generateDiff+smartapply(and their dependencies) are ported — not thegptdiff/gptpatch/plangptdiffCLIs. - Dependency injection replaces Python's
monkeypatch: passcallLlm/callLlmForApplyto test without a network. fetch+ Web Crypto replaceopenai/requests/tiktoken.
MIT