Commit Graph
10 Commits
Author SHA1 Message Date
Colby MchenryandClaude Opus 5.5 57ce5b9cdc fix(upgrade): a global npm install is upgraded with -g from any folder, even home (#2777)
codegraph upgrade told a global npm install from a local one by whether
the running file sat under the current folder. npm's global folder is
often inside the home folder (Windows %APPDATA%\npm, nvm, fnm,
~/.npm-global), so run from home every such global install was taken
for a local one: npm ran without -g, the global copy stayed on the old
version and a second copy landed in the home folder. The same test sent
a project's own install to npm -g when upgrade ran from one of its
subfolders or from outside it, and installed into the parent folder
instead when it ran from one.

The scope now comes from npm's layout around the outermost node_modules.
A global launcher beside it (<prefix>\codegraph.cmd on Windows,
<prefix>/bin/codegraph next to <prefix>/lib on POSIX) means global. A
project around it (node_modules/.bin/codegraph or a package.json) means
local, and npm then runs in that project, wherever upgrade was run from;
the version npm left there is read back, with a warning when it did not
change. A pnpm, yarn or bun install (a .pnpm layout, or that manager's
lockfile beside node_modules) is named and given that manager's
command, since npm run in its folder would only add a second copy.

Validation: on Windows, the real CLI was run with a private USERPROFILE,
HOME, APPDATA and LOCALAPPDATA (npm config get prefix = the private
AppData\Roaming\npm) against installs laid out the way npm lays them
out, with a fake npm first on PATH that logs its command and folder.
main ran the wrong npm command in 6 of 9 cases (a global install under
%APPDATA%\npm or ~\.npm-global run from home, and project installs run
from a subfolder, from home, from the drive root or from another
project), the fix ran the right one in 9 of 9; an npx cache run and the
bundled runtime are detected the same by both. On main's code 14 of the
91 upgrade tests fail on assertions (12 new ones, and 2 existing ones
that now also expect the project folder); all pass with the fix.

Fixes #2477

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 03:11:17 +00:00
Colby MchenryandClaude Opus 5.5 0abae11959 fix(installer): a symlinked instructions or config file is written through, not replaced (#2768)
Root cause: every installer write goes through one helper that writes
`<path>.tmp.<pid>` and renames it onto the path. A rename replaces a
symbolic link itself, not the file it points to. So a `~/.codex/AGENTS.md`
or `~/.claude.json` linked from a dotfiles repo became a detached regular
copy on install: the shared file never got CodeGraph's settings, and later
edits to it no longer reached the agent. This held for every target (Claude
Code, Codex, opencode, Cursor, Gemini, Antigravity, Kiro, Hermes and the
Copilot family) and for install, the refresh that `codegraph upgrade` runs,
and uninstall. Uninstall also deleted a link whose file it had emptied,
leaving CodeGraph's entry behind in the shared file.

Fix:
- The write helper resolves a link to the file at the end of its chain and
  writes and renames there, so the link stays exactly as it was, relative
  or absolute.
- A link to a file that doesn't exist yet gets that file created, but only
  in a folder that already exists, as writing through it in a shell would.
  No folder is ever created where a link points.
- A dangling chain is followed hop by hop from the real folder holding each
  link, so a relative link under a linked folder lands where the OS would
  put it.
- When the linked file can't be written (a read-only file, a folder that
  doesn't exist), the error names the link and its target and nothing
  changes.
- Where uninstall used to delete a user's file it had emptied (an
  instructions file, Codex's config.toml, Copilot CLI's mcp-config.json,
  the legacy project .claude.json), a link is kept and the emptied content
  is written through it.
- The refresh sweep catches a failure per agent, reports it, still
  refreshes the others, and exits non-zero, so the upgrade warns.

Decided: links to CodeGraph's own legacy files (Cursor's codegraph.mdc,
Kiro's steering doc), which install only ever deletes, are still removed.

Validation:
- 28 new tests. 25 are in installer-targets.test.ts:
  - every target and location: install and uninstall keep each of its
    files that is a link (18 cases);
  - the shared files' content through install and uninstall for Claude
    Code, Codex, opencode (two agents sharing one AGENTS.md) and Cursor;
  - relative links under a linked folder;
  - a link into a missing folder;
  - a link to a read-only file;
  - uninstall emptying through links;
  - a failing link in a refresh;
  - a folder link guard.
  The other 3:
  - 1 in upgrade.test.ts, where the refresh runs the built CLI;
  - 2 in a new installer-links-simulated.test.ts, which simulates the
    link with spied lstat/realpath/readlink, so it runs on machines that
    can't create file symlinks.
  With main's sources put back, 27 fail; the folder-link guard passes on
  both. All pass with the fix. The real-link tests are skipped, with the
  reason in their names, where file symlinks can't be created (Windows
  without Developer Mode or admin).
- This Windows machine has Developer Mode on, so the real-link tests ran
  for real.
- The real CLI against a private home (`install --yes`, `install
  --refresh`, `uninstall --yes --keep-cli`), 10 linked files for five
  agents plus relative, dangling and read-only links:
  - on main, all 29 link checks found the link replaced by a copy;
  - with the fix, none did, every link's text was unchanged, the shared
    files got CodeGraph's settings and lost them again on uninstall, and
    the missing-folder and read-only cases exited 1 with the new message
    and changed nothing.

Fixes #2721

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 01:06:12 +00:00
Colby MchenryandClaude Opus 5.5 d6e8c91e0a fix(installer): upgrade keeps everything around CodeGraph's section in instructions files (#2765)
Root cause: the write that refreshes CodeGraph's marker-fenced section in
an agent's instructions file (CLAUDE.md, AGENTS.md, GEMINI.md) located the
section with two independent searches: the first start marker anywhere in
the file and the first end marker anywhere in the file. Everything between
them was treated as CodeGraph's and replaced. A stray start marker above
the real section (what is left when part of an older section is deleted by
hand, or the marker quoted in the user's own notes) therefore stretched the
"section" over the user's text, and when that marker was the first line
the file came back as just the block, which is the report. The
`codegraph upgrade` refresh (`codegraph install --refresh`) and plain
`codegraph install` both go through this write, for the Claude Code, Codex,
Gemini and opencode targets. The same pairing made a lone end marker look
like "no section", so every run appended another block, and left a second
block stale. Uninstall (and the npm preuninstall hook) and the Cursor
rules-file cleanup used the same pairing to delete the same span.

Fix: one parser finds the well-formed sections. Each end marker closes the
nearest start marker before it, and a marker counts only when it is alone
on its line, as every block codegraph has written puts it. A stray start
or end marker is left as plain text. The refresh rewrites only the bytes
between the first section's markers, using the line breaks that section
already has, so a CRLF file stays CRLF and a current CRLF block is
unchanged. Further sections are duplicates and are removed, keeping the
text around them. With no section yet the block is appended after the
existing bytes, which are no longer trimmed. Uninstall and the Cursor
cleanup strip only well-formed sections.

Validation:
- 18 new tests (17 in installer-targets.test.ts: exact bytes for a stray
  start marker on line 1, a stray start marker with no section after it, a
  marker quoted in prose, a lone end marker, two sections, CRLF, no final
  newline, appending, a BOM, uninstall; a refresh plus uninstall per
  target for Claude Code, Codex, Gemini and opencode; the Cursor rules
  file. 1 in upgrade.test.ts: an npm upgrade whose refresh runs the built
  CLI against a private home). 16 fail on main's sources (the other 2 are
  guards that hold on both), all pass with the fix.
- The real CLI on this Windows machine against private temp homes:
  `codegraph install --refresh`, 14 file shapes x 4 targets x 2 runs. Main
  had 28 bad results of 112 (the user's text lost in 3 shapes, a block
  added on every run after a lone end marker, a stale second block kept),
  the fix 0. With the fix, the bytes outside the section are identical in
  every single-section shape, and the input stays a byte prefix in every
  append.
- `codegraph install --yes` and `codegraph uninstall --yes --keep-cli` on
  the reported shape: main kept none of the notes (install left only the
  802-byte block, uninstall left 19 bytes); the fix kept them all.

Fixes #2506

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:33:16 +00:00
Colby MchenryandClaude Opus 5.5 273e5cdc4c fix(upgrade): a Windows upgrade no longer breaks the install while CodeGraph is running (#2185) (#2294)
* fix(upgrade): a Windows upgrade no longer breaks the install while CodeGraph is running (#2185)

On Windows, `codegraph upgrade` renamed only node.exe aside and then
Copy-Item'd the new bundle over current\. Any open agent session runs a
CodeGraph MCP server from current\, which keeps the native kernel loaded, so
the copy failed on lib\kernel\codegraph-kernel.node halfway through, leaving
no node.exe and a mix of versions that no codegraph command could repair.
Re-running install.ps1 failed the same way, because it Remove-Items current\.

Both now share one PowerShell function (a test pins the two copies equal).
The bundle is unpacked next to current\ (same volume), then swapped in file
by file: every file being replaced or dropped is first renamed aside (Windows
allows renaming a running exe or a loaded DLL, just not overwriting or
deleting it), then the staged file is moved in. Any failure or interruption
undoes every step. Renamed-aside files, including the node.exe.old-* left by
earlier upgrades, are deleted on the next successful run once nothing holds
them. The upgrade's exit code now tells whether the install was left
unchanged or needs a reinstall, and the message says which.

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

* docs(changelog): Windows upgrade with CodeGraph running (#2185)

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 06:19:41 +00:00
Colby MchenryandClaude Opus 5.5 7bbc1fb7f6 fix(tests): the test suite no longer writes to the developer's real home dir (#2275) (#2295)
Running the suite on a machine with Claude Code configured could wire the
CodeGraph prompt hook into the developer's own ~/.claude/settings.json:
every upgrade test whose fake upgrade succeeded ran the real post-upgrade
prompt-hook self-heal, which detected the real global Claude profile and
wrote it. Daemon suites likewise left records in the real
~/.codegraph/daemons. On Windows a HOME-only redirect would not have
helped, since os.homedir() reads USERPROFILE there.

- The prompt-hook writer is now injected through UpgradeDeps
  (wirePromptHook); the CLI passes the real defaultWirePromptHook and the
  upgrade tests a recorder. New tests pin that a successful upgrade calls
  the recorder and that the real writer never touches a configured profile.
- A vitest setupFiles entry gives every engine test file a throwaway home:
  HOME/USERPROFILE (plus HOMEDRIVE/HOMEPATH/APPDATA/LOCALAPPDATA on
  Windows), XDG_CONFIG_HOME, and GIT_CONFIG_GLOBAL (seeded with a dummy
  identity) point into it, and CLAUDE_CONFIG_DIR/CODEX_HOME/COPILOT_HOME/
  HERMES_HOME are cleared. Spawned CLI/MCP children inherit it. A sandbox
  test asserts os.homedir(), the Claude profile, the daemon registry, a
  child process and git's global config all resolve inside it.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 06:18:46 +00:00
Colby MchenryandClaude Fable 5 a66683d3eb feat(installer): offer CodeGraph Pro beta signup after install and upgrade (#1297)
One-time, strictly opt-in prompt at the end of codegraph install and
codegraph upgrade to join the CodeGraph Pro beta waitlist (same list as
the getcodegraph.com homepage form). Nothing is sent unless the user
answers yes AND enters an email; either answer is recorded machine-wide
so no later install or upgrade re-asks, and --yes / non-interactive / CI
runs never see the prompt.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 19:11:06 -05:00
Colby MchenryandClaude Fable 5 40aa092f5b fix(uninstall): remove the CLI binaries too, not just agent configs (#1254)
* fix(uninstall): remove the CLI binaries too, not just agent configs (#1071)

`codegraph uninstall` swept agent configurations and stopped — every
installed binary stayed behind, so `codegraph` still ran afterward. Three
disconnected paths each removed a fraction of an installation (uninstall:
configs; install.sh --uninstall: the bundle; npm preuninstall: configs +
npm's own package), and none cleared a shadowed second install — the
uninstall edition of the #1071 PATH shadow.

The uninstall now PLANS every install present on the machine — the bundle
layout(s) (running binary's own, the platform default, a custom
CODEGRAPH_INSTALL_DIR), the npm global package (found by asking
`npm root -g`, so nvm/fnm/volta prefixes resolve correctly), and the
bin-dir launcher link (only when it verifiably points into a detected
install) — confirms with the user, then removes them all. `--yes` skips
the prompt; the new `--keep-cli` flag keeps the old configs-only behavior.

Safety rules: a source checkout is reported, never deleted; a
project-local npm install is left to the project; on unix the default
install dir doubles as the machine state dir, so only the install
artifacts (versions/, current) are removed there — telemetry choice and
daemon records survive. Windows can't delete a running exe but can rename
it (the in-place upgrade's trick): a locked node.exe is renamed aside and
surfaced as a one-file leftover instead of failing the removal, and npm
is routed through cmd.exe (a direct .cmd spawn EINVALs on modern Node).

Planner/executor are split with injected side effects (the upgrade
orchestrator's convention) and unit-tested across the shadow case,
state-dir preservation, custom dirs, foreign-shim protection, and the
locked-exe dance; validated end-to-end on macOS against a fake HOME.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(uninstall): key path math on the target platform, not the host

Real-Windows validation caught it: the planner/executor used the host
path module, so win32 fixtures were meaningless on a POSIX host and
POSIX fixtures failed on the Windows VM. Same convention as
detectInstallMethod now — path.win32/path.posix chosen by the injected
platform.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(upgrade): route npm through cmd.exe on Windows — a direct npm.cmd spawn EINVALs on modern Node

Found while validating the uninstall change on the Windows VM: upgradeNpm
spawned npm.cmd without a shell, which every current Node rejects with
EINVAL (the CVE-2024-27980 hardening) — so `codegraph upgrade` on a
Windows npm install failed before doing anything. Verified live on the VM:
spawnSync('npm.cmd') → EINVAL; cmd.exe /d /s /c npm → works.

npmInvocation moves into the upgrade orchestrator (remove-binary imports
it from there — same direction as its existing imports, no cycle), and the
win32 test now pins the WORKING invocation instead of the broken one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:37:20 -05:00
386bff0f84 fix(upgrade): refresh installer-written agent surfaces after a binary upgrade (#1238) (#1239)
* fix(upgrade): refresh installer-written agent surfaces after a binary upgrade

codegraph upgrade swapped the binary but never revisited what earlier
installs wrote into CLAUDE.md / AGENTS.md / GEMINI.md and the agent
configs, so sections written by a pre-1.0 installer kept teaching agents
a multi-tool surface (including tools that no longer exist) months of
releases later. The install path already self-heals everything it owns,
but nothing ever called it on upgrade.

- codegraph install --refresh: non-interactive sweep that re-runs
  install() for already-configured targets only — never a first
  install; permissions and prompt-hook choices are preserved.
- codegraph upgrade spawns it via the freshly-installed binary after a
  successful swap (the still-running old process would only rewrite its
  own stale template). Gated on PATH resolution and the
  CODEGRAPH_NO_INSTALL_REFRESH=1 kill-switch; never fatal to the
  upgrade.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(installer): clarify refresh change reporting

---------

Co-authored-by: xuing <np2v9bvbbs@privaterelay.appleid.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Colby McHenry <me@colbymchenry.com>
2026-07-10 10:58:55 -05:00
Colby MchenryandClaude Fable 5 63eb488ed4 fix(upgrade): stop npm installs self-shadowing on upgrade; verify the resolved version after every upgrade (#1238, #1071) (#1245)
Two fixes to make `codegraph upgrade` trustworthy in the terminal it ran in:

1. detectInstallMethod checked the bundle layout before the node_modules
   path check, but the npm thin-installer's per-platform package IS a
   complete bundle inside node_modules — so every npm install misdetected
   as a standalone bundle, and upgrade curled install.sh into ~/.codegraph:
   a second install that never wins the PATH race against npm's shim,
   leaving `codegraph -v` permanently on the old version. Path-based
   checks (_npx, node_modules) now win over layout sniffing, so npm
   installs upgrade through npm again, in place.

2. After a successful swap, runUpgrade now probes the PATH-resolved
   `codegraph --version` and reports the real outcome: a green
   confirmation that this terminal already serves the new version, a
   loud shadow warning naming the fix (`which -a codegraph`) on
   mismatch, or the old soft new-terminal hint only when the probe is
   inconclusive. Replaces the unconditional "open a new terminal if the
   version looks unchanged" hedge. Skipped for npm-local installs, whose
   binary PATH never serves.

Companion to #1239: the misdetection also broke its post-upgrade
`install --refresh` for npm users (the spawn resolved the stale shim).

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 10:52:22 -05:00
Colby MchenryandClaude Opus 4.8 4e5cf2de56 feat(cli): add codegraph upgrade self-update + stale-index re-index hint (#710)
`codegraph upgrade [version]` detects how the CLI was installed — the standalone
install.sh/install.ps1 bundle, npm-global, npx, or a source checkout — and
updates in place: re-running the canonical install.sh on macOS/Linux, an
in-place rename-and-extract swap on Windows (a running node.exe can't be
deleted, only renamed, so the detached-helper approach is avoided), and
npm/npx/source-specific guidance otherwise. Flags: `--check` (report only),
`--force`, and a positional version to pin.

Each full index is now stamped with the engine's EXTRACTION_VERSION in
project_metadata; `codegraph status` (and `--json`) flags an index built by an
older engine and recommends re-indexing, and `upgrade` prints the same reminder.
Gated on EXTRACTION_VERSION so it never nags on extraction-neutral releases.

Validated end-to-end on macOS (real bundle upgrade), Linux (Docker, real
curl|sh) and Windows (Parallels VM, real in-place swap). 32 new unit tests.

Closes #679

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 13:38:38 -04:00