feat(cli): codegraph version command + complete CLI Reference (#864)

* feat(cli): codegraph version command + complete CLI Reference

Add a `codegraph version` subcommand plus the `-v` and `-version`
spellings (commander already wires up `--version`/`-V`), so the version
is easy to reach however a user guesses at it. The `-v`/`-version` forms
are intercepted before commander parses — its version short flag is the
capital `-V`, and its parser rejects a multi-character single-dash flag.
A trailing `-v` on a subcommand still means `--verbose`.

Document the previously-missing commands in the README CLI Reference:
`daemon`/`daemons`, `unlock`, `telemetry`, `version`, and `help`.

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

* docs(changelog): reference #864 on the version-command entry

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-06-13 14:14:32 -05:00
committed by GitHub
co-authored by Claude Opus 4.8
parent ff288ac711
commit 070ce4da2b
4 changed files with 106 additions and 0 deletions
+1
View File
@@ -12,6 +12,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
### New Features
- New `codegraph daemon` command (alias `daemons`) — an interactive manager for the background daemons. It shows what's running (your current project's daemon first, pre-selected), and you arrow-key to one and press enter to stop it, or pick "Stop all". Previously the only way to shut a daemon down was to hunt for its pid and `kill` it by hand. (#845)
- Checking your installed version is now easy to reach however you guess at it: `codegraph version`, `codegraph -v`, and `codegraph -version` all print it, alongside the existing `codegraph --version`. (#864)
- The CodeGraph MCP server now self-heals if its main thread ever locks up. A lightweight watchdog notices when the process has stopped responding and stops it so a fresh one starts on your next request — it can no longer sit pinned at 100% CPU with no way to recover. Tune the detection window with `CODEGRAPH_WATCHDOG_TIMEOUT_MS`, or turn it off entirely with `CODEGRAPH_NO_WATCHDOG=1`. (#850)
### Fixes
+5
View File
@@ -474,6 +474,7 @@ codegraph uninit [path] # Remove CodeGraph from a project (--force to
codegraph index [path] # Full index (--force to re-index, --quiet for less output)
codegraph sync [path] # Incremental update
codegraph status [path] # Show statistics
codegraph unlock [path] # Remove a stale lock file that's blocking indexing
codegraph query <search> # Search symbols (--kind, --limit, --json)
codegraph explore <query> # Relevant symbols' source + call paths in one shot (same output as the codegraph_explore MCP tool)
codegraph node <symbol|file> # One symbol's source + callers, or read a file with line numbers (same output as codegraph_node)
@@ -483,7 +484,11 @@ codegraph callees <symbol> # Find what a function/method calls (--limit,
codegraph impact <symbol> # Analyze what code is affected by changing a symbol (--depth, --json)
codegraph affected [files...] # Find test files affected by changes (see below)
codegraph serve --mcp # Start MCP server
codegraph daemon # Manage background daemons — pick one to stop (alias: daemons)
codegraph telemetry [on|off] # Show or change anonymous usage telemetry
codegraph upgrade [version] # Update to the latest release (--check, --force)
codegraph version # Print the installed version (also -v, --version)
codegraph help [command] # Show help, optionally for one command
```
### `codegraph affected`
+73
View File
@@ -0,0 +1,73 @@
/**
* Tests for the `codegraph version` affordances.
*
* The version should be reachable however a user reaches for it the bare
* `version` subcommand, lowercase `-v`, single-dash `-version`, plus
* commander's stock `--version` / `-V`. All of them print the exact
* package.json version and nothing else.
*
* Exercised end-to-end against the built binary (same approach as
* status-json.test.ts) so the spellings survive future CLI refactors.
*/
import { describe, it, expect } from 'vitest';
import { execFileSync } from 'child_process';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
const BIN = path.resolve(__dirname, '../dist/bin/codegraph.js');
const PKG_VERSION = JSON.parse(
fs.readFileSync(path.resolve(__dirname, '../package.json'), 'utf-8'),
).version as string;
function run(args: string[]): string {
return execFileSync(process.execPath, [BIN, ...args], {
encoding: 'utf-8',
// Skip the daemon and the wasm-flag re-exec so the command resolves in a
// single fast process (no graph work happens for a version print anyway).
env: { ...process.env, CODEGRAPH_NO_DAEMON: '1', CODEGRAPH_WASM_RELAUNCHED: '1' },
stdio: ['ignore', 'pipe', 'pipe'],
}).trim();
}
describe('codegraph version affordances', () => {
for (const spelling of ['version', '-v', '-version', '--version', '-V']) {
it(`\`codegraph ${spelling}\` prints exactly the package version`, () => {
expect(run([spelling])).toBe(PKG_VERSION);
});
}
it('lists the `version` subcommand in --help', () => {
expect(run(['--help'])).toContain('version');
});
it('`codegraph help` prints usage and the command list', () => {
const out = run(['help']);
expect(out).toContain('Usage: codegraph');
expect(out).toContain('Commands:');
});
it('a trailing `-v` is still the subcommand\'s --verbose, not the version intercept', () => {
// A fresh temp dir outside any indexed project: `index -v` parses `-v` as
// the index command's --verbose, then short-circuits at "not initialized"
// and exits non-zero. The point is it must NOT print the bare version,
// which would mean the top-level intercept swallowed a subcommand flag.
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-version-test-'));
let combined = '';
try {
combined = execFileSync(process.execPath, [BIN, 'index', '-v', tempDir], {
encoding: 'utf-8',
env: { ...process.env, CODEGRAPH_NO_DAEMON: '1', CODEGRAPH_WASM_RELAUNCHED: '1' },
stdio: ['ignore', 'pipe', 'pipe'],
});
} catch (err: unknown) {
const e = err as { stdout?: string; stderr?: string };
combined = `${e.stdout ?? ''}${e.stderr ?? ''}`;
} finally {
fs.rmSync(tempDir, { recursive: true, force: true });
}
expect(combined.trim()).not.toBe(PKG_VERSION);
expect(combined).toContain('not initialized');
});
});
+27
View File
@@ -120,6 +120,18 @@ const packageJson = JSON.parse(
fs.readFileSync(path.join(__dirname, '..', '..', 'package.json'), 'utf-8')
);
// Make the version trivial to reach. commander's `.version()` (below) wires up
// `--version` and `-V`; intercept the spellings it can't — lowercase `-v` and
// single-dash `-version` — before any parsing. (commander's version short flag
// is the capital `-V`, and its parser rejects a multi-character single-dash
// flag.) The bare `codegraph version` subcommand is registered further down so
// the affordance also shows up in `codegraph --help`.
const firstArg = process.argv[2];
if (firstArg === '-v' || firstArg === '-version') {
console.log(packageJson.version);
return;
}
// =============================================================================
// ANSI Color Helpers (avoid chalk ESM issues)
// =============================================================================
@@ -1978,6 +1990,21 @@ program
process.exit(code);
});
/**
* codegraph version
*
* The bare-noun form of `--version`. commander already provides `--version`
* and `-V`, and the `-v` / `-version` spellings are intercepted before parse
* (see top of main). This subcommand makes `codegraph version` work and lists
* the version affordance in `codegraph --help`.
*/
program
.command('version')
.description('Print the installed CodeGraph version (also: -v, --version)')
.action(() => {
console.log(packageJson.version);
});
// Parse and run
program.parse();