Przeglądaj źródła

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>
Colby Mchenry 1 tydzień temu
rodzic
commit
070ce4da2b
4 zmienionych plików z 106 dodań i 0 usunięć
  1. 1 0
      CHANGELOG.md
  2. 5 0
      README.md
  3. 73 0
      __tests__/cli-version.test.ts
  4. 27 0
      src/bin/codegraph.ts

+ 1 - 0
CHANGELOG.md

@@ -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 - 0
README.md

@@ -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 - 0
__tests__/cli-version.test.ts

@@ -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 - 0
src/bin/codegraph.ts

@@ -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();