mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-30 01:51:20 +00:00
1107 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f4749cf1e4 |
fix(devcontainer): resync AI CLI state from host on every container-create
Two bugs were causing Claude Code to fire the onboarding wizard inside the container even with valid host credentials: 1. Missing the second state file. Claude Code 2.1.x writes a small `.claude.json` INSIDE `CLAUDE_CONFIG_DIR` (carrying migration tracking + userID), not just the one at `$HOME/.claude.json`. If the userIDs in the two files disagree, Claude treats the session as inconsistent and re-onboards. The previous post-create.sh only copied the `$HOME` one. 2. First-run guards (`[ ! -e $dst ]`) skipped the copy when stale named volumes from earlier rebuilds still had the prior session's state in them, leaving the container desynced from the host. Replace `copy_on_first_run` with `sync_from_host` that always overwrites from host on container-create. `link_readonly_share` now clears stale non-symlink dst entries before linking. Copies both `$HOME/.claude.json` and `$CLAUDE_CONFIG_DIR/.claude.json` so userIDs stay aligned. Container can still mutate its own state between rebuilds; resync only happens on rebuild (postCreate boundary). |
||
|
|
abbcacebca |
refactor(devcontainer): hybrid AI CLI config — read-only host share + per-container credentials
Restructure the Claude Code / Codex / Cursor mount topology to fix the silent first-run-UI bug surfaced in PR testing, and to harden against the host-write-through escape class the previous bind-mount design exposed. The actual root cause of the first-run wizard firing on the user's screenshot — confirmed via three parallel research agents (best practices, framework docs deep dive of the OpenAI Codex Rust source, adversarial design review) — was NOT a credential permission check. Claude Code splits state across `~/.claude/.credentials.json` AND `~/.claude.json` (a FILE at $HOME, sibling of the `.claude/` dir). The latter holds `hasCompletedOnboarding`, `userID`, `oauthAccount` metadata, MCP user-scope config, and per-project trust state — and Claude Code reads it at literal `$HOME/.claude.json`, not via `CLAUDE_CONFIG_DIR`. The previous design mounted `~/.claude/` but left `~/.claude.json` outside the topology entirely, so every container started with a missing onboarding-state file and re-ran the wizard. Confirmed by tfvchow/field-notes-public#10: "Persisting .credentials.json alone is NOT sufficient. Without .claude.json, Claude Code treats the session as a fresh install and prompts for login regardless of valid credentials being present." The new topology: **Mounts** - `${localEnv:HOME}/.claude` → `/host/.claude` (read-only bind) - `${localEnv:HOME}/.codex` → `/host/.codex` (read-only bind) - `${localEnv:HOME}/.cursor` → `/host/.cursor` (read-only bind) - `${localEnv:HOME}/.claude.json` → `/host/.claude.json` (read-only bind) - `claude-config-${devcontainerId}` → `/home/node/.claude` (named volume) - `codex-config-${devcontainerId}` → `/home/node/.codex` (named volume) - `cursor-config-${devcontainerId}` → `/home/node/.cursor` (named volume) **containerEnv** gains `CODEX_HOME=/home/node/.codex` (Codex's own env override, per its public Rust source). `CLAUDE_CONFIG_DIR=/home/node/ .claude` was already set. **`post-create.sh`** stages the named volumes on first run: - Symlinks shareable subdirs from `/host/.claude` into the named volume: `plugins/`, `skills/`, `agents/`, `memory/`, `commands/`. Codex gets `config.toml` symlinked. Cursor has no shareable subdirs (cli-config .json conflates auth and settings). - Copies `.credentials.json`, `auth.json`, `cli-config.json` on first run with `chmod 600`. After first run, container manages its own refresh; host's credentials untouched. - Copies `~/.claude.json` on first run (with stub `{"hasCompletedOnboarding":true,"installMethod":"global"}` fallback for hosts that haven't run Claude Code). This is the fix for the observed onboarding-wizard loop. `ensure-host-config-dirs.cjs` now also touches `~/.claude.json` on the host if missing, so the bind mount has a valid source on hosts that have never run Claude Code. **Why read-only + named volume vs. the previous full bidirectional bind mount:** 1. **Host filesystem write-through escape, eliminated.** Previous design symlinked `plugins/`, `agents/`, `skills/` write-through into the host's `~/.claude/` — a malicious npm package in the workspace dep tree could drop `agents/evil.md` into the host's config, which the next host Claude session would auto-load. The read-only `/host` mount blocks this; container compromise no longer persists across teardown via host-side autoload. 2. **Windows bind-mount perm-flattening, sidestepped.** Files surfaced through a Docker Desktop Windows bind mount appear as `root:root` mode `777`. Credentials in the named volume come with proper Linux ownership and `chmod 600` — what each CLI expects on write (none enforces on read, but write-side hygiene matters for the host's understanding of "where credentials live"). 3. **No `ide/` lock-file collisions.** Previous design symlinked `~/.claude/ide/` write-through, including per-PID lock files. Host PID and container PID namespaces are unrelated → lock-file PIDs misclassify dead processes as alive. Skipping `ide/` keeps lock files container-local. 4. **No `projects/` ghost dirs.** Host encodes the workspace path as `D--development-coding-GitNexus`, container as `-workspace`. Bidirectional `projects/` symlinks would split memory and session state across two ghost project dirs for what is conceptually the same project. Skipping `projects/` keeps per-project state container-local; host's projects/ stays untouched. 5. **No `settings.json` version drift.** Container is pinned to a specific Claude Code version (`CLAUDE_CODE_VERSION` build arg); host floats with auto-update. Bidirectional `settings.json` writes produced silent schema rollback. Skipping settings.json keeps each side authoritative for its own version. **README** rewritten in the same section to describe the new topology honestly: what's shared, what isn't, the OAuth refresh-token divergence between host and container, per-CLI quirks (macOS Keychain storage, Cursor's known upstream in-container auth bug, Codex keyring storage). Trust-boundary section updated to name the threat model accurately — same read surface as before (malicious dep can still READ all credentials), but write-through into host plugin/agent dirs is now blocked. Verified locally: `@devcontainers/cli read-configuration` resolves all 19 mounts correctly on Windows, `post-create.sh` parses, and `ensure-host-config-dirs.cjs` idempotently touches `~/.claude.json`. Research backing this design: - Anthropic Claude Code devcontainer docs (named-volume pattern): https://code.claude.com/docs/en/devcontainer - tfvchow/field-notes-public#10 (both files required): https://github.com/tfvchow/field-notes-public/issues/10 - anthropics/claude-code#29029 (VS Code extension strips hasCompletedOnboarding): https://github.com/anthropics/claude-code/issues/29029 - OpenAI Codex Rust source (no read-side perm check): https://github.com/openai/codex/blob/main/codex-rs/login/src/auth/storage.rs - Cursor CLI in-Docker auth issue: https://forum.cursor.com/t/cursor-agent-authentication-issue-inside-docker/143995 |
||
|
|
6a569e401e |
feat(devcontainer): bind-mount ~/.docker, ~/.aws, ~/.azure for agent workflows
Extend the host bind-mount surface so coding agents inside the container inherit cloud + container-registry auth from the host without any per-container setup: - ~/.docker (read-write) — Docker registry auth (config.json) + buildx config. Container-registry pushes (ghcr.io, docker.io) from inside the container pick up host `docker login` state. Read-write because the Docker CLI refreshes credential-helper tokens. - ~/.aws (read-only) — AWS CLI / SDK credentials. Read-only because rotating creds typically happens via the host. Empty on this dev box, so forward-compatible: the moment you `aws configure` on the host the container picks it up on the next rebuild. - ~/.azure (read-only) — Azure CLI credentials. Same pattern as ~/.aws. `ensure-host-config-dirs.cjs` extends to mkdir these three on init so the bind mounts always have a valid source even if a CLI has never been used on this host. The Docker CLI itself isn't installed in the container by default — the ~/.docker/ mount is inert until you add `docker-outside-of-docker:1` or similar Feature. README now calls this out under "What you still don't have inside the container" so it's obvious which CLIs are agent-ready and which need a feature add to become useful. README updates: - Bind-mount table gains a "Why" column and rows for the three new mounts, making it clear at a glance what each one enables. - Trust-boundary section lists Docker registry tokens, AWS, and Azure creds in the read-side exfil path so the threat model stays honest as the credential surface grows. - New subsection lists not-included CLIs (Docker, AWS, Azure, gcloud, kubectl, private-npm) with the exact Feature ID or mount snippet needed to enable each — turns "I want my agent to do X" into a one-line config change. Verified locally: `npx @devcontainers/cli read-configuration` resolves all 9 host bind mounts to valid C:\Users\<name>/* paths on Windows. |
||
|
|
213390a4b5 |
fix(devcontainer): drop ~/.gitconfig bind mount; defer to VS Code auto-copy
VS Code's Dev Containers extension auto-copies the host's gitconfig into the container at attach time using `(dd ...) >> /home/node/.gitconfig`. A read-only bind mount of ~/.gitconfig blocks that write, so attach failed with `cannot create /home/node/.gitconfig: Read-only file system`. Making it read-write would let the append succeed, but the bind mount means the host file and the container file are the same file — VS Code's append would double the host gitconfig contents on every container start. Drop the ~/.gitconfig bind mount entirely. VS Code's auto-copy is the purpose-built mechanism for this, gives the container the host's user.name / user.email transparently, and avoids both the read-only write failure and the append-duplication trap. The container ends up with a writable /home/node/.gitconfig that's a copy of the host's, not a mount. The remaining six bind mounts (.claude, .codex, .cursor, .ssh, .config/git, .config/gh) keep their existing modes — XDG-style git config under ~/.config/git is unaffected by VS Code's auto-copy (which only targets ~/.gitconfig), so its read-only bind mount stays. Also remove the `.gitconfig` touch from ensure-host-config-dirs.cjs (now unnecessary) and update the README CLI-state table, sharing explanation, and troubleshooting row to reflect that gitconfig flows in via VS Code auto-copy rather than the bind mount. |
||
|
|
a29a1cfb9f |
feat(devcontainer): support Windows-native via auto setx HOME on first run
Reverses the "WSL2 required on Windows" posture. Windows-native now
works after a one-time auto-handled setup.
The root cause of the bind-mount failure: VS Code resolves
`${localEnv:HOME}` by reading its own process env, and Windows doesn't
set `HOME` by default — Windows uses `USERPROFILE`. So the bind sources
were collapsing to `/.claude`, `/.codex`, etc., and Docker rejected them.
`ensure-host-config-dirs.cjs` now handles this automatically on Windows
hosts where `HOME` is unset:
1. Runs `setx HOME "%USERPROFILE%"`, which writes to the user-level
Windows environment (HKCU\Environment) — no admin required. Every
future user process inherits HOME from there.
2. Prints a clear one-time setup banner explaining the user needs to
fully restart VS Code (File > Exit, not just close the window) for
VS Code to pick up the new env at its next startup.
3. Exits 1 so VS Code surfaces this as a clean container-create failure
instead of letting Docker error opaquely later.
On the second Reopen-in-Container attempt, `HOME` is now set in VS
Code's env, the script skips the setup block, creates the bind-mount
source dirs, and the container builds normally. Subsequent rebuilds
have no extra steps.
Mac, Linux, and WSL2 hosts have `HOME` set by the shell, so the new
block is a no-op there. Same `devcontainer.json` works across all
supported hosts.
README rewritten to reflect the new posture:
- Header lists Windows 11 (native) as a supported host alongside macOS,
Linux, and WSL2, with a note that Windows-native gets a one-time
HOME setup handled by the initializeCommand.
- New "Windows 11 setup" section walks through the auto-handled setup
flow + a manual `setx HOME "%USERPROFILE%"` fallback for users who
want to do it themselves.
- "Known trade-offs of Windows-native vs WSL2" subsection lays out the
Docker Desktop Windows bind-mount edge cases (file watchers, npm
install perf, husky/_ EPERM) so users opting into Windows-native do
so eyes-open. WSL2 remains documented as the faster path for users
who want it, but it's no longer the only supported one.
- Troubleshooting table gets two new rows: the one-time setup banner
(with "what to do" instructions) and the residual `bind source path
does not exist` case (run setx manually + fully exit VS Code).
|
||
|
|
f4dc20289a |
fix(devcontainer): fail-fast on Windows-native with HOME-not-set diagnostic
The previous commit's "Windows-native works" softening was wrong. VS Code
on Windows-native resolves `${localEnv:HOME}` by reading the host shell's
HOME env var, and cmd.exe has no HOME set — the bind sources collapse to
`/.claude`, `/.codex`, etc., and Docker errors:
Error response from daemon: invalid mount config for type "bind":
bind source path does not exist: /.claude
The @devcontainers/cli output that prompted the softening was misleading
because I ran it from a Bash session with HOME already set, not from VS
Code's cmd.exe call context. The original Finding-1 P0 — that Windows-
native silently breaks the bind-mount feature — was correct.
Three changes:
1. `ensure-host-config-dirs.cjs` detects the failure mode early:
`if (process.platform === 'win32' && !process.env.HOME)` prints a
targeted error message naming the root cause (cmd.exe has no HOME →
${localEnv:HOME} resolves empty → bind sources fail) and a step-by-step
pointer to set up WSL2. Exits 1 so VS Code surfaces it as a clean
container-creation failure, not the cryptic Docker bind-mount error.
2. README header reverted to "Windows 11 via WSL2" only (not "and
Windows-native"). The "Windows 11 — WSL2 is required" section names
the specific HOME-resolution mismatch concretely so future readers
understand why the constraint exists.
3. Troubleshooting table gets a new row for the `ERROR: GitNexus
devcontainer requires WSL2` message pointing at the setup section.
|
||
|
|
f592c804ed |
fix(devcontainer): Node-based initializeCommand; bind-mount .ssh + .config/git
Two fixes bundled:
1. The previous commit's OS-keyed `initializeCommand` object was based
on a misread of the Dev Containers spec. The object form on command
properties is **named parallel tasks**, not OS dispatch — VS Code ran
all three keys in parallel via cmd.exe on Windows, the POSIX branches
failed, and container creation aborted before Docker was invoked.
Restore the single-string Node-based form:
`node .devcontainer/ensure-host-config-dirs.cjs`. Node works
identically in cmd.exe on Windows and bash/zsh on Linux/macOS/WSL,
and `os.homedir()` respects $HOME on POSIX and %USERPROFILE% on
Windows. The script is idempotent (mkdirSync recursive is a no-op
for existing dirs; touch is gated on .gitconfig existence).
Document Node ≥18 on the host as the only host-side prerequisite
beyond Docker Desktop and the VS Code Dev Containers extension.
Anyone running Claude Code on the host already has it.
2. Extend the host-bind mount surface with `~/.ssh` and `~/.config/git`,
both read-only:
- `~/.ssh` lets commit signing + push over SSH remotes work inside
the container without copying private keys. Read-only mount means
container code can read keys but can't modify or delete them.
(Threat: a malicious dep can still read private keys from inside
the container; the read-only mount narrows write-side blast
radius, not read-side. Documented in the trust-boundary section.)
- `~/.config/git` covers XDG-style git config (`~/.config/git/config`,
`~/.config/git/ignore`, `~/.config/git/attributes`) for users who
keep settings there instead of `~/.gitconfig`. Read-only, same as
`~/.gitconfig`.
Update the CLI-state-sharing table and trust-boundary paragraph to
reflect the expanded surface.
Re-adds .devcontainer/ensure-host-config-dirs.cjs (deleted before the
OS-keyed attempt).
|
||
|
|
32cb7dc28a |
fix(devcontainer): cross-platform initializeCommand + soften Windows-native posture
The previous commit's `initializeCommand` was POSIX-only (`mkdir -p $HOME/...`).
VS Code on Windows runs the host shell as `cmd.exe /c ...`, which can't
parse POSIX syntax — `$HOME` doesn't expand, `mkdir -p` errors, the init
fails with `The syntax of the command is incorrect`, and container
creation aborts before Docker is invoked.
Switch `initializeCommand` to the spec's OS-keyed object form:
- linux/darwin (covers WSL2 because VS Code runs initializeCommand in
the WSL shell when attached via the WSL extension): POSIX mkdir+touch,
as before
- win32: PowerShell snippet that creates the same directories under
$USERPROFILE and touches the gitconfig if missing
Soften the README's hard "WSL2 required" framing from the previous
commit. Reality per `@devcontainers/cli read-configuration` output:
`${localEnv:HOME}` on Windows-native resolves to `C:\Users\<name>`
(VS Code falls back to USERPROFILE), so the bind mount sources are
valid Windows paths and Docker Desktop handles the translation. The
earlier `accessing specified distro mount service` failure was a
separate Docker Desktop WSL-integration issue, not a HOME-resolution
issue. Windows-native works; it's just slower with more bind-mount
permission edge cases (the husky/_/h EPERM class). The README now
explains the tradeoff and steers toward WSL2 for performance + file
watchers + permission reliability, rather than blocking Windows-native
checkouts outright.
Update the troubleshooting row to reflect the new posture.
|
||
|
|
cd8ac5f6d1 |
refactor(devcontainer): address ce-code-review findings (P0 + 4 × P1 + 8 × P2 + 2 × P3)
Walkthrough resolution of the 16-finding ce-code-review on PR #1875. 15 of 16 findings applied; one (F12, Anthropic Feature floating tag) was superseded by F6's Feature removal. P0 - F1: WSL2 is now REQUIRED for Windows hosts, not just recommended. ${localEnv:HOME} resolves to empty string on Windows-native (no HOME env var) — bind mounts then point at /.claude, /.codex etc. and silently break. ensure-host-config-dirs.cjs wrote to USERPROFILE-derived paths via os.homedir(), so the two surfaces disagreed about which env var was "home" on Windows. README header reframed; "Windows 11 — WSL2 is required" section explains the mismatch concretely. P1 - F2: Workspace `node_modules` volume names now include `-${devcontainerId}` so two GitNexus checkouts on the same host (~/work/GitNexus and ~/projects/GitNexus) don't share volumes and corrupt each other's installs. - F3 + F5: `postCreateCommand` extracted to `.devcontainer/post-create.sh` with `set -euo pipefail` and six labeled echo steps so failure logs name the step instead of an opaque &&-chain index. Chown step extended to cover /home/node/.npm, /commandhistory, and /home/node/.local — these named-volume mount points were owned by build-time UID 1000 but the container's `node` is re-IDed at runtime by updateRemoteUserUID on non-1000 Linux hosts, leaving them unwritable until now. - F4: Cursor installer downloaded to a temp file with curl --retry + --max-time; sha256 logged to build output before execution so drift across rebuilds is visible in CI logs. Full hard-pin (to a versioned downloads.cursor.com tarball with verified sha256) tracked as a follow-up in README "What's not included". P2 - F6: Anthropic Feature replaced with a direct `npm install -g @anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}` so CLAUDE_CODE_VERSION actually pins the installed binary (the Feature ignored the ARG and pulled latest at install time). Honors the earlier "pin known-good versions" decision and resolves F12's floating-tag concern for this Feature. - F7: Dockerfile ARG defaults dropped for the three version vars; `devcontainer.json` `build.args` is now the single source of truth. Standalone `docker build .devcontainer/` must pass --build-arg. - F8: ensure-host-config-dirs.cjs deleted; `initializeCommand` now uses POSIX `mkdir -p` + `touch ~/.gitconfig` directly, dropping the host-Node-on-PATH prerequisite that broke on fresh Windows+Docker Desktop installs without Node. - F9: ~/.gitconfig bind-mounted read-only so `git commit` inside the container uses the host's user.name / user.email. Read-only so container-side `git config --global` doesn't leak to host. - F10: ~/.config/gh bind-mounted (read-write) so `gh pr create` / `gh pr checks` / `gh issue create` work inside the container without re-auth. AGENTS.md's commit + PR workflow now fully functional for agents inside the container. - F11: CLAUDE_CONFIG_DIR removed from Dockerfile ENV; canonical value lives only in devcontainer.json containerEnv. Eliminates the two-file edit risk. - F13: Mounts comment now documents per-instance vs per-workspace-name scoping rationale so future contributors don't guess. - F14: README "Trust boundary, concretely" paragraph names the exfil path explicitly (malicious npm postinstall → OAuth tokens → ~/.claude/projects/<workspace>/memory/MEMORY.md secrets) and lists vendor-side rotation runbook entries. P3 - F15: Dockerfile pre-create + chown of /home/node/.claude, .codex, .cursor dropped — those paths are bind-mounted, which fully shadows any image-side ownership. Only .npm, .local, /commandhistory still benefit from the pre-create. - F16: README "Bumping CLI versions" section rewritten against the post-F6 reality: CLAUDE_CODE_VERSION and CODEX_VERSION are real pins; CURSOR_VERSION is informational only. Verified locally: `docker build .devcontainer/ --build-arg ...` succeeds. Smoke-tested image: `claude --version` (2.1.153), `codex --version` (0.134.0), `cursor-agent --version` all resolve as the non-root `node` user; named-volume mount points (/home/node/.npm, /commandhistory) are node-owned at build time so non-1000 host UIDs get the post-create.sh chown fix instead of EACCES. |
||
|
|
35e61a9e66 |
feat(devcontainer): bind-mount host CLI config dirs for plugin/skill/memory sync
Switch the credential/config mounts from per-devcontainer named volumes
to bind mounts of `${localEnv:HOME}/.claude`, `~/.codex`, and
`~/.cursor`. Effect inside the container:
- Authentication is shared with the host. If you've already run
`claude login` / `codex login --device-auth` / `cursor-agent login`
on the host, you're already authenticated in the container.
- Plugins, skills, agents, memory, and settings sync both ways. Install
a plugin in the container, it shows up on the host; add a custom
agent on the host, the container sees it immediately.
- All devcontainers on the host share the same CLI state, mirroring
how host shells already share it. (Per-workspace isolation of plugins
was never a stated requirement; the previous per-devcontainer named
volumes leaked nothing useful.)
Add `.devcontainer/ensure-host-config-dirs.cjs` and wire it as
`initializeCommand`. It runs on the host before container create and
guarantees `~/.claude`, `~/.codex`, `~/.cursor` exist, so Docker doesn't
reject the bind mount when a CLI has never been used on this host.
Cross-platform via Node `os.homedir()` + `fs.mkdirSync({recursive: true})`;
idempotent; no third-party deps.
Update `.devcontainer/README.md`:
- New "How CLI state is shared with your host" section explaining the
bind-mount model up front so users know their host plugins/skills/
memory carry into the container.
- Mark first-time-login section as skippable when the user is already
authenticated on the host.
- Note the high-trust escape hatch: replace the three bind mounts with
`type=volume` named volumes if the host/container trust boundary
needs to be separated (Anthropic's reference pattern for enterprise).
- Replace the obsolete "rm named volume" troubleshooting row with one
that covers EACCES/EPERM on the host-bind-mount path.
|
||
|
|
a75fc2e060 |
fix(devcontainer): clear stale .husky/_ before npm install
When `npm install` runs the root `prepare` script (husky), husky tries
to copyfile `node_modules/husky/husky` → `.husky/_/h`. On Docker Desktop
Windows bind mounts, if `.husky/_/` already exists from a prior
container run, the new container's `node` user can't overwrite it via
the bind mount's permission translation and the install fails with:
Error: EPERM: operation not permitted, copyfile
'/workspace/node_modules/husky/husky' -> '.husky/_/h'
Drop `.husky/_` defensively in `postCreateCommand` before `npm install`
so husky always starts from a clean slate. `.husky/_` is a husky
runtime cache (gitignored), so removing it has no effect on the repo —
husky regenerates it. No-op for WSL2-side checkouts (where this class
of bind-mount permission collision doesn't occur).
Add a troubleshooting row to `.devcontainer/README.md` covering the
manual recovery (`rm -rf .husky/_` on the host) and the long-term fix
(clone in WSL2 — Windows-side bind mounts will keep biting on this
kind of issue across rebuilds with different UID alignment).
|
||
|
|
326cb06fe7 |
fix(devcontainer): install gitnexus-web before gitnexus in postCreateCommand
The previous order (root → gitnexus-shared → gitnexus → gitnexus-web) broke at the `gitnexus` install step because `gitnexus`'s `prepare` script runs `scripts/build.js`, which compiles `gitnexus-web` whenever its source tree exists. In the devcontainer the entire workspace is bind-mounted, so `gitnexus-web/` is present from the start — but its `node_modules/` wasn't yet, so `tsc -b` failed with: error TS2688: Cannot find type definition file for 'vite/client' error TS2688: Cannot find type definition file for 'node' Reorder so `gitnexus-web` installs before `gitnexus`. Verified end-to-end via `npx @devcontainers/cli up`: container builds clean, all three CLIs (Claude 2.1.153, Codex 0.134.0, Cursor) respond, and `npx tsc --noEmit` inside `/workspace/gitnexus` passes. Production Dockerfiles (`Dockerfile.cli` etc.) don't hit this because they only COPY `gitnexus/` + `gitnexus-shared/`, so `gitnexus-web/` doesn't exist at install time and `scripts/build.js` skips the web step. The devcontainer's full-tree bind mount changes that calculus. |
||
|
|
05e9c58fb1 |
fix(devcontainer): make interactive login the default auth path for all CLIs
The previous `containerEnv` injected `CURSOR_API_KEY: "${localEnv:CURSOR_API_KEY}"`.
When the host had no `CURSOR_API_KEY` set, this resolved to an empty
string and Docker injected `CURSOR_API_KEY=""` into the container.
Cursor CLI treats a set-but-empty `CURSOR_API_KEY` as "use this key"
rather than "fall back to stored login", which silently broke
`cursor-agent login` on the most common path — users who hadn't
explicitly opted into API key auth.
Drop `CURSOR_API_KEY` from `containerEnv`. Login is now the
unconditional default for all three CLIs (Claude Code, Codex CLI,
Cursor CLI); the named-volume + Dockerfile-chown pattern keeps
credentials persistent across container rebuilds for every login path.
Reorganize the README's auth section to put login first for all three
CLIs uniformly (matching the new behavior) and move API key
authentication into a separate "Alternative" section for CI/headless
use. Document that API keys are intentionally not auto-propagated from
the host and explain the export-in-shell or VS Code dotfiles-repo paths
for users who want them. Update the troubleshooting row to reflect the
new design.
|
||
|
|
9c07ef9cf9 |
feat(devcontainer): add cross-platform devcontainer for Claude Code, Codex, and Cursor CLIs
Add a Dev Container that pre-installs Claude Code (2.1.153, via Anthropic's
official Feature), OpenAI Codex CLI (pinned 0.134.0), and Cursor CLI alongside
the GitNexus native build chain. Opens via VS Code's Dev Containers extension
on Windows 11 (Docker Desktop + WSL2), macOS, or Linux without OS-specific
branches in devcontainer.json.
Topology and base
- Base image `mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm`
(multi-arch, monthly patched, ships the `node` non-root user, zsh, `gh`).
- Node 22 LTS satisfies `gitnexus/`'s engines `>=22.0.0` and matches the
`node:22-bookworm-slim` SHA-pinned base used by `Dockerfile.cli`.
- Single container with all three CLIs co-installed (vs. docker-compose
per-tool) — prevailing 2026 community pattern, lowest daily-driver friction.
Persistence and auth
- Per-devcontainer named volumes scoped by `${devcontainerId}` for
`/home/node/.claude`, `/home/node/.codex`, `/home/node/.cursor`,
`/commandhistory`, and `/home/node/.npm`. Authentication survives rebuilds
without leaking between workspaces.
- Four sub-workspace `node_modules` volumes (root, gitnexus, gitnexus-web,
gitnexus-shared) keep tree-sitter native bindings and onnxruntime off the
bind mount — the actual Win/Mac perf win.
- Credential mount paths are pre-created in the Dockerfile with
`chown node:node` BEFORE `USER node`, so empty named volumes inherit
correct ownership on first mount and first-run logins don't EACCES.
- `CURSOR_API_KEY` is injected via `containerEnv: ${localEnv:CURSOR_API_KEY}`
(Cursor's documented headless path); falls back to interactive
`cursor-agent login` when the host env var is unset.
Build-arg promotion
- Build args (`CLAUDE_CODE_VERSION`, `CODEX_VERSION`, `CURSOR_VERSION`, `TZ`)
are promoted to ENV in the Dockerfile so lifecycle commands and shells can
resolve them. Without this promotion, Docker ARG values are build-only and
silently no-op at lifecycle time.
Workspace setup
- `postCreateCommand` chowns the four workspace `node_modules` volumes
(Docker creates them root-owned), then installs in dependency order:
root → gitnexus-shared (install + build) → gitnexus → gitnexus-web. The
shared package must build before its consumers (`file:../gitnexus-shared`).
Ports
- 5173 (Vite dev) and 4173 (Vite preview) auto-forwarded.
- 4747 (`gitnexus serve`) marked `requireLocalPort: true` because
`gitnexus-web/src/services/backend-client.ts` hardcodes
`http://localhost:4747` as the default backend URL; a remapped port would
silently break the web UI.
VS Code integration
- Recommended extensions: `anthropic.claude-code`,
`dbaeumer.vscode-eslint`, `esbenp.prettier-vscode`, `eamodio.gitlens`.
- Settings: format-on-save with Prettier, ESLint auto-fix on save, zsh as
default terminal profile, persistent zsh history via `HISTFILE` →
`/commandhistory`.
Documentation
- `.devcontainer/README.md` covers WSL2 setup (clone inside WSL2 for IO and
file-watcher reliability), first-time auth flows for each CLI, port-
forwarding notes, LadybugDB container limitations, and the bumping
procedure for each CLI version.
- `CONTRIBUTING.md` gets a "Containerized development (optional)"
subsection pointing at the devcontainer README.
Deferred to a follow-up PR
- Opt-in egress firewall (originally planned as a fourth implementation
unit). The Dev Containers spec makes `runArgs` static — toggling
`NET_ADMIN`/`NET_RAW` capabilities cleanly requires either a separate
`devcontainer-firewall.json` profile or an `initializeCommand`-generated
overlay. Keeping this PR focused on the working baseline.
- Codespaces-specific tuning (works incidentally when the firewall is off,
not actively tested).
- Inside-container Playwright e2e (needs Chromium libs not in the base
image).
Verification deferred to user
- This change introduces a new dev tooling artifact. Validate by running
`docker build .devcontainer/`, opening the repo in VS Code via
"Dev Containers: Reopen in Container", confirming `claude --version`,
`codex --version`, `cursor-agent --version` resolve inside the container,
and `cd gitnexus && npm run test:unit` runs clean against the
named-volume `node_modules`.
|
||
|
|
ccf9a4d564 |
chore: extend .gitattributes for shell scripts and binary assets
Append explicit `*.sh text eol=lf` and `*.bash text eol=lf` rules so shell scripts (notably anything COPYed into a Linux container) check out with LF endings on Windows hosts with `core.autocrlf=true`, regardless of the auto-detection on the existing `* text=auto eol=lf` line. Add binary markers for `*.node`, `*.wasm`, `*.onnx`, `*.so`, `*.dll`, `*.dylib` so native and ML model artifacts aren't ever subjected to text normalization. The existing `* text=auto eol=lf` and `.husky/* text eol=lf` rules are preserved. `git ls-files --eol` confirmed zero CRLF or mixed blobs in the index, so no `--renormalize` was needed. |
||
|
|
50715e3894
|
chore(deps)(deps-dev): bump @playwright/test in /gitnexus-web (#1860)
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.58.2 to 1.60.0. - [Release notes](https://github.com/microsoft/playwright/releases) - [Commits](https://github.com/microsoft/playwright/compare/v1.58.2...v1.60.0) --- updated-dependencies: - dependency-name: "@playwright/test" dependency-version: 1.60.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Abhigyan Patwari <126312502+abhigyanpatwari@users.noreply.github.com> |
||
|
|
ca95df6316
|
chore(deps): bump github/codeql-action from 4.35.4 to 4.35.5 (#1866)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.35.4 to 4.35.5.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](
|
||
|
|
9d609cc386
|
chore(deps)(deps): bump axios from 1.16.0 to 1.16.1 in /gitnexus-web (#1864)
Bumps [axios](https://github.com/axios/axios) from 1.16.0 to 1.16.1. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.16.0...v1.16.1) --- updated-dependencies: - dependency-name: axios dependency-version: 1.16.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
128a199970
|
chore(deps)(deps-dev): bump @types/node in /gitnexus-web (#1863)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 25.6.0 to 25.9.1. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 25.9.1 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
76409783aa
|
chore(deps)(deps): bump @langchain/langgraph in /gitnexus-web (#1861)
Bumps [@langchain/langgraph](https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core) from 1.2.9 to 1.3.2. - [Release notes](https://github.com/langchain-ai/langgraphjs/releases) - [Changelog](https://github.com/langchain-ai/langgraphjs/blob/main/libs/langgraph-core/CHANGELOG.md) - [Commits](https://github.com/langchain-ai/langgraphjs/commits/@langchain/langgraph@1.3.2/libs/langgraph-core) --- updated-dependencies: - dependency-name: "@langchain/langgraph" dependency-version: 1.3.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
99168be773
|
feat(ingestion): trace indirect call patterns — FastAPI Depends() and frontend HTTP consumers (#1852) | ||
|
|
d9d6318b64
|
feat(group): add Kotlin Spring HTTP consumer extraction (#1855)
* feat(group): add Kotlin Spring HTTP consumer extraction Follow-up to #1849 (Kotlin providers). Extends `http-patterns/kotlin.ts` with three call-site patterns common in Kotlin Spring projects: - RestTemplate: `restTemplate.getForObject("/x", ...)` and the full verb family (getForObject/getForEntity → GET, postForObject/postForEntity → POST, put → PUT, delete → DELETE, patchForObject → PATCH). Mirrors the Java plugin's `REST_TEMPLATE_TO_HTTP` map so polyglot repos coalesce on a single contract id. - WebClient short form: `webClient.get().uri("/x")` and the `.post()` / `.put()` / `.delete()` / `.patch()` siblings. The chain parses as two nested `call_expression` nodes; the query anchors on the outer `.uri(...)` and walks one level inward to constrain the verb. - OkHttp: `Request.Builder().url("/x")`. Kotlin parses `Request.Builder()` as a `call_expression` whose callee is a `navigation_expression` (not Java's `object_creation_expression`), so the query shape differs from `java.ts` but the receiver/method constraints (`Request` / `Builder` / `url`) and emitted contract format match. Out of scope: `webClient.method(HttpMethod.X).uri("/y")` long form. The verb sits on a sibling `call_expression` two hops away, so it needs a walk-up helper rather than a flat tree-sitter query. A dedicated anti-overreach test pins the current behavior so a future short-form change can't accidentally start matching the long form. Receiver name constraints (`#eq? @obj "restTemplate"`, `#eq? @cls "Request"`) match the Java plugin's heuristic — a project that aliases the receiver under a different name won't be picked up. This trade-off keeps false-positive rates low and is documented in the file header. Tests: 5 new cases under `consumer extraction — fetch patterns`, gated by tree-sitter-kotlin grammar availability. positive (3) - RestTemplate verbs (5 calls × 5 verbs) - WebClient short-form verbs (5 calls × 5 verbs) - OkHttp Request.Builder().url("/x") anti-regression (2) - WebClient long form `.method(HttpMethod.X)` produces no consumer (deferred-feature pin) - non-restTemplate receiver does not match (receiver-name pin) Reverse-validated: removing the `(#eq? @obj "restTemplate")` constraint causes the receiver-name anti-regression test to fail. Local validation: - test/unit/group/http-route-extractor.test.ts: 59/59 ✅ - test/unit/group: 539/539 ✅ - npm run format:check: clean ✅ * test(group): pin Kotlin OkHttp POST-chain heuristic-default GET behavior Address Claude review on PR #1855 (Finding 1). The OkHttp query in `kotlin.ts:OK_HTTP_PATTERNS` matches the `.url("/x")` sub-expression of a builder chain, but the verb is encoded on a separate sibling call (`.post(body)` / `.delete()` / ...). The query intentionally does not walk the chain to recover the verb — it emits `method: 'GET'` for every match, mirroring the Java plugin's `OK_HTTP_PATTERNS` (java.ts). Concretely: `Request.Builder().url("/x").post(body).build()` becomes `http::GET::/x`, not `http::POST::/x`. This is an already-accepted Java parity heuristic, but it was untested on the Kotlin side. This commit: - Adds an anti-overreach test pinning the current behavior: * exactly one consumer is emitted with method=GET * no second http::POST::/x consumer appears - Documents the limitation in kotlin.ts as a "Known limitation" block tied to the test, so a future verb-walk implementation has to update the comment in lockstep with the assertion. Rationale for not implementing verb-walk in this PR: - Verb-walk requires walking sibling call_expression nodes (the `.post(body)` chain), which is the same shape as the deferred WebClient long-form work - Java has the same limitation in production today; fixing only Kotlin would create polyglot drift - A coordinated future PR can add verb-walk to both plugins at once and update both comments + the pin tests together Finding 2 (silent test-skip when tree-sitter-kotlin grammar is unavailable) is intentionally NOT addressed here — same gating pattern was accepted in #1849 for Provider tests, and a coordinated follow-up should add a CI sentinel covering both Provider and Consumer suites in one place. Local validation: - test/unit/group/http-route-extractor.test.ts: 60/60 ✅ - test/unit/group: 540/540 ✅ - npm run format:check: clean ✅ --------- Co-authored-by: henry <zhangwei2017@unipus.cn> |
||
|
|
46eb0ebf56
|
feat(group): add Kotlin Spring HTTP route extraction (named + positional) (#1849)
* feat(group): add Kotlin Spring HTTP route extraction (named + positional)
Mirror the Java Spring named-argument fix for Kotlin Spring Boot
controllers. Adds a new `http-patterns/kotlin.ts` plugin behind the
optional `tree-sitter-kotlin` grammar, registered for `.kt`/`.kts`.
Both annotation forms produce providers:
@RequestMapping("/api") / @GetMapping("/users")
@RequestMapping(path = "/api") / @GetMapping(value = "/users")
@RequestMapping(value = "/api") / @GetMapping(path = "/users")
The Kotlin AST (fwcd/tree-sitter-kotlin) shares one node type
(`value_argument`) for positional and named forms, so the queries
are split:
- positional: anchors `string_literal` as the first named child
of `value_argument` via the immediate-child anchor `.`
- named: explicitly captures `simple_identifier` and constrains
it to `^(path|value)$` via `#match?`, mirroring the same
safety bar enforced by `http-patterns/java.ts` and
`topic-patterns/java.ts`. Without this constraint the query
would also capture non-route attributes like `produces`,
`consumes`, `headers`, `name`, `params`.
`tree-sitter-kotlin` is an optionalDependency (parser-loader.ts,
parse-worker.ts pattern). When the native binding is unavailable
the plugin exports `null` and `index.ts` skips registering
`.kt`/`.kts` so the orchestrator stays healthy.
Scope: providers only. Consumer detection (RestTemplate, WebClient,
OkHttp) on Kotlin call-site ASTs differs enough from Java's
`method_invocation` shape to warrant a separate, focused PR.
Tests: 11 new cases under `provider extraction — source-scan
fallback (Strategy B)`, gated by the kotlin grammar availability.
positive (8)
- class @RequestMapping("/api/v1") (positional)
- class @RequestMapping(path = "/api/v2")
- class @RequestMapping(value = "/orders")
- method @GetMapping(value = "/users")
- method @GetMapping(path = "/users")
- method @PostMapping(path = "/users")
- mixed: class named-arg + method positional
- mixed: class positional + method named-arg
anti-regression (3)
- @GetMapping(produces = "application/json") emits no provider
- @GetMapping(name = "x", value = "/users") emits exactly one provider
- @RequestMapping(path = "/api", name = "myApi") prefix stays /api
Reverse-validated: removing the `(#match? @key "^(path|value)$")`
constraint causes precisely the 3 anti-regression tests to fail.
Local validation:
- test/unit/group/http-route-extractor.test.ts: 54/54
- test/unit/group: 534/534
- npx tsc --noEmit: clean (modulo the pre-existing TS2339 in
user-defined-conversions.ts merged from main, unrelated)
* style(test): apply prettier line wrapping to long itKotlin titles
---------
Co-authored-by: henry <zhangwei2017@unipus.cn>
|
||
|
|
eeea46466b
|
fix(group): handle named annotation args in Java Spring route extraction (#1834)
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
* fix(group): handle named annotation args in Java Spring route extraction
The Java HTTP plugin only matched positional `@RequestMapping("/path")`
syntax for class-level prefixes and method-level routes. Named argument
forms (`path = "/path"` and `value = "/path"`) produce an
`element_value_pair` AST node that the tree-sitter queries did not cover,
causing the class prefix to be lost and named-arg method routes to be
missed entirely during cross-repo contract extraction.
Add a second pattern to both SPRING_CLASS_PREFIX_PATTERNS and
SPRING_METHOD_ROUTE_PATTERNS matching the element_value_pair structure.
* fix(group): constrain Spring named-arg query to path/value keys + add regression tests
Address Claude review on PR #1834. The named-argument patterns added
in
|
||
|
|
ca3e1755c2
|
chore(deps)(deps): bump lru-cache from 11.4.0 to 11.5.0 in /gitnexus (#1844)
Bumps [lru-cache](https://github.com/isaacs/node-lru-cache) from 11.4.0 to 11.5.0. - [Changelog](https://github.com/isaacs/node-lru-cache/blob/main/CHANGELOG.md) - [Commits](https://github.com/isaacs/node-lru-cache/compare/v11.4.0...v11.5.0) --- updated-dependencies: - dependency-name: lru-cache dependency-version: 11.5.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
6acdc49f06
|
chore(deps)(deps-dev): bump @types/node in /gitnexus (#1845)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 25.9.0 to 25.9.1. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 25.9.1 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
b1445daf04
|
feat(cpp): rank user-defined conversions (#1829) | ||
|
|
d903152eba
|
fix(typescript): reuse suffix index in scope resolver (#1840)
* fix(typescript): reuse suffix index in scope resolver Build a suffix index once per TypeScript scope-resolution pass and pass it into standard import resolution so package-style imports avoid repeated linear file-list scans.\n\nFixes #1839 * test(typescript): add wiring-level test for scope-resolver suffix index - Test typescriptScopeResolver.resolveImportTarget directly (the real production entry point) with package-style, unresolvable, and relative imports - Use vi.spyOn on buildSuffixIndex to verify the index is built inside the makeTsResolveImportTarget closure — fails if index wiring is removed - Fix existing test to pass real file lists instead of empty arrays alongside the prebuilt index, matching production wiring --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Test <test@example.com> |
||
|
|
6c572749b0
|
fix(web): stop Nexus AI agent when user clicks Stop (#1820)
* fix(web): stop Nexus AI agent when user clicks Stop Wire AbortController through chat streaming so Stop cancels the LangGraph run instead of only hiding the loading UI. Fixes #1615. * fix(web): address PR review feedback for Nexus AI stop Guard stream cleanup against Stop-then-Send races, remove dead cancelled handler, tighten abort error detection, add stopped tool-call status, and extend abort unit tests. Fixes #1615. * chore(autofix): apply prettier + eslint fixes via /autofix command * fix(web): address review findings for Nexus AI stop/cancel - Fix race conditions in useAppState.tsx abort lifecycle: - Replace stale isChatLoading closure guard with chatStateRef - Track and cancel rAF handles in stopChatResponse/finally - Move cancelled chunk check before onChunk dispatch - Simplify finally block to unconditional cleanup via chatStateRef - Guard tool_result from overwriting stopped status - Have clearChat abort in-flight streams before clearing - Reorder isAbortError to check error identity before signal.aborted - Refactor AgentStreamChunk to discriminated union for exhaustive switch - Fix test assertions to use exact .toEqual() per DoD §2.7 - Add test for plain Error with name AbortError - Remove dead markStopped alias, simplify signal spread-conditional --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Test <test@example.com> |
||
|
|
7556a8e73a
|
feat(cobol): migrate COBOL to scope-based resolution (regex provider) (#941) (#1835)
* feat(cobol): migrate COBOL to scope-based resolution (regex provider)
Migrate COBOL to scope-based registry resolution, validating the
parse-source-agnostic contract — COBOL uses regex, not tree-sitter,
but implements the same LanguageProvider interface via emitScopeCaptures.
Phase 1-5 complete per #941 DoD.
New files:
languages/cobol/captures.ts — emitScopeCaptures wrapping regex tagger
languages/cobol/interpret.ts — import/type-binding/receiver hooks
languages/cobol/index.ts — barrel export
languages/cobol/scope-resolver.ts — ScopeResolver wiring (9 fields, 3 toggles)
Modified files:
languages/cobol.ts — wire 4 scope-resolution hooks
registry.ts — register cobolScopeResolver
registry-primary-flag.ts — document REGISTRY_PRIMARY_COBOL
Fixtures:
17 fixture files, 30 test cases across 11 required classes
test/integration/resolvers/cobol-scope.test.ts
Tests: 24/24 pass (default + REGISTRY_PRIMARY_COBOL=0)
tsc: zero cobol-specific errors
Shadow mode (GITNEXUS_SHADOW_MODE=1): zero crashes
Regex perf: 10K-line file in 408ms (threshold: 2000ms)
NOT added to MIGRATED_LANGUAGES — REGISTRY_PRIMARY_COBOL env var only.
* chore(cobol): add COBOL to MIGRATED_LANGUAGES
* Revert "chore(cobol): add COBOL to MIGRATED_LANGUAGES"
This reverts commit
|
||
|
|
c8117d1292
|
feat(web): Introduce Tree View and Circles View in Web Viewer (#1799)
* feat(graph-view): add tree and circles layout modes
Add alternate graph layouts to the web viewer with new graph view state, canvas controls, adapters, and Sigma layout logic for tree and concentric-circle rendering. Include layout and adapter tests plus tree-view E2E coverage aligned with the English UI labels, and tune node visibility, edge layering, large-graph behavior, and tree-layer spacing so the new views stay readable. Follow up the tree-view work by keeping noisy variables hidden by default and mapping Property/Const icons so filter coverage stays in sync with the expanded node taxonomy.
Co-authored-by: OpenAI Codex <noreply@openai.com>
AI-model: GPT-5 Codex
* fix(web): cap tree layout spring iterations and remove unused variable
Finding A (blocker): calculateTreeLayout runs 14 synchronous spring
iterations over all edges and nodes — O(N×E×14) + O(N log N) per layer
per iteration — with no size guard. At 10K+ nodes this freezes the
main thread for several seconds.
Fix: make SPRING_ITERATIONS adaptive:
- N > 10 000 → 0 iterations (proportional initial layout only)
- N > 3 000 → 4 iterations
- otherwise → 14 iterations (unchanged behaviour for small graphs)
Also removes the unused `const r` at useSigma.ts:1314, which was a
leftover after the radial-resistance decomposition was removed.
This clears the CodeQL "unused variable" warning (Finding G).
Co-authored-by: Claude <noreply@anthropic.com>
AI-model: claude-sonnet-4-6
* test(graph-adapter): add circles adapter tests and tree layout perf bound
Finding B (high): knowledgeGraphToCirclesGraphology had zero test
coverage. Adds three new tests:
- ring placement: verifies Folder→ring 0, File→ring 1, Function→ring 3
and confirms circles-specific attributes (circlesRing, circlesAnchorX/Y)
are set while tree attributes (treeAnchorX/Y) are absent.
- edge styling: CONTAINS is marked isHierarchyEdge=true with the
hierarchy colour; CALLS is cross-cutting with its own colour.
- CALLS cross-cutting: a lone CALLS edge between two Functions is
correctly identified as a non-hierarchy edge.
Also adds a performance-bound test for the tree adapter at 2 000 nodes /
4 000 edges (the adaptive 14-iteration path) asserting completion within
2 s — catches regressions to the O(N×E×iterations) main-thread blocking
that Finding A identified.
Co-authored-by: Claude <noreply@anthropic.com>
AI-model: claude-sonnet-4-6
* refactor(web): rename Tree View → Sequential Layout, Circles → Radial Layout
Aligns the UI labels with standard graph layout terminology from the
Cambridge Intelligence taxonomy (cambridge-intelligence.com/blog/automatic-graph-layouts):
Tree View → Sequential Layout (顺序布局)
Circles → Radial Layout (径向布局)
Force Graph → Force Graph (unchanged)
Internal graphViewMode keys ('tree', 'circles', 'force') are unchanged —
only the displayed strings in en/zh-CN locales and the E2E button selectors
are updated.
Co-authored-by: Claude <noreply@anthropic.com>
AI-model: claude-sonnet-4-6
* perf(web): add adaptive large-graph guards to sequential layout physics
For graphs with N > 5 000 nodes, each rAF frame of runTreeLayout was
doing O(N log N) sort + O(N × k) repulsion pair comparisons (k ≈ 2 400
for a 20 K-node graph spread across 1 080 px at range 130). At that
scale each frame took hundreds of ms, making the canvas appear completely
frozen even though the physics loop was still running.
Fix mirrors the circles layout adaptive strategy:
N > 5 000 (large):
- Skip repulsion pass (O(N × k) → 0)
- Skip spread-force sort (O(N log N) → 0)
- Velocity cap raised to ±12 / ±6 px so nodes cover ground faster
- Damping 0.58, 1 sim step/frame, 30 s max duration
- Looser early-stop thresholds (max v 0.05, avg v 0.03, active 2 %)
N > 1 500 (medium):
- Velocity cap raised to ±6 / ±3 px
- 24 s max duration
- Repulsion and spread still active
N ≤ 1 500 (small):
- Unchanged behaviour (velocity ±3/±2, 18 s, all forces active)
Layer gravity (O(N)) and edge springs (O(E)) run for all graph sizes —
they provide the structural pull that replaces repulsion at large N.
Co-authored-by: Claude <noreply@anthropic.com>
AI-model: claude-sonnet-4-6
* fix(web): fix stale closure in sigma event handlers breaking node selection
The sigma 'clickNode', 'clickStage', 'enterNode', and 'leaveNode' handlers
are registered in a one-time useEffect (empty dep array). They captured
options.onNodeClick via closure, so they always called the initial version
of handleNodeClick — the one created before the graph loaded where
`if (!graph) return` exits immediately.
Consequence: clicking a node in the canvas never updated the app-level
selectedNode state. This broke:
- The Focus Depth filter (warning "Select a node to apply depth filter"
persisted even after a canvas click)
- The depth hop filter not applying (selectedNode was always null)
- The code panel not opening on canvas node click
Fix: store the three callback props in refs (onNodeClickRef, onNodeHoverRef,
onStageClickRef) and update them synchronously on every render. The sigma
event handlers now read from the refs, so they always invoke the latest
version of the callbacks without needing to re-register.
Co-authored-by: Claude <noreply@anthropic.com>
AI-model: claude-sonnet-4-6
* fix(web): address three code-review bugs in graph rendering
Bug 1 (useSigma.ts): forces in the tree physics loop were computed once
before the sub-steps loop and reused for every step, causing 2× displacement
on slow frames (>64ms, simulationSteps>1). Fix: move forceX/forceY Maps and
all force accumulation (layer gravity, edge springs, repulsion, spread) inside
the loop so each sub-step integrates from current node positions.
Bug 2 (graph-adapter.ts): all three adapters used `graph.hasEdge(src,tgt)`
as a dedup guard, which silently drops any second edge between the same node
pair. A CALLS relationship between nodes that also have a CONTAINS edge was
always lost. Fix: switch from `new Graph()` to `new MultiGraph()` (allows
multiple edges per pair) and dedup by `rel.id` instead of by node pair.
Bug 3 (graph-adapter.test.ts): the cross-cutting edge styling test never
executed its CALLS branch because Bug 2 dropped the CALLS edge before the
assertion ran. Fix: assert `sigmaGraph.size === 2` and verify both edges
individually after collecting attrs by relationType.
Co-authored-by: Claude <noreply@anthropic.com>
AI-model: claude-sonnet-4-5
* fix(web): address three code-review bugs in graph rendering
- Move radial layout force accumulation inside the sub-step loop so
forces are recomputed from updated node positions each iteration
instead of using stale forces computed before the loop began
- Revert knowledgeGraphToGraphology from MultiGraph back to Graph with
node-pair deduplication to prevent ForceAtlas2 from double-applying
spring forces for node pairs that share multiple relation types
- Add Target to the lucide-icons import in FileTreePanel.tsx so the
Const node type icon resolves without a ReferenceError
Co-authored-by: Claude <noreply@anthropic.com>
AI-model: claude-sonnet-4-6
* fix(web): address four more PR review comments
Edge visibility (useSigma.ts): HAS_METHOD / HAS_PROPERTY edges were hidden
when any edge-type filter was active because those types are not in the EdgeType
union. Normalize HAS_METHOD → DEFINES and HAS_PROPERTY → CONTAINS before the
visibleTypes.includes() guard so Kotlin/Java hierarchy edges follow the same
filter logic as their semantic equivalents.
Force-mode edge styles (graph-adapter.ts): HAS_METHOD / HAS_PROPERTY fell back
to the default gray color in the force-graph adapter because EDGE_STYLES had no
entries for them. Added explicit entries using the same hues as DEFINES/CONTAINS
so force mode renders Kotlin/Java hierarchy edges consistently with tree/circles.
Accessibility (GraphCanvas.tsx, locales): the layout-mode switcher (Force /
Tree / Circles) had no ARIA semantics. Added role="tablist" on the container
and role="tab" + aria-selected on each button. Added the viewModes.label i18n
key (used as aria-label on the tablist) to en and zh-CN locale files.
Flaky test (graph-adapter.test.ts): replaced the hard 2 s wall-clock assertion
with a structural check (node count + edge count) that is deterministic across
CI hardware. Timing tests are inherently flaky and provide no correctness signal.
Co-authored-by: Claude <noreply@anthropic.com>
AI-model: claude-sonnet-4-5
---------
Co-authored-by: OpenAI Codex <noreply@openai.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
|
||
|
|
681a352006
|
fix(worker): analyze native worker aborts (#1833)
* fix(analyze): avoid native aborts on generated worker bundles Retire timed-out parse workers instead of force-terminating native parser state, and skip Monaco generated worker bundles by default while preserving explicit .gitnexusignore negation overrides. Constraint: Node native tree-sitter bindings can abort the process when a timed-out worker is terminated while inside parser state. Rejected: Falling back to sequential parsing for native stalls | it can move the same native crash onto the main thread. Confidence: high Scope-risk: moderate Directive: Keep timeout recovery from force-terminating workers until they return to JS or exit naturally. Tested: npm test; npx tsc --noEmit; npm run build; targeted analyze on /Users/wangxc/Code/keep; gitnexus detect_changes --scope staged Not-tested: Node 22 LTS runtime and non-macOS platforms * fix(worker): bound retired parser worker lifetimes Keep timeout recovery from immediately terminating workers that may still be inside native parser state, while making terminal pool shutdown own retired worker cleanup so long-lived processes do not accumulate retired threads. Constraint: Claude review on PR #1833 required retiredWorkers cleanup in pool.terminate() and tripBreaker() without regressing no-immediate-terminate timeout safety. Rejected: clearing the retiredWorkers set without terminating | would remove JS bookkeeping while leaking the underlying worker thread. Confidence: high Scope-risk: moderate Directive: Preserve the distinction between recoverable timeout retirement and terminal pool shutdown; do not reintroduce immediate terminate in removeWorkerFromSlot(..., 'retire'). Tested: npx vitest run test/unit/worker-pool-timeout-retire.test.ts; npx vitest run test/unit/worker-pool-timeout-retire.test.ts test/unit/worker-pool-resilience.test.ts test/unit/worker-pool-cumulative-timeout.test.ts test/unit/worker-pool-slot-generation.test.ts; npx tsc --noEmit; npm run build; npx prettier --check src/core/ingestion/workers/worker-pool.ts test/unit/worker-pool-timeout-retire.test.ts ../docs/todo/pr-1833-retired-worker-cleanup-plan.md; npx eslint src/core/ingestion/workers/worker-pool.ts test/unit/worker-pool-timeout-retire.test.ts; gitnexus detect_changes --scope staged. Not-tested: npm test full suite did not complete green in this environment; two runs each had one unrelated test/unit/hooks.test.ts parseHookOutput null failure, and each failed hook test passed when rerun in isolation. * ci: retrigger checks Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: wangxc <wangxc_a_bj@si-tech.com.cn> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Test <test@example.com> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
5e012c373b
|
fix(cli): detect missing LadybugDB native binary at startup with actionable guidance (#835) (#1837)
* fix(cli): detect missing LadybugDB native binary at startup with actionable guidance (#835) Add checkLbugNative() pre-flight that verifies lbugjs.node exists before any command transitively imports @ladybugdb/core. When missing (bun default install, --ignore-scripts), prints repair instructions instead of crashing with ERR_DLOPEN_FAILED. Also enhances `gitnexus doctor` to probe the native binary status. * fix(review): guard eval-server, un-guard status command eval-server transitively loads @ladybugdb/core and needs the native binary check. status only reads filesystem metadata and should remain accessible when the binary is missing. * fix(lint): use console.log instead of console.error in native check gate The project eslint config only allows console.log. * fix(cli): route native-check to stderr and validate binary loadability Fixes two Codex adversarial review findings: 1. Native-check failure message now goes to process.stderr.write instead of console.log, preventing MCP stdout protocol contamination. 2. checkLbugNative now attempts a controlled require() probe after the existence check. Truncated, ABI-mismatched, or wrong-platform binaries produce actionable guidance instead of passing through to crash at process.dlopen. --------- Co-authored-by: Test <test@example.com> |
||
|
|
05d269ec28
|
feat(ruby): migrate Ruby to scope-based resolution (RFC #909 Ring 3) (#1831)
* feat(ruby): migrate Ruby to scope-based resolution (RFC #909 Ring 3) Implement the full scope-resolution pipeline for Ruby following the PR #1639 (Rust migration) standard, targeting registration in MIGRATED_LANGUAGES with 100% scope parity. Scope resolver hooks (languages/ruby/): - query.ts: RUBY_SCOPE_QUERY covering scopes, declarations, imports, type-bindings (constructor inference via .new), and references - captures.ts: emitRubyScopeCaptures orchestrator with import decomposition, receiver-binding synthesis, method reclassification, and arity metadata for both declarations and calls - receiver-binding.ts: self type-binding synthesis for instance methods, singleton methods, and class << self blocks - interpret.ts: interpretRubyImport (wildcard semantics) and interpretRubyTypeBinding (YARD, constructor, alias sources) - import-target.ts: resolveRubyImportTarget adapting the existing suffix resolver for require/require_relative/load - merge-bindings.ts: tier-based shadowing (local > namespace > import) - arity.ts: Ruby arity check with *args/**kwargs/&block support - scope-resolver.ts: rubyScopeResolver with custom buildRubyMro (kind-aware IMPLEMENTS partitioning: prepend > direct > include; extend excluded from instance MRO per legacy semantics) - simple-hooks.ts: bindingScopeFor, importOwningScope, receiverBinding Wiring: - ruby.ts provider gains 7 scope-resolution hooks - Registered in SCOPE_RESOLVERS map and MIGRATED_LANGUAGES - 127 legacy tests wired with createResolverParityIt('ruby') - 27 new scope-specific tests in ruby-scope.test.ts Parity: 89/127 legacy tests pass under registry-primary; 38 are heritage/property/YARD gaps expected in V1. All 127 pass under legacy. Closes #931 * feat(ruby): add emitHeritageEdges hook, YARD parsing, bare calls, property emission Extend the scope-resolution pipeline with a new optional `emitHeritageEdges` hook (ScopeResolver contract + run.ts wiring) that runs between `preEmitInheritanceEdges` and `buildMro`. This lets languages whose heritage declarations are syntactic method calls (Ruby include/extend/prepend) emit IMPLEMENTS edges from the scope-resolver without touching the legacy pipeline. Ruby scope-resolution improvements: - Heritage: intercept include/extend/prepend in captures.ts, encode as special imports, emit IMPLEMENTS edges via emitHeritageEdges hook - Properties: intercept attr_accessor/attr_reader/attr_writer, emit Property nodes + HAS_PROPERTY edges via the same hook - Bare calls: add (body_statement (identifier)) capture to scope query, matching the legacy query pattern for zero-arity method calls - YARD parsing: second-pass comment scanner for @param/@return/@type annotations with findFollowingMethod that handles body_statement nesting - Query fixes: @declaration.trait for modules (was @declaration.module which normalizeNodeLabel didn't recognize), constant constructor bindings (SERVICE = UserService.new), call-return inference Parity: 114/127 legacy tests pass under registry-primary (up from 89). Remaining 13 are advanced type-inference chain resolution (compound receiver, cross-file return-type propagation, for-in element types). * feat(ruby): achieve 100% scope-resolution parity (127/127) Fix all 13 remaining type-inference failures: - Add expandsWildcardTo hook (expandRubyWildcardNames) so finalize can materialize individual bindings from require/require_relative wildcard imports, unblocking cross-file return-type propagation - Add member-call-return type binding synthesis in captures.ts for assignments like `x = obj.method()` — enables compound receiver chaining through member call return types - Add YARD @return support for attr_accessor/attr_reader/attr_writer calls, creating field-type bindings for chain resolution - Add @declaration.property captures alongside __property__ imports so properties register in localDefs → model.fields → write-access - Add constructor-return inference for methods ending with Foo.new() - Add for-loop variable type aliasing in scope query - Rebuild nodeLookup after emitHeritageEdges in run.ts so Property nodes created by the heritage hook are visible to downstream passes - Extend compound-receiver resolver to handle compound member-call rawNames with () and increase max depth from 4 to 8 - Extend receiver-bound-calls Case 3b for compound rawNames All 127 legacy Ruby tests pass under both REGISTRY_PRIMARY_RUBY=0 (legacy) and =1 (registry-primary). Ruby is now fully registered in MIGRATED_LANGUAGES with 100% scope parity. * test(ruby): add pipeline benchmark exercising heritage emission Synthetic Ruby codebases at 100/250/500 files with include + extend + prepend mixins, diamond mixin patterns (shared BaseMixin modules), attr_accessor properties, YARD annotations, and cross-file imports. Strict equality assertions verify exact IMPLEMENTS and HAS_PROPERTY edge counts: 4 IMPLEMENTS per class (include x2, extend, prepend) plus 1 per non-base mixin module, 3 HAS_PROPERTY per class. Dedup in emitRubyMixinEdges prevents double-counting when the worker path (repos >= 15 files) already created Property/IMPLEMENTS edges before scope-resolution runs. Scaling: 0.76x and 1.40x (both linear, well under 3x threshold). * ci: retrigger build * fix(ci): resolve format, registry-primary-flag, and sequential-mixin test failures - Run prettier on all changed files (captures.ts, run.ts, ruby-scope.test.ts, ruby.test.ts, ruby-pipeline-benchmark.test.ts) - Update registry-primary-flag.test.ts: use Swift (not in MIGRATED_LANGUAGES) instead of Ruby for the isolation and env-var mutation tests - Pin ruby-sequential-mixin.test.ts to REGISTRY_PRIMARY_RUBY=0 (legacy mode) since it tests inferImplicitReceiver + selectDispatch hooks that live in the legacy call-processor (gated off under registry-primary) --------- Co-authored-by: Test <test@example.com> |
||
|
|
d5b2edddc4
|
fix(test): use retry cleanup in antigravity e2e to prevent ENOTEMPTY flake (#1838)
* fix(test): use retry cleanup in antigravity e2e to prevent ENOTEMPTY flake Replace bare `fsp.rm` / `fs.rmSync` in antigravity-hook-e2e.test.ts afterAll with `cleanupTempDir` / `cleanupTempDirSync` from test-db.ts which retry with backoff on transient filesystem errors. Also make `shouldSwallowCleanupError` swallow ENOTEMPTY on all platforms (was Windows-only). The CI failure on macOS was ENOTEMPTY on a deeply nested node-gyp cache directory inside the temp HOME — a cleanup-time race that retries usually resolve, but the final attempt must not crash the test suite if the race persists. * fix: restore fsp import needed for mkdtemp/mkdir --------- Co-authored-by: Test <test@example.com> |
||
|
|
4870879b21
|
fix(wiki): add budget-aware grouping to prevent context overflow on large repos (#627) (#1832)
* fix(wiki): add budget-aware grouping to prevent context overflow on large repos (#627) When the grouping prompt exceeds 100k tokens (e.g. Apache TVM with ~2,378 files and ~306k estimated tokens), batch files by top-level directory and issue one LLM call per batch. Partial results are deterministically merged; any batch failure falls back to directory-based grouping. * fix(wiki): address review findings — exact assertions, progress fix, error logging - Replace bounds-only .toBeGreaterThan assertions with exact .toBe values - Add per-batch budget compliance assertion for sub-batch case - Add assertion that partial LLM results don't leak through nuclear fallback - Pass fixedPercent/percentRange to streamOpts in batched LLM calls - Log batch failure in onProgress before falling back to directory grouping - Strengthen mergeGroupings dedup test from .toContain to exact .toEqual * chore(autofix): apply prettier + eslint fixes via /autofix command * fix(wiki): prevent slug collisions and handle single-file oversize in batched grouping mergeGroupings now normalizes module keys by slug so case/punctuation variants ("API Routes" vs "API routes") merge into one module instead of producing colliding .md files. batchFilesForGrouping now truncates per-file symbol lists via binary search when a single file exceeds GROUPING_TOKEN_BUDGET, so every LLM request stays within the context window. * style(wiki): apply prettier formatting to generator.ts --------- Co-authored-by: Test <test@example.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
c916c88361
|
feat(mcp): add limit/offset/summaryOnly pagination to impact tool (#1818)
* feat(mcp): add limit/offset/summaryOnly pagination to impact tool (#414) The impact tool returns unbounded byDepth arrays for hub symbols (base error classes, shared utilities), producing 140KB+ responses that get truncated by MCP clients. maxDepth alone does not help when most dependents are at depth 1. Add three new parameters: - summaryOnly: returns counts/risk/processes/modules without byDepth - limit: caps symbols per depth level (default 100) - offset: skips symbols for pagination Also adds byDepthCounts to all responses so agents can see total counts even when the symbol list is paginated or omitted. Closes #414 * fix(mcp): prevent pagination from silently truncating cross-repo impact Address review findings on #1818: - F1 (blocker): _runImpactBFS no longer defaults to limit 100 when limit is not set — only _impactImpl (MCP entry) applies the default. Internal callers (impactByUid, group impact) get complete results. GroupToolPort.impact interface gains optional limit param, and cross-impact.ts passes limit: 10000 for local UID collection. - F2 (blocker): tool description updated — byDepth is now documented as paginated, not 'all affected symbols'. - F3: impactByUid calls _runImpactBFS without limit, so Phase-2 neighbor results are no longer capped at 100. - F4: pagination metadata now appears when offset > 0 (head truncation), not just tail truncation. Pagination.limit is null when uncapped. - F5: limit/offset schema types changed from number to integer; Math.trunc applied in implementation as defense-in-depth. - F6: 7 new tests — multi-depth pagination, offset-only truncation, offset past end, float inputs, _runImpactBFS internal uncapped path, collectImpactSymbolUids with paginated vs complete data. * fix(mcp): NaN guard on pagination params, complete GroupToolPort interface - Add Number.isFinite guard to limit/offset in _runImpactBFS so NaN inputs fall through to uncapped/zero defaults instead of producing silent empty byDepth with no truncation signal. - Add offset and summaryOnly to GroupToolPort.impact interface to match the implementation and prevent silent param loss at the port boundary. - Replace bounds-only toBeLessThan assertion with exact byDepthCounts and pagination assertions per DoD §2.7. * fix(mcp): address remaining review findings for impact pagination - #3: Forward limit/offset/summaryOnly through callToolAtGroupRepo so group-mode MCP callers can use the new pagination params. - #4: Extract GROUP_LOCAL_PHASE_LIMIT constant from magic 10000 in cross-impact.ts with a comment explaining the intent. - #7: eval-server formatImpactResult uses byDepthCounts[depth] for the 'and N more' suffix instead of paginated slice length. - #8: Extract ImpactParams interface from duplicate inline type definitions in impact() and _impactImpl(). - #9: Add --limit, --offset, --summary-only CLI flags to the impact command with i18n help strings (en + zh-CN). - #10: Clarify in tool description that limit/offset apply per depth level, not per total result set. * chore(autofix): apply prettier + eslint fixes via /autofix command * @ fix(mcp): address Copilot review feedback on impact pagination - Sanitize limit/offset with Number.isFinite in _impactImpl to prevent NaN passthrough from bypassing the default limit of 100 - Omit pagination.limit field instead of emitting null when paginationLimit is Infinity, keeping the response schema consistent - Move GROUP_LOCAL_PHASE_LIMIT after all imports in cross-impact.ts - Stop forwarding limit/offset/summaryOnly to group-mode impact since runGroupImpact overrides limit with GROUP_LOCAL_PHASE_LIMIT for UID collection and does not re-paginate - Validate CLI parseInt results with Number.isFinite before passing to the backend, falling back to undefined so defaults apply - Use byDepthCounts to decide whether to render depth sections in formatImpactResult, handling empty pages from offset past end @ * @ fix(mcp): address code review findings on impact pagination - Fix formatImpactResult "N more" count: use Math.min(items.length, 12) instead of hardcoded 12, so paginated pages with <12 items show the correct remaining count - Detect summaryOnly responses (byDepth absent, byDepthCounts present) and show a summary-mode message instead of misleading "(0 items on this page — adjust offset)" per depth level - Document that limit/offset/summaryOnly are single-repo only and ignored in group mode (@groupName) in MCP tool schema descriptions - List byDepthCounts in summaryOnly description and note byDepth absence when summaryOnly is true - Remove unused limit/offset/summaryOnly from GroupToolPort.impact interface since they are never forwarded to group impact - Deduplicate parseInt calls in CLI tool.ts: extract to local variables with consistent optional-chain usage @ * chore(autofix): apply prettier + eslint fixes via /autofix command * @ fix(group): restore limit in GroupToolPort.impact interface cross-impact.ts passes limit: GROUP_LOCAL_PHASE_LIMIT through the GroupToolPort.impact interface for UID collection. Only offset and summaryOnly were truly unused — limit must stay. @ * @ docs: add limit/offset/summaryOnly to impact tool options in README @ --------- Co-authored-by: Test <test@example.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
966ddb981e
|
feat(cpp): thread base-specifier qualifier through dependent-base lookup (#1815) (#1819)
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
* feat(cpp): thread base-specifier qualifier through dependent-base lookup (#1815) captures.ts: add extractBaseLookupQualifier, fix isBaseDependent for qualified_identifier bases. two-phase-lookup.ts: qualifier storage, markCppDependentBase accepts qualifier, dedup index by nodeId (last-wins), V3 qualifier targeting (dormant). Infrastructure delivered: qualifier extraction, storage, dedup, isBaseDependent fix. V3 targeting dormant until qualifiedName computation fix reaches localDefs. Part of #1564. Infrastructure for #1815. * fix(cpp): three conservatism fixes for dependent-base lookup Fix 1 — Map collision in markCppDependentBase (line 83): Change innermost storage from Map<baseName, qualifier> to Map<baseName, Set<qualifier>> so multiple captures of the same dependent base name with different qualifiers don't collide. Fix 2 — Single-candidate bypass (lines 197-206): For qualified bases with only one candidate, verify namespace match before accepting. Unqualified bases still accept the unique candidate. Previously accepted regardless, creating false edges. Fix 3 — V3→V2 fallthrough (line 221): When a syntactic qualifier is present but no exact match is found, suppress rather than falling through to V2 prefix-heuristic. V2 only runs for truly unqualified bases, which is what it was designed for. All three are conservative bug fixes — turn false positives into suppression, not behavior changes. 250/250 tests pass both modes. --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
05151b1079
|
chore(deps)(deps): bump lru-cache from 11.3.6 to 11.4.0 in /gitnexus (#1826) | ||
|
|
5458ddce77
|
chore(deps)(deps-dev): bump @types/node in /gitnexus (#1825) | ||
|
|
3161938730
|
chore(deps)(deps-dev): bump vitest from 4.1.6 to 4.1.7 in /gitnexus (#1824) | ||
|
|
d4449b4ec8
|
fix(lbug): resolve non-ASCII paths for KuzuDB on Windows (#1811) (#1817)
* fix(lbug): resolve non-ASCII paths to 8.3 short form on Windows (#1811) KuzuDB's native C++ layer uses ANSI file APIs (fopen) on Windows. When the repo path contains CJK or other non-ASCII characters, the UTF-8 bytes from Node.js are misinterpreted as the system's Active Code Page (e.g. GBK), producing a garbled path — "Error 3: The system cannot find the path specified." Add `toNativeSafePath()` which converts non-ASCII paths to their Windows 8.3 short-name form (all-ASCII) before passing them to the native layer. Applied to both the database open path and the COPY CSV paths. No-ops on non-Windows and on all-ASCII paths. Closes #1811 * test(lbug): add unit + integration tests for non-ASCII path handling (#1811) - Unit tests for toNativeSafePath: ASCII passthrough, non-Windows no-op, Windows short-path conversion, nonexistent-path fallback - Integration test: full initLbug + loadGraphToLbug round-trip with CJK characters in the storage path — runs on all platforms - Fix toNativeSafePath to reject cmd.exe output containing '?' chars (replacement for unrepresentable Unicode in the console code page) - Register integration test in vitest lbug-db project and cross-platform-tests.ts matrix * chore(autofix): apply prettier + eslint fixes via /autofix command * feat(lbug): junction fallback, tmpdir CSV staging, pool-adapter coverage (#1811) U1+U4: toNativeSafePath now tries 8.3 short path → NTFS junction fallback → diagnostic warning. Junctions target path.dirname(p) and reconstruct the leaf. Handles EEXIST races. Registers cleanup on exit/SIGTERM/SIGINT. Orphan scan on first call removes stale junctions from prior crashes. U2: loadGraphToLbug redirects csvDir to os.tmpdir() when storagePath contains non-ASCII on Windows, avoiding non-ASCII characters in COPY FROM paths entirely. U3: All 4 createLbugDatabase call sites in pool-adapter.ts now wrap dbPath with toNativeSafePath. * fix(test): fix CI failures from toNativeSafePath addition (#1811) - Fix lbug-non-ascii-path integration test: use CodeRelation (actual relationship table name) instead of CALLS - Add toNativeSafePath to lbug-config.js mocks in pool-wal-recovery and lbug-pool-win-fts-probe tests — pool-adapter now imports it * fix(lbug): sanitize path before cmd.exe shell expansion (CodeQL) Reject paths containing cmd.exe metacharacters (" % | & < > ^) before interpolating into the `for %I` short-path command. Prevents command injection via crafted path names. * fix(lbug): address code review findings in non-ASCII path implementation - U1: Use process.exit(0) on Windows instead of process.kill re-raise (SIGTERM forcefully kills on Windows, handlers never fire) - U2: Pass safePath to openWithLockRetry so sidecar sweep targets the path KuzuDB actually opened, not the original non-ASCII path - U3: Skip junction creation in worker threads (isMainThread guard) to prevent junction leaks from pool-adapter workers - U4: Replace existsSync with lstatSync in orphan scan to avoid 30s blocking on unreachable UNC network targets * chore(autofix): apply prettier + eslint fixes via /autofix command * fix(lbug): correct SIGTERM exit code and run Prettier (#1811) - Use exit code 143 (SIGTERM) / 130 (SIGINT) on Windows instead of 0 so termination is not masked as success - Run Prettier to fix formatting (CI Gate blocker) * fix(lbug): eliminate CodeQL command-injection taint in tryShortPath Pass the path via GITNEXUS_SP environment variable instead of interpolating it into the cmd.exe command string. The FOR loop reads %GITNEXUS_SP% from the environment, so the command text is entirely static — no user-controlled data in the shell command. Also removes CMD_UNSAFE_RE since the env var approach makes character-level sanitization unnecessary. --------- Co-authored-by: Test <test@example.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
a229e8e77b
|
fix(build): skip build.js when running outside the monorepo (#1795) (#1816)
`scripts/build.js` assumes the monorepo sibling `gitnexus-shared`
exists. When a user runs `npm install` from within a global install
directory, the `prepare` lifecycle fires `build.js`, which calls
`execSync(tsc, { cwd: nonExistentPath })` — Node reports this as
the misleading `spawnSync /bin/sh ENOENT`.
Add an early guard: if `gitnexus-shared` is absent and `dist/`
already exists (published package context), exit cleanly. If neither
exists, print a helpful error pointing to the monorepo checkout.
Co-authored-by: Test <test@example.com>
|
||
|
|
50c6acb108
|
feat(setup): implement antigravity integration setup and hook adapter… (#1730)
* feat(setup): implement antigravity integration setup and hook adapter for gitnexus * docs(readme): list Antigravity in supported editors * test(setup-antigravity): pin platform per-test to fix Windows CI failure The MCP entry assertion expected `npx` directly, but on Windows `getMcpEntry()` wraps it as `cmd /c npx ...`, which broke the Windows runner. Pin platform to darwin in beforeEach so the existing assertion is deterministic, restore the descriptor in afterEach, and add a parity test for the win32 cmd-wrapper shape. * fix(antigravity): align hook adapter to Gemini CLI schema + fix Windows CI Rebase the Antigravity integration on the canonical Gemini CLI hooks contract (https://geminicli.com/docs/hooks/reference/), which is the documented schema Antigravity 2.0 inherits: - Hook adapter: replace PreToolUse/PostToolUse with the single AfterTool event. BeforeTool has no documented context-injection channel in the Gemini contract, so augmentation runs in AfterTool where hookSpecificOutput.additionalContext is the documented way to append text to the tool result the agent reads. Stale-index hints land in the same channel (so the agent sees them) and are mirrored to stderr for terminal users. Tool-name matcher updated to Gemini CLI snake_case (search_file_content|glob|run_shell_command). - Setup: write hooks to ~/.gemini/settings.json under canonical hooks.AfterTool[] (replaces the ad-hoc hooks.json top-level group). Polite-neighbor merge preserves existing user hooks. Also copy win-rm-list-json.ps1 alongside hook-db-lock-probe.cjs so the Windows MCP server ownership probe doesn't silently fail open. - Tests: 17 regression tests covering MCP write, win32 shape, hook schema, polite-neighbor merge, idempotency, adapter context emission, stale-index hint, and skill layout. - README: footnote documenting the AfterTool design choice and a link to the Gemini CLI hooks reference. Windows CI fix: installSkillsTo previously used glob('*.md') + glob('*/SKILL.md'), which returned zero matches under the Windows runner's temp paths (8.3 short-name like RUNNER~1). Replace with fs.readdir + dirent type checks — same behavior, no path quirks. This fixes the only failing Windows job on the PR. * fix(antigravity): address PR review — windowsHide, stale docs, dead code Addresses the production-readiness review findings on PR #1730: - F1 (blocker): add windowsHide:true to all four spawnSync sites in the Antigravity hook adapter (findCanonicalRepoRoot, runGitNexusCli's two branches, buildStaleIndexHint) so they don't flash console windows on Windows. Matches the fix #1794 already on main for the Claude hook. - F2 (blocker): update gitnexus/README.md editor table to say AfterTool and link the Gemini CLI hooks reference. The published README had drifted to the pre-c1872b4 PreToolUse + PostToolUse schema. - F3: rewrite the stale ~/.gemini block comment in setup.ts. It still described the old hooks.json + gitnexus group + grep_search design. - F4: remove grep_search dead code from extractPattern and its doc comment. The registered matcher is search_file_content|glob|run_shell_command, so grep_search would never be invoked. - F5: annotate timeout:10000 with a ms-unit comment noting Gemini CLI uses milliseconds (Claude Code uses seconds). - F6: add the GITNEXUS_DEBUG branch to extractAugmentContext for parity with the Claude adapter, so suppressed augment stderr is recoverable. - F7: stageAdapter test helper now copies win-rm-list-json.ps1 alongside the .cjs helpers, so the adapter's Windows lock-probe path isn't a silent fail-open in child-process smoke tests. * test(antigravity): add integration tests and register in cross-platform matrix Adds end-to-end coverage on top of the unit-level tests, per maintainer request: - test/integration/setup-antigravity.test.ts (10 tests): exercises the real setupCommand() against a temp HOME with ~/.gemini/antigravity/ present. Verifies mcp_config.json shape, ~/.gemini/settings.json AfterTool entry, adapter + helpers + win-rm-list-json.ps1 copy, baked-in cliPath rewrite (issue #108 regression class), skill layout, polite-neighbor merge against existing user hooks, idempotency, skip-when-absent, corrupt-file safety, and key preservation. - test/integration/antigravity-hook-e2e.test.ts (19 tests): runs the full install-then-execute flow — invokes setupCommand to lay down the adapter + helpers, then spawns the INSTALLED adapter as a real child process against a temp git repo + .gitnexus/. The source adapter cannot be spawned directly (it requires sibling .cjs helpers that only live in hooks/claude/); install-then-spawn mirrors the production codepath. Covers staleness detection across all five git mutation types, --embeddings propagation, polite skip on toolResponse.error / exit_code !== 0, augment crash-free behavior, cwd validation, corrupted/missing meta.json, unknown event names, empty stdin, and the no-.gitnexus deep-nested case. - scripts/cross-platform-tests.ts: registers all three antigravity test files (unit in PLATFORM_LOGIC, two integration files in SPAWN_CLI) so Windows and macOS CI exercise them on every run. * fix(antigravity): review fixes — dedup, silent-failure guard, type coercion, glob filter - Delete mergeGeminiSettingsHooks (verbatim copy of mergeHooksJsonc), replace call site with the original - Unify geminiHasGitnexusHook into hasGitnexusHook with commandFragment parameter; delete the duplicate - Guard against silent adapter-copy failure: verify the adapter file exists before registering the AfterTool hook entry in settings.json; surface helper copy errors instead of swallowing - Fix toolSucceeded type coercion: use Number() so string exit_code values from Gemini CLI are handled correctly - Align glob tool extractPattern with Claude adapter's restrictive regex filter (/[*\/]([a-zA-Z][a-zA-Z0-9_-]{2,})/) - Remove bounds-only toBeGreaterThan(0) assertion (DoD §2.7) - Add antigravity adapter to HOOK_FILES windowsHide regression list * chore(autofix): apply prettier + eslint fixes via /autofix command * chore: trigger CI --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Test <test@example.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
baf57bec88
|
feat(rust): Migrate Rust to scope-based resolution (RFC #909 Ring 3) (#1639)
* Initial plan * feat: add Rust scope-resolution hooks (RFC #909 Ring 3) Implement the scope-based resolution pipeline for Rust, following the established pattern from Go and other migrated languages. New files in gitnexus/src/core/ingestion/languages/rust/: - query.ts: tree-sitter scope query covering scopes, declarations, imports, type bindings, and references - cache-stats.ts: parse cache hit/miss counters - import-decomposer.ts: decomposes use declarations into individual import captures (handles grouped, wildcard, renamed, re-exported) - receiver-binding.ts: synthesizes self type bindings for impl methods - interpret.ts: interprets captures into ParsedImport/ParsedTypeBinding - arity.ts: arity compatibility checker (no overloading in Rust) - merge-bindings.ts: local-shadows-import binding merge strategy - simple-hooks.ts: binding scope, import owning scope, receiver binding - import-target.ts: resolves Rust module paths (crate/super/self) - method-owners.ts: bridges impl block methods to struct defs - captures.ts: main emit function with import decomposition and self-binding synthesis - scope-resolver.ts: ScopeResolver implementation - index.ts: barrel re-exports Wiring changes: - rust.ts: add scope hook imports and properties to defineLanguage - registry.ts: register rustScopeResolver in SCOPE_RESOLVERS - registry-primary-flag.ts: add Rust to MIGRATED_LANGUAGES Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * feat(LANG-rust): add scope-resolution hooks and register Rust ScopeResolver Implements RFC #909 Ring 3 deliverables: - query.ts: tree-sitter scope query for Rust - captures.ts: emitRustScopeCaptures with method reclassification - import-decomposer.ts: use statement decomposition (groups, renames, globs) - interpret.ts: interpretRustImport + interpretRustTypeBinding - import-target.ts: crate/module/super/self path resolution - receiver-binding.ts: self/&self/&mut self receiver synthesis - method-owners.ts: impl block → struct ownership bridging - arity.ts: no-overloading arity check - merge-bindings.ts: local > import > wildcard binding precedence - simple-hooks.ts: binding/import scope, receiver binding - scope-resolver.ts: ScopeResolver contract implementation - Wired into rustProvider (rust.ts) with scope hooks - Registered in SCOPE_RESOLVERS (pipeline/registry.ts) - NOT yet added to MIGRATED_LANGUAGES (29 advanced pattern tests pending) Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/8f14b730-79d4-4356-9505-325750d71f84 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * Add Rust scope-resolution integration tests (RFC #909 Ring 3) Tests cover the core deliverables for the Rust scope-resolution pipeline: - impl blocks and trait implementations - Module resolution (crate::, super::, self::) - Struct fields and type bindings - Self/&self/&mut self receiver binding - Generic functions (V1 ignores generic args) - Grouped imports (use foo::{A, B}) - Renamed imports (use foo::Bar as Baz) - Arity checking (no overloading) - Struct literal constructor inference - Return type inference - Scoped/qualified calls (Foo::new()) - Enum declarations - Multiple impl blocks - Free function calls - Typed let bindings Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * test(LANG-rust): add 28 scope-resolution integration tests Covers 15 test suites validating: impl blocks, trait impls, grouped imports, renamed imports, module resolution, receiver binding, arity filtering, struct literal inference, return type inference, qualified calls, struct fields, enums, multiple impl blocks, free calls, and typed let bindings. Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/8f14b730-79d4-4356-9505-325750d71f84 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * feat(LANG-rust): add implicit crate path fallback, 35 scope tests - Import resolver now falls back to crate-relative for unqualified module paths (Rust 2015 edition compat) - Added 7 more test cases: re-exports, shadowing, closures, default trait methods (35 total, exceeding ≥30 requirement) Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/8f14b730-79d4-4356-9505-325750d71f84 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * feat(rust): add Rust to MIGRATED_LANGUAGES with 100% scope-resolution parity 35/35 integration tests pass under both REGISTRY_PRIMARY_RUST=0 (legacy) and =1 (scope-resolution). Rust scope-resolution is now the default production call-resolution path. * test(rust): add pipeline benchmark matching PHP benchmark pattern Generates synthetic Rust codebases at 100/250/500 files with structs, impl blocks, traits, cross-module use declarations, and method calls. Measures wall-clock time, peak heap, and scaling ratios. Results: sub-linear scaling (0.79x ratio), 500 files in 3.8s with workers, 110MB peak heap. Gated by GITNEXUS_BENCH=1. * chore(autofix): apply prettier + eslint fixes via /autofix command * perf(rust): optimize captures, type normalization, and method-owner linking - Cache findEnclosingImpl result to avoid duplicate tree walk per function - Avoid double namedChild accessor call in struct field arity counting - Extract regex constants (REF_PREFIX_RE, PTR_PREFIX_RE) from hot normalization loops - Replace O(s) suffix-match scan with O(1) Map lookup in method-owner linking Benchmark: 500 files 3806ms → 2970ms (-22%), 250 files 2432ms → 1752ms (-28%) * fix(rust): resolve CodeQL alerts — file-system race and dead code - Remove existsSync+appendFileSync/writeFileSync TOCTOU in benchmark fixture generator; appendFileSync creates if missing - Remove always-true guard on computeRustCallArity return; narrow return type from number|undefined to number - Remove no-op .filter() in fixture generator * feat(rust): hoist impl return-type bindings to struct scope for chain resolution Synthesize a module-level duplicate of @type-binding.return captures for methods inside impl blocks. The scope-extractor's auto-hoist places these on the struct's Class scope, making them visible to the compound receiver chain resolver via classScopeByDefId. Without this, method return types are only on the impl block scope (which has no class-like def and is not indexed by classScopeByDefId), so chains like svc.get_user().save() cannot follow the intermediate return type. This is the structural prerequisite for chain resolution, pattern binding, and for-loop element-type parity (28 remaining tests). The cross-file return-type propagation step still needs wiring for full parity. * fix(rust): use implNode anchor for return-type hoisting — fixes chain resolution Use the enclosing impl_item node (not tree.rootNode) as the synthetic capture anchor. The scope-extractor's auto-hoist places bindings whose anchor matches the innermost scope on the parent scope. With implNode, the binding lands on the Module scope (parent of impl's Class scope), giving declaredAtScope the correct context for findClassBindingInScope to resolve the return type across the scope chain. Unlocks: chain calls (svc.get_user().save()), return-type inference, assignment chains, cross-file binding propagation, call-result binding, deep field chains — 28→27 failing tests. * feat(rust): add populateRangeBindings hook + .await query capture Implement populateRustRangeBindings (Phase 2 hook, same pattern as Go's populateGoRangeBindings) to populate type bindings that need runtime type lookup — for-loop element types, if-let/while-let captured patterns, match arm patterns, and struct destructuring field types. Also add tree-sitter query capture for let x = fn().await — unwraps await_expression to find the inner call_expression. 28→19 failing tests: fixes for-loop Tier 1c, .iter()/.into_iter(), async .await, if-let captured_pattern. * fix(rust): fix tuple_struct_pattern variable extraction + Result<T,E> raw type lookup - Skip wrapper type identifier when finding bound variable in tuple_struct_pattern (Some(user) was binding 'Some' not 'user') - Add lookupRawParameterType to read unstripped generic type from AST for Ok/Err pattern resolution (normalizeRustTypeName strips generics) 28→15 failing tests: fixes if-let Some, if-let Ok/Err, match arm patterns. * fix(rust): fix match_arm parent traversal + raw return type for for-loop calls - Walk up from match_arm through match_block to find match_expression for source variable extraction - Add lookupRawFunctionReturnType to find unstripped return type from AST for same-file for-loop call expression iterables 28→14 failing tests. * feat(rust): inject field type bindings on struct scopes for chain resolution Walk struct_item AST nodes and inject field types (e.g., address -> Address) as typeBindings on the struct's Class scope. The compound receiver chain resolver uses these to follow field chains like user.address.save(). Also fixes: match_arm parent traversal to match_expression, lookupFieldType to check typeBindings first. 28→11 failing tests: fixes field type chains, deep chains, struct destructuring. * feat(rust): cross-file return type lookup for for-loop call iterables Build allReturnTypes map across all parsedFiles in Phase 2 first pass, then use it to resolve for-loop iterables like `for x in get_fn()` when get_fn is defined in another file. 28→9 failing tests. * feat(rust): cross-file field type map for struct destructuring Build allFieldTypes map across parsedFiles in Phase 2 first pass. Used by processStructDestructuring to resolve `let Point { x, y } = p` when Point is defined in another file. 28→7 failing tests. * fix(rust): compound assignment write capture + pending assignment fixpoint - Add compound_assignment_expr query for +=, -=, etc. field writes - Add processPendingAssignments with 3-pass fixpoint for field access and method call result variable bindings (let addr = user.address, let city = addr.get_city()) 28→5 failing tests. * fix(rust): identity method return-type bindings for unwrap/expect chains Inject unwrap/expect/clone/as_ref/as_mut as return-type bindings on struct scopes that return the struct's own type. Since normalizeRustTypeName already unwraps Option<T> → T, calling .unwrap() on a value typed as T is semantically an identity — the return type equals the receiver type. 28→3 failing tests: fixes user.unwrap().save() and repo.unwrap().save() chains. * fix(rust): skip enum variant call-return bindings + cross-file pending assignments + identity alias - Skip Some/None/Ok/Err in @type-binding.call-return — these are enum variant constructors, not type names; let the annotation capture win - Add identifier alias handler in processPendingAssignments for `let alias = opt` chains - Cross-file field type and method return type lookup in pending assignment fixpoint via findFieldTypeAcrossFiles/findMethodReturnTypeAcrossFiles - Identity method bindings (unwrap/expect/clone) on struct scopes 155/156 tests pass (99.4%). Remaining: trait default method dispatch via MRO (repo.count() where count has default impl on Repository trait). * feat(rust): 100% scope-resolution parity — MRO with same-file IMPLEMENTS + trait default method reclassification - Add buildRustMro that includes same-file IMPLEMENTS edges in the MRO chain, so trait default methods (e.g., repo.count()) resolve through the struct → trait ancestry walk - Only add IMPLEMENTS to MRO when struct and trait are in the same file; cross-file trait calls require the trait to be imported (Rust semantics) - Reclassify function_item inside trait_item as @declaration.method so default trait methods register in the model's methods lookup 156/156 legacy parity tests pass. 35/35 scope tests pass. 0 regressions. * fix(rust): address code review findings — null guard, name collision, scope order - Fix Array.find() null guard: check === undefined not === null in processCapturedPattern (find() never returns null) - Fix allReturnTypes/allFieldTypes name collision: delete entry on second occurrence so colliding names (new, default, Config) produce no result rather than a wrong result - Fix lookupTypeInScopes: search function scope then module scope only, skip unrelated Class scopes that could shadow names from other functions --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Test <test@example.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
5ce448a93a
|
feat(wiki): support local Claude and Codex providers (#1769)
* feat(wiki): support local Claude and Codex providers * chore(autofix): apply prettier + eslint fixes via /autofix command * fix(wiki): address local CLI provider review findings - Add subprocess timeout: LocalCLIConfig gains requestTimeoutMs, runLocalCLI sets a kill timer that rejects with an actionable error matching the HTTP timeout message format. --timeout is no longer silently ignored for claude/codex providers. - Add windowsHide: true to spawn() to prevent console window flash on Windows, matching cursor-client.ts behavior. - Skip GITNEXUS_MODEL env var for local providers so a user's OpenAI model name doesn't cross-contaminate claude/codex CLI invocations. Precedence for local providers: --model → savedLocalModel → ''. - Guard against empty stdout: reject with actionable error when CLI exits 0 but produces no output, preventing silent empty wiki pages. * fix(wiki): address deep-review findings in local CLI providers - Move empty-output guard from runLocalCLI to per-provider callers so Codex can read --output-last-message file even when stdout is empty - Merge existing config in interactive setup (local + Azure paths) to prevent saveCLIConfig from erasing previously saved API keys - Use StringDecoder for stdout/stderr to handle multi-byte UTF-8 chars split across pipe chunk boundaries - Distinguish ENOENT from non-zero exit in detectLocalCLI so users see auth guidance instead of misleading "CLI not found" when the binary exists but is not authenticated * test(wiki): add subprocess contract tests for local CLI providers Add 21 integration-level tests covering the Claude and Codex subprocess contracts that wiki-flags.test.ts mocks out: - Claude argv: -p, --output-format text, --no-session-persistence, --model conditional, stdin prompt content, CI=1, windowsHide:true - Codex argv: exec subcommand, --sandbox read-only, -c approval_policy, --output-last-message temp path, --cd, stdin marker, --model - Timeout: kill timer fires and rejects, no timer when unset - Codex file fallback: stdout used when file missing, error when both empty - detectLocalCLI: warn on non-ENOENT, silent on ENOENT - onChunk: cumulative byte count forwarded Also register the test in cross-platform-tests.ts SPAWN_CLI section and fix detectLocalCLI ENOENT detection logic (invert the check so non-ENOENT errors produce a warning). * fix(wiki): platform-aware process tree kill and Codex contract snapshot - Add killChildTree helper that uses taskkill /T /F /PID on Windows to terminate the entire process tree (including cmd.exe grandchildren), with fallback to child.kill() if taskkill fails or on non-Windows - Add Codex CLI flag contract snapshot test that locks the exact spawn args — any flag rename, reorder, or removal is caught immediately - Add Windows taskkill tests: success path asserts taskkill called with correct PID and /T /F flags, failure path verifies child.kill() fallback --------- Co-authored-by: eddie.pan2 <eddie.pan2@jtexpress.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Test <test@example.com> |
||
|
|
1bbc876336
|
fix(cpp): dependent-base resolution across nested/inline namespaces (#1634) (#1814)
* fix(cpp): dependent-base resolution across nested/inline namespaces (#1634) Replace exact namespace-prefix match with prefix-contains filter capped at one level deeper, then accept only if exactly one candidate survives. Behavior change: - Derived<T> in ns::outer can now find Inner<T> in ns::outer::inner (nested namespace) or ns::v1 (inline namespace) via prefix walking - Global-scope deriving classes match any single-segment namespace - Sibling namespace collisions (e.g. detail::Inner vs public_api::Inner) correctly suppress when multiple candidates share the same simple name - Deep nesting (ns → ns.a.b) still suppresses (one-level cap) Fixtures added: pos: nested ns, this->f() -> 1 edge to inner::Inner::f neg: no Inner exists -> 0 edges inline: inline namespace variant -> 1 edge sibling-suppress: sibling collision -> 0 edges (ambiguity suppressed) Part of #1564. 64. * test: add deep-nesting suppression fixture, link #1815 in comment, unqualify inline fixture - Update code comment to reference follow-up issue #1815 instead of 'deferred to follow-up' - Inline fixture: drop explicit v1:: qualifier (exercise inline-expansion path more idiomatically as DoD intended) - Add deep-nesting suppression fixture (ns.a.b -> 0 edges) that pins the one-level cap as a documented invariant - Add legacy parity entry for deep-nesting fixture Part of #1564, #1634. --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
5e8690f992
|
feat(progress): add per-language progress reporting to scope-resolution phase (#1813)
* feat(progress): add per-language progress reporting to scope-resolution phase (#1741) The scope-resolution phase (which can run 74+ minutes on large Java/Kotlin repos) previously emitted zero progress updates, causing the CLI progress bar to freeze at ~49% with a stale "Parsing code" label — making users think the tool was stuck. - Add `scopeResolution` to PipelinePhase type and PHASE_LABELS - Add `onProgress` callback to `runScopeResolution` with per-file updates during the extract loop and sub-phase boundary markers (building scope model, resolving references, emitting edges) - Wire progress through `scopeResolutionPhase` with pre-counted file totals, per-language labels, and pipeline-wide percent mapping (90-95 internal) - Bump mro/communities/processes percent ranges to 95-100 to maintain monotonic progress after scope resolution - Add `scopeResolution` to mro's deps (latent ordering fix: mro reads EXTENDS edges that scope resolution writes via preEmitInheritanceEdges) * fix(progress): clamp overallRatio, fire final extract event, fix mro @deps JSDoc - Clamp overallRatio to [0,1] so percent never exceeds 95 when readFileContents drops files (langFileCount < totalScopeFiles) - Fire onProgress for the last file in the extract loop even when files.length is not divisible by progressInterval - Update mro @deps JSDoc to include scopeResolution * fix(progress): ensure bar redraws at every state transition - Fire initial 'extracting' event at file 0 so the sub-phase label appears immediately, not after progressInterval files - Emit a completion event at percent 95 when scope resolution finishes so the bar definitively reaches the phase ceiling before mro starts * feat(progress): improve UX with human-readable elapsed, language counter, cleaner labels - Format elapsed time as "5m 12s" / "1h 20m" instead of raw "(312s)" for all pipeline phases (CLI-wide improvement) - Add language counter "[1/3]" to scope-resolution detail so users know how many languages remain and which is active - Rename sub-phases for clarity: "building scope model" → "analyzing types", "emitting edges" → "linking symbols" - Remove nested parentheses from detail strings for cleaner display - Expand scope-resolution percent range from 5 to 8 points (90-98 internal → 54-59% display) for more visible bar motion - Re-allocate mro (98), communities (98-99), processes (99-100) * feat(progress): typed sub-phases, i18n locales, and test coverage - Extract ScopeResolutionSubPhase union type with exhaustive switch guard so adding a sub-phase without updating phase.ts is a compile error - Add scopeResolution key to en and zh-CN locale files so the web UI shows translated labels instead of raw message fallback - Extract formatElapsed to its own module with 7 boundary-value tests (0s, 59s, 60s, 3599s, 3600s, 3661s, 7323s) - Add runScopeResolution onProgress integration test proving sub-phase order (extracting → analyzing types → resolving references → linking symbols) and the 0-file early-return path --------- Co-authored-by: Test <test@example.com> |
||
|
|
efcab45560
|
feat(web): support GITNEXUS_BACKEND_URL env var for Docker deployments (#1286)
* feat(web): support GITNEXUS_BACKEND_URL env var for Docker deployments * fix(docker): escape inline script injection to prevent XSS and add server-level integration tests - Add jsonForScriptTag() that escapes <, >, & after JSON.stringify to prevent </script> breakout in inline config script - Sanitize rawBackendUrl in warning log to prevent log injection via newlines - Replace 5 duplicated-helper injection tests with 7 server-level HTTP integration tests that spawn the real docker-server.mjs with GITNEXUS_BACKEND_URL set - Add XSS-specific test: URL containing </script> must produce exactly 1 <script> tag - Add empty-string backendUrl frontend test - Improve Docker Compose Linux guidance with explicit <server-ip> example * fix(docker): harden log sanitization, fix error leak, fix killAndWait race - Broaden log sanitization regex from [\r\n] to [\x00-\x1f\x7f] to strip all C0 control characters including ANSI escape sequences - Replace error.message leak in 500 handler with generic string; log the real error server-side via console.error - Fix killAndWait TOCTOU race by registering exit listener before kill and adding post-kill exitCode guard * fix(docker): handle readFile race to resolve CodeQL file-system-race alert Wrap readFile in try/catch so the TOCTOU between stat() and readFile() is handled gracefully — if the file vanishes between the check and the read, return 404 instead of crashing. * @ fix(docker): eliminate TOCTOU race and format web components Replace the previous try/catch approach with fs.promises.open() to get a file handle, then use handle.stat()/readFile()/createReadStream() from the same fd — properly eliminates the CodeQL "file system race condition" alert by removing the window between stat() and read. Also runs prettier on the 5 web component files that were failing the format CI check. @ * chore(autofix): apply prettier + eslint fixes via /autofix command * chore: trigger CI * @ fix(docker): pass GITNEXUS_BACKEND_URL to the web container The env var was documented but commented out, so docker-server.mjs never received it and the config injection was dead. Uncomment the environment block with a passthrough default so users can set GITNEXUS_BACKEND_URL in .env or their shell for remote/custom deployments. @ * @ fix(docker): eliminate stat() to resolve CodeQL js/file-system-race CodeQL pairs any stat() (FileCheck) with a subsequent open() (FileUse) on an aliased path. The previous approach kept stat() for directory detection, which the analyzer flagged regardless of the fd-based reads. Replace stat() entirely with open() + handle.stat(). On Linux (Docker), open() succeeds for directories, so handle.stat().isDirectory() detects them without a standalone stat() call. This removes the FileCheck node from the data-flow graph, eliminating the alert at its source. @ * @ fix(docker): break CodeQL path alias chain between open() calls CodeQL js/file-system-race pairs two open() calls when their path arguments are data-flow aliased. The previous approach derived the fallback path from the request path (resolve(initialPath, index.html)), creating an alias chain the analyzer could trace. Restructure so the SPA fallback uses a module-level constant (spaFallback = resolve(root, index.html)) with zero data-flow from the request. The two open() calls now have provably independent path arguments, eliminating the FileCheck/FileUse pair. Also simplifies the logic: for an SPA, all non-file requests serve root/index.html — no directory/index.html detection needed since the client-side router handles subroutes. @ --------- Co-authored-by: Test <test@example.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
73a6a5376e
|
fix(cpp): thread call-site types into qualified member lookup (#1632) (#1810)
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
* fix(cpp): thread call-site types into qualified member lookup (#1632) Widen Callsite (arity optional, add argumentTypes) and add optional callsite?: Callsite to ScopeResolver.resolveQualifiedReceiverMember. receiver-bound-calls.ts passes the ReferenceSite through structurally; resolveCppQualifiedNamespaceMember forwards it to narrowOverloadCandidates along with cppConversionRank, enabling exact-type and conversion-rank disambiguation across inline-namespace children. Behavior change: - outer::foo(42) where v1 declares foo(int) and v2 declares foo(double) now resolves to v1::foo (was: 0 edges, conservatively suppressed). - Same-name same-normalized-signature (e.g. foo(int) vs foo(long)) still suppresses at 0 edges via isOverloadAmbiguousAfterNormalization. - ADL using-import path (resolveAdlCandidates) unchanged — passes no callsite, narrowing degrades to existing pass-through behavior. Closes #1632. Part of #1564. * fix(cpp): update legacy parity expected-failure list for #1632 - Remove stale expected-failure entry for old diff-sigs test name (test now expects 1 edge; legacy DAG also emits 1 edge) - Add entry for normalized-signature ambiguity (int vs long) test - Rename describe block from 'conservative suppress' to 'distinct signatures resolved via call-site types' Verified both modes: REGISTRY_PRIMARY_CPP=1: 241/241 passed REGISTRY_PRIMARY_CPP=0: 194 passed, 47 skipped, 0 failed |