Merge branch 'main' into fix/skill-evolution-gate

This commit is contained in:
Gergő Magyar 2026-09-04 20:02:51 +01:00 committed by GitHub
commit 8f08261d05
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 121 additions and 69 deletions

View file

@ -6,7 +6,7 @@
"plugins": [
{
"name": "gitnexus",
"version": "1.6.10",
"version": "1.6.11",
"source": {
"source": "local",
"path": "./gitnexus-claude-plugin"

View file

@ -11,7 +11,7 @@
"plugins": [
{
"name": "gitnexus",
"version": "1.6.10",
"version": "1.6.11",
"source": "./gitnexus-claude-plugin",
"description": "Code intelligence powered by a knowledge graph. Provides execution flow tracing, blast radius analysis, and augmented search across your codebase."
}

View file

@ -1,7 +1,7 @@
{
"name": "gitnexus",
"description": "Code intelligence powered by a knowledge graph. Provides execution flow tracing, blast radius analysis, and augmented search across your codebase.",
"version": "1.6.10",
"version": "1.6.11",
"author": {
"name": "GitNexus"
},

View file

@ -1,7 +1,7 @@
{
"name": "gitnexus",
"description": "Code intelligence powered by a knowledge graph. Provides execution flow tracing, blast radius analysis, and augmented search across your codebase.",
"version": "1.6.10",
"version": "1.6.11",
"skills": "./skills",
"mcpServers": "./.mcp.json",
"hooks": "./hooks/hooks.json",

View file

@ -2,7 +2,7 @@
"mcpServers": {
"gitnexus": {
"command": "npx",
"args": ["-y", "gitnexus@1.6.10", "mcp"]
"args": ["-y", "gitnexus@1.6.11", "mcp"]
}
}
}

View file

@ -2,7 +2,7 @@
"mcpServers": {
"gitnexus": {
"command": "npx",
"args": ["-y", "gitnexus@1.6.10", "mcp"]
"args": ["-y", "gitnexus@1.6.11", "mcp"]
}
}
}

View file

@ -2,7 +2,7 @@
"mcpServers": {
"gitnexus": {
"command": "npx",
"args": ["-y", "gitnexus@1.6.10", "mcp"]
"args": ["-y", "gitnexus@1.6.11", "mcp"]
}
}
}

View file

@ -2,7 +2,7 @@
"mcpServers": {
"gitnexus": {
"command": "npx",
"args": ["-y", "gitnexus@1.6.10", "mcp"]
"args": ["-y", "gitnexus@1.6.11", "mcp"]
}
}
}

View file

@ -2,7 +2,7 @@
"mcpServers": {
"gitnexus": {
"command": "npx",
"args": ["-y", "gitnexus@1.6.10", "mcp"]
"args": ["-y", "gitnexus@1.6.11", "mcp"]
}
}
}

View file

@ -2,7 +2,7 @@
"mcpServers": {
"gitnexus": {
"command": "npx",
"args": ["-y", "gitnexus@1.6.10", "mcp"]
"args": ["-y", "gitnexus@1.6.11", "mcp"]
}
}
}

View file

@ -2,7 +2,7 @@
"mcpServers": {
"gitnexus": {
"command": "npx",
"args": ["-y", "gitnexus@1.6.10", "mcp"]
"args": ["-y", "gitnexus@1.6.11", "mcp"]
}
}
}

View file

@ -2,7 +2,7 @@
"mcpServers": {
"gitnexus": {
"command": "npx",
"args": ["-y", "gitnexus@1.6.10", "mcp"]
"args": ["-y", "gitnexus@1.6.11", "mcp"]
}
}
}

View file

@ -2,7 +2,7 @@
"mcpServers": {
"gitnexus": {
"command": "npx",
"args": ["-y", "gitnexus@1.6.10", "mcp"]
"args": ["-y", "gitnexus@1.6.11", "mcp"]
}
}
}

View file

@ -2,7 +2,7 @@
"mcpServers": {
"gitnexus": {
"command": "npx",
"args": ["-y", "gitnexus@1.6.10", "mcp"]
"args": ["-y", "gitnexus@1.6.11", "mcp"]
}
}
}

View file

@ -4,6 +4,50 @@ All notable changes to GitNexus will be documented in this file.
## [Unreleased]
## [1.6.11] - 2026-09-04
### Added
- **Zig is a supported language** — functions, methods, structs/enums/unions, relative and `build.zig` / `build.zig.zon` imports, and CALLS including receiver-bound dispatch. `comptime`-false branches mark CALLS as `staticGated`. The grammar is an optional dependency, so a missing native binding skips `.zig` files instead of failing install (#1432, #3161)
- **Update notifications** — cache-first npm `latest` check with one stderr line on interactive CLI, a `doctor` line, one MCP process log, and a dismissible web banner from `/api/info`. Silent for npx, dev checkouts, and Docker; opt out with `GITNEXUS_NO_UPDATE_NOTIFIER` (#3175)
- **`gitnexus auto-sync`** — scheduled SSH clone/pull and analyze from `GITNEXUS_HOME/watch_config.yml`. `gitnexus watch` is reserved and prints the split with `analyze --watch` (#2493)
- **`analyze --watch`** — local incremental re-index with debounce, serialized writers, and last-good graph on failure (#3072)
- **`--no-parse-cache` forces a cold parser rebuild** on analyze (#3153)
- **Spring / JVM modeling** — vendor-suffix mapping annotations (#2883), messaging destinations (#3132), handler annotation arguments and template publishes (#3128), Kotlin decorator routes and config consumers (#3133, #3126), optional Actuator runtime import (#3107), `SpringContextUtil.getBeans` dynamic lookups (#2886), Lombok and Kotlin accessor synthesis (#2885), and Java static-wildcard / Kotlin star-import folding for route constants (#3110, #3059)
- **More contract and route surfaces** — AsyncAPI 3.x Destination nodes (#3140), wrapped-client HTTP consumers with leading-prefix template stripping (#3111), GraphQL cross-repo contracts (#3070), and PHP generated-client `Request(method, host + resourcePath)` consumers (#3079)
- **Wiki `grok` local CLI provider** (#3069)
- **Optional bearer auth on the `serve` MCP route** (#3100)
### Fixed
- **Web UI is built from `prepack`**, not on every `npm ci` (#3166)
- **`analyze --watch` no longer drops events** across a gitignore reload (#3159)
- **`detect_changes` does not call a zero-symbol result a clean tree** (#3138)
- **`includeTests` is honored** for C#, Java, Swift and PHP test paths (#2866)
- **Decorator routes connect to their handler function** (#2865)
- **Analyze no longer clobbers customized GitNexus skills** (#3124)
- **Generated agent block requires graph tools** on structural reads (#3125)
- **`group` degraded links, sync warnings and UID-only impact actually work** (#3113); Maven child coordinates parse independently of parent POMs (#3108); ambiguous sync names fail closed and `analyze --name` is honored (#3094)
- **Grep tool honors regex, `fileFilter`, and `caseSensitive`** in serve and the web UI (#3109)
- **Razor ViewComponent names bind to in-repo classes** (#3104)
- **Incremental analyze skips derived layers it can reuse** (#3016, #3102)
- **Parse-cache chunks are stable** and ParsedFile loads are cheaper (#3093)
- **MCP omitted `repo` resolves from cwd** (#3085)
- **`status` judges freshness by covered files**, not a dirty working tree (#3083)
- **`impact` File risk is comparable via shared axes** (#3075, #3082), repo-relative paths resolve through `filePath` (#3074, #3084), and scope-extraction omissions are reported (#3071)
- **Cursor hooks preserve quoted shell search patterns** (#2938)
### Performance
- **Six equivalent redundancies dropped** on the Java-scale emit path (#3129)
- **V8 sidecars plus hardlinked ParsedFile restore** (#3099)
### Chore / Dependencies
- **Transitive CVE patches** (#3095)
- **Major runtime bumps**`chokidar` 4 → 5 (#3117), `graphql` 16 → 17 (#3119)
- **Dependency bumps** across gitnexus (`js-yaml`, `qs`, `fast-uri`, `ignore`, `tsx`, `@types/node`, `onnxruntime-node`), gitnexus-web (`axios`, `mermaid`, Playwright, Vitest, Testing Library), and the CodeQL action group (#3115, #3118, #3135, #3141#3151, #3154, #3155, #3158)
## [1.6.10] - 2026-08-27
### Added

View file

@ -1,12 +1,12 @@
{
"name": "gitnexus",
"version": "1.6.10",
"version": "1.6.11",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "gitnexus",
"version": "1.6.10",
"version": "1.6.11",
"hasInstallScript": true,
"license": "PolyForm-Noncommercial-1.0.0",
"dependencies": {

View file

@ -1,6 +1,6 @@
{
"name": "gitnexus",
"version": "1.6.10",
"version": "1.6.11",
"description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
"author": "Abhigyan Patwari",
"license": "PolyForm-Noncommercial-1.0.0",

View file

@ -4,11 +4,8 @@
* pipe/JSON-safe (`gitnexus query … | jq`, `status --json`).
*/
import { createRequire } from 'node:module';
import type { Command } from 'commander';
const _require = createRequire(import.meta.url);
const pkg = _require('../../package.json') as { version?: unknown };
import { packageVersion } from '../core/package-version.js';
const SKIP_COMMAND_BANNER = new Set(['__update-check', 'help']);
@ -18,7 +15,7 @@ const SPECIAL_TITLES: Record<string, string> = {
};
export function installedCliVersion(): string {
return typeof pkg.version === 'string' ? pkg.version : '';
return packageVersion();
}
export function commandDisplayName(name: string): string {

View file

@ -4,7 +4,7 @@
// Removing it from here improves MCP server startup time significantly.
import { Command } from 'commander';
import { createRequire } from 'node:module';
import { packageVersion } from '../core/package-version.js';
import {
createAnalyzerLbugLazyAction,
createLazyAction,
@ -17,15 +17,13 @@ import { t } from './i18n/index.js';
import { writeCommandBanner } from './command-banner.js';
import { runProcessCliUpdateNotice } from './update-notice.js';
const _require = createRequire(import.meta.url);
const pkg = _require('../../package.json');
const program = new Command();
function collectCodingAgents(value: string, previous: string[] | undefined): string[] {
return [...(previous ?? []), ...value.split(',')];
}
program.name('gitnexus').description('GitNexus local CLI and MCP server').version(pkg.version);
program.name('gitnexus').description('GitNexus local CLI and MCP server').version(packageVersion());
program
.command('setup')
@ -521,5 +519,5 @@ program.hook('preAction', (_thisCommand, actionCommand) => {
writeCommandBanner(actionCommand);
});
runProcessCliUpdateNotice(typeof pkg.version === 'string' ? pkg.version : '');
runProcessCliUpdateNotice(packageVersion());
program.parse(process.argv);

View file

@ -10,10 +10,10 @@ import fs from 'fs/promises';
import path from 'path';
import os from 'os';
import { execFile, execFileSync } from 'child_process';
import { createRequire } from 'module';
import { promisify } from 'util';
import { fileURLToPath } from 'url';
import { parseTree, modify, applyEdits, ParseError, parse as parseJsonc } from 'jsonc-parser';
import { packageVersion } from '../core/package-version.js';
import { getGlobalDir } from '../storage/repo-manager.js';
import {
getEditorTargets,
@ -37,9 +37,8 @@ const execFileAsync = promisify(execFile);
// re-stamped every release by scripts/sync-plugin-manifests.mjs (#2445),
// since they too execute `gitnexus@<version>` on connect. Only the READMEs
// stay on `gitnexus@latest` — they're quickstart docs, not executed state.
const _require = createRequire(import.meta.url);
const _pkg = _require('../../package.json') as { version?: unknown };
if (typeof _pkg.version !== 'string' || !_pkg.version) {
const PKG_VERSION = packageVersion();
if (!PKG_VERSION) {
throw new Error(
'gitnexus/package.json#version is missing or not a string — cannot generate MCP fallback config.',
);
@ -47,7 +46,7 @@ if (typeof _pkg.version !== 'string' || !_pkg.version) {
// Version-pinned ref for the persisted MCP entry — deliberately distinct from
// the cjs's exported `gitnexus@latest` hint ref (resolve-analyze-cmd.cjs); the
// two are not unified (see the comment above and that file's MCP_PINNED_REF).
const MCP_PINNED_REF = `gitnexus@${_pkg.version}`;
const MCP_PINNED_REF = `gitnexus@${PKG_VERSION}`;
/**
* Build the `command` string written into an editor's hook settings, which the

View file

@ -5,16 +5,13 @@
*/
import { spawn } from 'node:child_process';
import { createRequire } from 'node:module';
import { homedir } from 'node:os';
import { composeWin32NpmCommand } from '../core/embeddings/runtime-install.js';
import { packageVersion } from '../core/package-version.js';
import { STRICT_UPDATE_VERSION } from '../core/update-cache.js';
import { refresh, type UpdateState } from '../core/update-check.js';
import { t } from './i18n/index.js';
const _require = createRequire(import.meta.url);
const pkg = _require('../../package.json') as { version?: unknown };
export const UPDATE_PACKAGE = 'gitnexus';
export interface UpdateCommandDependencies {
@ -30,7 +27,7 @@ export interface UpdateCommandDependencies {
}
function defaultInstalledVersion(): string {
return typeof pkg.version === 'string' ? pkg.version : '';
return packageVersion();
}
export function updateInstallArgs(version: string): string[] {

View file

@ -0,0 +1,12 @@
import { createRequire } from 'node:module';
const _require = createRequire(import.meta.url);
const pkg = _require('../../package.json') as { version?: unknown };
/**
* Published version from this package's `package.json`.
* Empty string if the field is missing or not a string.
*/
export function packageVersion(): string {
return typeof pkg.version === 'string' ? pkg.version : '';
}

View file

@ -1,11 +1,11 @@
import fs from 'node:fs/promises';
import { createRequire } from 'node:module';
import { getGlobalDir } from '../storage/global-dir.js';
import { writeFileAtomic } from '../storage/fs-atomic.js';
import { acquireFileLock, FileLockBusyError } from '../storage/file-lock.js';
import { validateGitUrl } from './net/url-guard.js';
import { updateEligibleInstall } from './install-context.js';
import { createLogger } from './logger.js';
import { packageVersion } from './package-version.js';
import {
isNewerVersion,
isUpdateCacheFresh,
@ -19,9 +19,6 @@ import {
type UpdateCacheEntry,
} from './update-cache.js';
const _require = createRequire(import.meta.url);
const pkg = _require('../../package.json') as { version?: unknown };
const FETCH_TIMEOUT_MS = 3_000;
const MAX_RESPONSE_BYTES = 64 * 1024;
const MAX_REDIRECTS = 5;
@ -31,7 +28,7 @@ const LOCK_BUSY_RETRY_JITTER_MS = 30_000;
const updateLogger = createLogger('update-check');
function defaultInstalledVersion(): string {
return typeof pkg.version === 'string' ? pkg.version : '';
return packageVersion();
}
function nextSchedulerDelay(entry: UpdateCacheEntry | null, now: number): number {

View file

@ -11,8 +11,8 @@
* Resources: repos, repo/{name}/context, repo/{name}/clusters, ...
*/
import { createRequire } from 'module';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { packageVersion } from '../core/package-version.js';
import { CompatibleStdioServerTransport } from './compatible-stdio-transport.js';
import {
CallToolRequestSchema,
@ -106,12 +106,10 @@ export function createMCPServer(
}
const repositoryPolicy = options.repositoryPolicy ?? McpRepositoryPolicy.unrestricted();
const scopedBackend = repositoryPolicy.scopeBackend(backend);
const require = createRequire(import.meta.url);
const pkgVersion: string = require('../../package.json').version;
const server = new Server(
{
name: 'gitnexus',
version: pkgVersion,
version: packageVersion(),
},
{
capabilities: {

View file

@ -1,9 +1,6 @@
import { createRequire } from 'node:module';
import { packageVersion } from '../core/package-version.js';
import { armUpdateRefreshScheduler, evaluate, type UpdateState } from '../core/update-check.js';
const _require = createRequire(import.meta.url);
const pkg = _require('../../package.json');
export interface ServerInfoResponse {
version: string;
launchContext: 'npx' | 'global' | 'local';
@ -92,7 +89,7 @@ export const buildServerInfo = (updateState: UpdateState | null): ServerInfoResp
}
return {
version: pkg.version,
version: packageVersion(),
launchContext,
nodeVersion: process.version,
...(updateState?.updateAvailable && updateState.latestVersion

View file

@ -7,8 +7,10 @@ import { afterEach, describe, expect, it } from 'vitest';
import { CLI_SPAWN_PREFIX, tsxLoaderUrl } from '../../helpers/cli-entry.js';
import { cleanupTempDirSync } from '../../helpers/test-db.js';
import { packageVersion } from '../../../src/core/package-version.js';
const repoRoot = path.resolve(import.meta.dirname, '../../..');
const installedVersion = packageVersion();
const tempDirs: string[] = [];
function tempHome(): string {
@ -218,7 +220,9 @@ globalThis.fetch = async () => {
},
);
expect(result.stderr).toContain('GitNexus 99.0.0 is available (you are running 1.6.10).');
expect(result.stderr).toContain(
`GitNexus 99.0.0 is available (you are running ${installedVersion}).`,
);
expect(result.stdout).not.toContain('99.0.0 is available');
});
});

View file

@ -0,0 +1,13 @@
import { createRequire } from 'node:module';
import { describe, expect, it } from 'vitest';
import { packageVersion } from '../../src/core/package-version.js';
const fromManifest = (createRequire(import.meta.url)('../../package.json') as { version: string })
.version;
describe('packageVersion', () => {
it('returns the same string as gitnexus/package.json#version', () => {
expect(packageVersion()).toBe(fromManifest);
expect(packageVersion()).toMatch(/^\d+\.\d+\.\d+/);
});
});

View file

@ -8,9 +8,11 @@ import {
buildServerInfo,
createServeUpdateController,
} from '../../src/server/update-controller.js';
import { packageVersion } from '../../src/core/package-version.js';
import { evaluate } from '../../src/core/update-check.js';
import type { UpdateState } from '../../src/core/update-check.js';
const PKG_VERSION = packageVersion();
const baseKeys = ['version', 'launchContext', 'nodeVersion'];
const tempDirs: string[] = [];
@ -27,7 +29,7 @@ describe('GET /api/info update state', () => {
expect(Object.keys(response)).toEqual(baseKeys);
expect(response).toEqual({
version: '1.6.10',
version: PKG_VERSION,
launchContext: expect.stringMatching(/^(npx|global|local)$/),
nodeVersion: process.version,
});
@ -35,7 +37,7 @@ describe('GET /api/info update state', () => {
it('adds optional update fields only for an available version', () => {
expect(buildServerInfo({ updateAvailable: true, latestVersion: '9.8.7' })).toEqual({
version: '1.6.10',
version: PKG_VERSION,
launchContext: expect.stringMatching(/^(npx|global|local)$/),
nodeVersion: process.version,
latestVersion: '9.8.7',
@ -43,7 +45,7 @@ describe('GET /api/info update state', () => {
});
expect(
Object.keys(buildServerInfo({ updateAvailable: false, latestVersion: '1.6.10' })),
Object.keys(buildServerInfo({ updateAvailable: false, latestVersion: PKG_VERSION })),
).toEqual(baseKeys);
});

View file

@ -20,11 +20,10 @@ import fs from 'fs/promises';
import os from 'os';
import path from 'path';
import { spawnSync } from 'child_process';
import { createRequire } from 'module';
import { commitAll, initGitRepo } from '../helpers/temp-git-repo.js';
import { packageVersion } from '../../src/core/package-version.js';
const PKG_VERSION = (createRequire(import.meta.url)('../../package.json') as { version: string })
.version;
const PKG_VERSION = packageVersion();
const NPX_REF = `gitnexus@${PKG_VERSION}`;
// vi.hoisted lets the mock factory below (which is hoisted by Vitest) see

View file

@ -2,10 +2,9 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import fs from 'fs/promises';
import os from 'os';
import path from 'path';
import { createRequire } from 'module';
import { packageVersion } from '../../src/core/package-version.js';
const PKG_VERSION = (createRequire(import.meta.url)('../../package.json') as { version: string })
.version;
const PKG_VERSION = packageVersion();
const NPX_REF = `gitnexus@${PKG_VERSION}`;
const execFileMock = vi.fn((...args: any[]) => {

View file

@ -3,10 +3,9 @@ import fs from 'fs/promises';
import os from 'os';
import path from 'path';
import { parse as parseJsonc } from 'jsonc-parser';
import { createRequire } from 'module';
import { packageVersion } from '../../src/core/package-version.js';
const PKG_VERSION = (createRequire(import.meta.url)('../../package.json') as { version: string })
.version;
const PKG_VERSION = packageVersion();
const NPX_REF = `gitnexus@${PKG_VERSION}`;
const execFileMock = vi.fn((...args: any[]) => {

View file

@ -2,12 +2,9 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import fs from 'fs/promises';
import os from 'os';
import path from 'path';
import { createRequire } from 'module';
import { packageVersion } from '../../src/core/package-version.js';
// Match what setup.ts emits — read the version from the same package.json
// so the test never goes stale on a release bump.
const PKG_VERSION = (createRequire(import.meta.url)('../../package.json') as { version: string })
.version;
const PKG_VERSION = packageVersion();
const MCP_PINNED_REF = `gitnexus@${PKG_VERSION}`;
/** Flatten the spied console.log calls into one searchable string. */