diff --git a/gitnexus/hooks/claude/gitnexus-hook.cjs b/gitnexus/hooks/claude/gitnexus-hook.cjs index 7bfa150cd..ca7e89ca2 100755 --- a/gitnexus/hooks/claude/gitnexus-hook.cjs +++ b/gitnexus/hooks/claude/gitnexus-hook.cjs @@ -298,11 +298,12 @@ function handlePostToolUse(input) { // If HEAD matches last indexed commit, no reindex needed if (currentHead && currentHead === lastCommit) return; - const analyzeCmd = `npx gitnexus analyze${hadEmbeddings ? ' --embeddings' : ''}`; + const analyzeCmd = `gitnexus analyze${hadEmbeddings ? ' --embeddings' : ''}`; + const npxAnalyzeCmd = `npx gitnexus analyze${hadEmbeddings ? ' --embeddings' : ''}`; sendHookResponse( 'PostToolUse', `GitNexus index is stale (last indexed: ${lastCommit ? lastCommit.slice(0, 7) : 'never'}). ` + - `Run \`${analyzeCmd}\` to update the knowledge graph.`, + `Run \`${analyzeCmd}\` to update the knowledge graph, or \`${npxAnalyzeCmd}\` if the CLI is not installed.`, ); } diff --git a/gitnexus/src/cli/ai-context.ts b/gitnexus/src/cli/ai-context.ts index 41ba6c73e..d2e1ae4c8 100644 --- a/gitnexus/src/cli/ai-context.ts +++ b/gitnexus/src/cli/ai-context.ts @@ -119,7 +119,7 @@ function generateGitNexusContent( This project is indexed by GitNexus as **${projectName}**${noStats ? '' : ` (${stats.nodes || 0} symbols, ${stats.edges || 0} relationships, ${stats.processes || 0} execution flows)`}. Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. -> If any GitNexus tool warns the index is stale, run \`npx gitnexus analyze\` in terminal first. +> If any GitNexus tool warns the index is stale, run \`gitnexus analyze\` in terminal first (or \`npx gitnexus analyze\` if the CLI is not installed). ## Always Do diff --git a/gitnexus/src/mcp/resources.ts b/gitnexus/src/mcp/resources.ts index 88e7a99cc..4a9fb8475 100644 --- a/gitnexus/src/mcp/resources.ts +++ b/gitnexus/src/mcp/resources.ts @@ -340,7 +340,9 @@ async function getContextResource(backend: LocalBackend, repoName?: string): Pro lines.push(' - cypher: Raw graph queries'); lines.push(' - list_repos: Discover all indexed repositories'); lines.push(''); - lines.push('re_index: Run `npx gitnexus analyze` in terminal if data is stale'); + lines.push( + 're_index: Run `gitnexus analyze` in terminal if data is stale (or `npx gitnexus analyze` if the CLI is not installed)', + ); lines.push(''); lines.push('resources_available:'); lines.push(' - gitnexus://repos: All indexed repositories'); @@ -581,7 +583,7 @@ async function getSetupResource(backend: LocalBackend): Promise { const repos = await backend.listRepos(); if (repos.length === 0) { - return '# GitNexus\n\nNo repositories indexed. Run: `npx gitnexus analyze` in a repository.'; + return '# GitNexus\n\nNo repositories indexed. Run: `gitnexus analyze` in a repository (or `npx gitnexus analyze` if the CLI is not installed).'; } const sections: string[] = []; diff --git a/gitnexus/test/integration/hooks-e2e.test.ts b/gitnexus/test/integration/hooks-e2e.test.ts index 6d3b79172..2afa852ea 100644 --- a/gitnexus/test/integration/hooks-e2e.test.ts +++ b/gitnexus/test/integration/hooks-e2e.test.ts @@ -77,7 +77,10 @@ describe.each(HOOKS)('hooks e2e ($name)', ({ name, path: hookPath }) => { const output = parseHookOutput(result.stdout); expect(output).not.toBeNull(); expect(output!.additionalContext).toContain('stale'); - expect(output!.additionalContext).toContain('npx gitnexus analyze'); + expect(output!.additionalContext).toContain('Run `gitnexus analyze`'); + expect(output!.additionalContext).toContain( + '`npx gitnexus analyze` if the CLI is not installed', + ); }); it('stays silent when meta.json lastCommit matches HEAD', () => { diff --git a/gitnexus/test/unit/ai-context.test.ts b/gitnexus/test/unit/ai-context.test.ts index bbaecb932..2620699d9 100644 --- a/gitnexus/test/unit/ai-context.test.ts +++ b/gitnexus/test/unit/ai-context.test.ts @@ -57,6 +57,8 @@ describe('generateAIContextFiles', () => { const content = await fs.readFile(path.join(tmpDir, 'CLAUDE.md'), 'utf-8'); expect(content).toContain('If any GitNexus tool warns the index is stale'); + expect(content).toContain('run `gitnexus analyze`'); + expect(content).toContain('`npx gitnexus analyze` if the CLI is not installed'); expect(content).toContain('## Always Do'); expect(content).toContain('## Never Do'); expect(content).toContain('## Resources'); diff --git a/gitnexus/test/unit/resources.test.ts b/gitnexus/test/unit/resources.test.ts index ccd8461f1..86da22f58 100644 --- a/gitnexus/test/unit/resources.test.ts +++ b/gitnexus/test/unit/resources.test.ts @@ -188,6 +188,7 @@ describe('readResource', () => { const backend = createMockBackend({ repos: [] }); const result = await readResource('gitnexus://repos', backend); expect(result).toContain('No repositories indexed'); + expect(result).toContain('Run: gitnexus analyze'); }); it('routes gitnexus://setup to setup resource', async () => { @@ -211,6 +212,8 @@ describe('readResource', () => { const backend = createMockBackend({ repos: [] }); const result = await readResource('gitnexus://setup', backend); expect(result).toContain('No repositories indexed'); + expect(result).toContain('Run: `gitnexus analyze`'); + expect(result).toContain('`npx gitnexus analyze` if the CLI is not installed'); }); it('routes group contracts resource through backend', async () => {