gitnexushub 0.4.3 → 0.4.4

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.
@@ -16,9 +16,8 @@ function reportError(err) {
16
16
  process.exit(1);
17
17
  }
18
18
  export function registerWikiCommand(program) {
19
- const wiki = program.command('wiki').description('Client-side wiki generation and upload');
20
- wiki
21
- .command('upload')
19
+ const wiki = program
20
+ .command('wiki')
22
21
  .description('Generate the wiki locally with Claude Code and upload it to the Hub')
23
22
  .option('--mode <mode>', 'full | incremental', 'full')
24
23
  .option('--model <model>', 'Claude model to use', REAL_DEFAULT_MODEL)
@@ -28,14 +27,29 @@ export function registerWikiCommand(program) {
28
27
  if (opts.model)
29
28
  deps.model = opts.model;
30
29
  const ac = new AbortController();
30
+ let sigCount = 0;
31
+ let forceExitTimer = null;
31
32
  const onSig = () => {
32
- warn('interrupt received, aborting session...');
33
- ac.abort();
33
+ sigCount++;
34
+ if (sigCount === 1) {
35
+ warn('interrupt received — cleaning up (Ctrl+C again to force quit)...');
36
+ ac.abort();
37
+ forceExitTimer = setTimeout(() => {
38
+ warn('cleanup timed out, forcing exit');
39
+ process.exit(130);
40
+ }, 3000);
41
+ forceExitTimer.unref();
42
+ }
43
+ else {
44
+ if (forceExitTimer)
45
+ clearTimeout(forceExitTimer);
46
+ process.exit(130);
47
+ }
34
48
  };
35
49
  process.on('SIGINT', onSig);
36
50
  process.on('SIGTERM', onSig);
37
51
  try {
38
- info(`Resolving repo context...`);
52
+ info('Resolving repo context...');
39
53
  const result = await runWikiUpload({ cwd, mode: opts.mode, model: opts.model, abortSignal: ac.signal }, deps);
40
54
  ok(`Uploaded ${result.pagesPersisted} pages.`);
41
55
  if (result.failedSlugs.length > 0) {
@@ -43,9 +57,15 @@ export function registerWikiCommand(program) {
43
57
  }
44
58
  }
45
59
  catch (err) {
60
+ if (ac.signal.aborted) {
61
+ warn('aborted');
62
+ process.exit(130);
63
+ }
46
64
  reportError(err);
47
65
  }
48
66
  finally {
67
+ if (forceExitTimer)
68
+ clearTimeout(forceExitTimer);
49
69
  process.off('SIGINT', onSig);
50
70
  process.off('SIGTERM', onSig);
51
71
  }
@@ -4,7 +4,7 @@ export async function resolveWikiContext(deps, opts = {}) {
4
4
  const repoRoot = deps.getGitRoot(deps.cwd);
5
5
  if (!repoRoot) {
6
6
  throw new GnxError(ErrorCode.NOT_GIT_REPO, 'not a git repository', {
7
- hint: 'cd into your project, or run: gnx wiki upload <path>',
7
+ hint: 'cd into your project, or run: gnx wiki <path>',
8
8
  });
9
9
  }
10
10
  const headCommit = deps.getGitHead(repoRoot);
@@ -21,6 +21,7 @@ export interface RunSessionDeps {
21
21
  modules: ModuleNode[];
22
22
  moduleTree: unknown;
23
23
  generatePage: GeneratePageFn;
24
+ onSessionStart?: (sessionId: string) => void;
24
25
  onPageStart?: (slug: string) => void;
25
26
  onPageDone?: (slug: string, bytes: number) => void;
26
27
  onPageFail?: (slug: string, err: unknown) => void;
@@ -8,6 +8,7 @@ export async function runWikiUploadSession(deps) {
8
8
  clientModel: deps.clientModel,
9
9
  });
10
10
  const sessionId = started.sessionId;
11
+ deps.onSessionStart?.(sessionId);
11
12
  const receivedSlugs = [];
12
13
  const failedSlugs = [];
13
14
  let aborted = false;
@@ -1,6 +1,7 @@
1
1
  import { resolveWikiContext } from './resolve-context.js';
2
2
  import { runWikiUploadSession } from './session.js';
3
3
  import { GnxError, ErrorCode } from './errors.js';
4
+ import { info } from '../cli-helpers.js';
4
5
  function slugify(name) {
5
6
  return name
6
7
  .toLowerCase()
@@ -115,11 +116,13 @@ export async function runWikiUpload(opts, deps) {
115
116
  if (opts.abortSignal?.aborted)
116
117
  throw new GnxError(ErrorCode.USER_ABORTED, 'aborted before session start');
117
118
  const runner = deps.createClaudeRunner();
119
+ info(`Fetching graph context from Hub (${ctx.hubFullName})...`);
118
120
  const [prompts, groupingCtx] = await Promise.all([
119
121
  ctx.api.wikiPromptTemplates(ctx.hubRepoId),
120
122
  ctx.api.wikiGroupingContext(ctx.hubRepoId),
121
123
  ]);
122
124
  // Phase 1: Module tree
125
+ info('Phase 1/3: generating module tree with Claude Code...');
123
126
  const groupingPrompt = fillTemplate(prompts.grouping.user, {
124
127
  COMMUNITY_GROUPS: groupingCtx.communityGroups,
125
128
  INTER_COMMUNITY_EDGES: groupingCtx.interCommunityEdges,
@@ -138,6 +141,7 @@ export async function runWikiUpload(opts, deps) {
138
141
  const allPageNodes = [...leaves, ...parents];
139
142
  const moduleRegistry = allPageNodes.map((m) => `- [${m.title}](${m.slug}.md)`).join('\n');
140
143
  const generatedPages = new Map();
144
+ info(`Phase 2/3: generating ${leaves.length} leaf page(s) + ${parents.length} parent page(s)...`);
141
145
  if (opts.abortSignal?.aborted)
142
146
  throw new GnxError(ErrorCode.USER_ABORTED, 'aborted before page generation');
143
147
  // Phase 2a: Leaf pages
@@ -279,6 +283,19 @@ export async function runWikiUpload(opts, deps) {
279
283
  summary: m.summary,
280
284
  files: m.files,
281
285
  }));
286
+ info('Phase 3/3: streaming pages to Hub + generating overview...');
287
+ let activeSessionId = null;
288
+ const onAbort = async () => {
289
+ if (!activeSessionId)
290
+ return;
291
+ try {
292
+ await ctx.api.wikiUploadAbort(ctx.hubRepoId, activeSessionId);
293
+ }
294
+ catch {
295
+ /* ignore secondary errors */
296
+ }
297
+ };
298
+ opts.abortSignal?.addEventListener('abort', onAbort, { once: true });
282
299
  try {
283
300
  return await runWikiUploadSession({
284
301
  api: ctx.api,
@@ -290,6 +307,9 @@ export async function runWikiUpload(opts, deps) {
290
307
  modules: orderedModules,
291
308
  moduleTree,
292
309
  generatePage: generatePageByType,
310
+ onSessionStart: (id) => {
311
+ activeSessionId = id;
312
+ },
293
313
  });
294
314
  }
295
315
  catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexushub",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "Connect your editor to GitNexus Hub — one command MCP setup + project context",
5
5
  "author": "Abhigyan Patwari",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",