gitnexus 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -15,7 +15,7 @@ const program = new Command();
15
15
  program
16
16
  .name('gitnexus')
17
17
  .description('GitNexus local CLI and MCP server')
18
- .version('1.1.9');
18
+ .version('1.2.0');
19
19
  program
20
20
  .command('setup')
21
21
  .description('One-time setup: configure MCP for Cursor, Claude Code, OpenCode')
@@ -56,6 +56,7 @@ program
56
56
  .option('--model <model>', 'LLM model name (default: gpt-4o-mini)')
57
57
  .option('--base-url <url>', 'LLM API base URL (default: OpenAI)')
58
58
  .option('--api-key <key>', 'LLM API key (saved to ~/.gitnexus/config.json)')
59
+ .option('--gist', 'Publish wiki as a public GitHub Gist after generation')
59
60
  .action(wikiCommand);
60
61
  program
61
62
  .command('augment <pattern>')
@@ -9,5 +9,6 @@ export interface WikiCommandOptions {
9
9
  model?: string;
10
10
  baseUrl?: string;
11
11
  apiKey?: string;
12
+ gist?: boolean;
12
13
  }
13
14
  export declare const wikiCommand: (inputPath?: string, options?: WikiCommandOptions) => Promise<void>;
package/dist/cli/wiki.js CHANGED
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import path from 'path';
8
8
  import readline from 'readline';
9
+ import { execSync } from 'child_process';
9
10
  import cliProgress from 'cli-progress';
10
11
  import { getGitRoot, isGitRepo } from '../storage/git.js';
11
12
  import { getStoragePaths, loadMeta, loadCLIConfig, saveCLIConfig } from '../storage/repo-manager.js';
@@ -160,17 +161,19 @@ export const wikiCommand = async (inputPath, options) => {
160
161
  bar.update(100, { phase: 'Done' });
161
162
  bar.stop();
162
163
  const elapsed = ((Date.now() - t0) / 1000).toFixed(1);
164
+ const wikiDir = path.join(storagePath, 'wiki');
165
+ const viewerPath = path.join(wikiDir, 'index.html');
163
166
  if (result.mode === 'up-to-date' && !options?.force) {
164
167
  console.log('\n Wiki is already up to date.');
165
- console.log(` ${path.join(storagePath, 'wiki')}\n`);
168
+ console.log(` Viewer: ${viewerPath}\n`);
169
+ await maybePublishGist(viewerPath, options?.gist);
166
170
  return;
167
171
  }
168
- const wikiDir = path.join(storagePath, 'wiki');
169
172
  console.log(`\n Wiki generated successfully (${elapsed}s)\n`);
170
173
  console.log(` Mode: ${result.mode}`);
171
174
  console.log(` Pages: ${result.pagesGenerated}`);
172
175
  console.log(` Output: ${wikiDir}`);
173
- console.log(` Viewer: ${path.join(wikiDir, 'index.html')}`);
176
+ console.log(` Viewer: ${viewerPath}`);
174
177
  if (result.failedModules && result.failedModules.length > 0) {
175
178
  console.log(`\n Failed modules (${result.failedModules.length}):`);
176
179
  for (const mod of result.failedModules) {
@@ -179,6 +182,7 @@ export const wikiCommand = async (inputPath, options) => {
179
182
  console.log(' Re-run to retry failed modules (pages will be regenerated).');
180
183
  }
181
184
  console.log('');
185
+ await maybePublishGist(viewerPath, options?.gist);
182
186
  }
183
187
  catch (err) {
184
188
  bar.stop();
@@ -197,3 +201,75 @@ export const wikiCommand = async (inputPath, options) => {
197
201
  process.exitCode = 1;
198
202
  }
199
203
  };
204
+ // ─── Gist Publishing ───────────────────────────────────────────────────
205
+ function hasGhCLI() {
206
+ try {
207
+ execSync('gh --version', { stdio: 'ignore' });
208
+ return true;
209
+ }
210
+ catch {
211
+ return false;
212
+ }
213
+ }
214
+ function publishGist(htmlPath) {
215
+ try {
216
+ const output = execSync(`gh gist create "${htmlPath}" --desc "Repository Wiki — generated by GitNexus" --public`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
217
+ // gh gist create prints the gist URL as the last line
218
+ const lines = output.split('\n');
219
+ const gistUrl = lines.find(l => l.includes('gist.github.com')) || lines[lines.length - 1];
220
+ if (!gistUrl || !gistUrl.includes('gist.github.com'))
221
+ return null;
222
+ // Build a raw viewer URL via gist.githack.com
223
+ // gist URL format: https://gist.github.com/{user}/{id}
224
+ const match = gistUrl.match(/gist\.github\.com\/([^/]+)\/([a-f0-9]+)/);
225
+ let rawUrl = gistUrl;
226
+ if (match) {
227
+ rawUrl = `https://gistcdn.githack.com/${match[1]}/${match[2]}/raw/index.html`;
228
+ }
229
+ return { url: gistUrl.trim(), rawUrl };
230
+ }
231
+ catch {
232
+ return null;
233
+ }
234
+ }
235
+ async function maybePublishGist(htmlPath, gistFlag) {
236
+ if (gistFlag === false)
237
+ return;
238
+ // Check that the HTML file exists
239
+ try {
240
+ const fs = await import('fs/promises');
241
+ await fs.access(htmlPath);
242
+ }
243
+ catch {
244
+ return;
245
+ }
246
+ if (!hasGhCLI()) {
247
+ if (gistFlag) {
248
+ console.log(' GitHub CLI (gh) is not installed. Cannot publish gist.');
249
+ console.log(' Install it: https://cli.github.com\n');
250
+ }
251
+ return;
252
+ }
253
+ let shouldPublish = !!gistFlag;
254
+ if (!shouldPublish && process.stdin.isTTY) {
255
+ const answer = await new Promise((resolve) => {
256
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
257
+ rl.question(' Publish wiki as a GitHub Gist for easy viewing? (Y/n): ', (ans) => {
258
+ rl.close();
259
+ resolve(ans.trim().toLowerCase());
260
+ });
261
+ });
262
+ shouldPublish = !answer || answer === 'y' || answer === 'yes';
263
+ }
264
+ if (!shouldPublish)
265
+ return;
266
+ console.log('\n Publishing to GitHub Gist...');
267
+ const result = publishGist(htmlPath);
268
+ if (result) {
269
+ console.log(` Gist: ${result.url}`);
270
+ console.log(` Viewer: ${result.rawUrl}\n`);
271
+ }
272
+ else {
273
+ console.log(' Failed to publish gist. Make sure `gh auth login` is configured.\n');
274
+ }
275
+ }
@@ -50,6 +50,7 @@ export declare class WikiGenerator {
50
50
  mode: 'full' | 'incremental' | 'up-to-date';
51
51
  failedModules: string[];
52
52
  }>;
53
+ private ensureHTMLViewer;
53
54
  private fullGeneration;
54
55
  private buildModuleTree;
55
56
  /**
@@ -51,6 +51,8 @@ export class WikiGenerator {
51
51
  const forceMode = this.options.force;
52
52
  // Up-to-date check (skip if --force)
53
53
  if (!forceMode && existingMeta && existingMeta.fromCommit === currentCommit) {
54
+ // Still regenerate the HTML viewer in case it's missing
55
+ await this.ensureHTMLViewer();
54
56
  return { pagesGenerated: 0, mode: 'up-to-date', failedModules: [] };
55
57
  }
56
58
  // Force mode: delete snapshot to force full re-grouping
@@ -85,14 +87,21 @@ export class WikiGenerator {
85
87
  finally {
86
88
  await closeWikiDb();
87
89
  }
88
- // Generate self-contained HTML viewer
89
- if (result.pagesGenerated > 0) {
90
- this.onProgress('html', 98, 'Building HTML viewer...');
91
- const repoName = path.basename(this.repoPath);
92
- await generateHTMLViewer(this.wikiDir, repoName);
93
- }
90
+ // Always generate the HTML viewer after wiki content changes
91
+ await this.ensureHTMLViewer();
94
92
  return result;
95
93
  }
94
+ // ─── HTML Viewer ─────────────────────────────────────────────────────
95
+ async ensureHTMLViewer() {
96
+ // Only generate if there are markdown pages to bundle
97
+ const dirEntries = await fs.readdir(this.wikiDir).catch(() => []);
98
+ const hasMd = dirEntries.some(f => f.endsWith('.md'));
99
+ if (!hasMd)
100
+ return;
101
+ this.onProgress('html', 98, 'Building HTML viewer...');
102
+ const repoName = path.basename(this.repoPath);
103
+ await generateHTMLViewer(this.wikiDir, repoName);
104
+ }
96
105
  // ─── Full Generation ────────────────────────────────────────────────
97
106
  async fullGeneration(currentCommit) {
98
107
  let pagesGenerated = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
5
5
  "author": "Abhigyan Patwari",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",