@gramatr/client 0.6.20 → 0.6.21

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/core/routing.ts CHANGED
@@ -47,6 +47,27 @@ export async function routePrompt(options: {
47
47
  };
48
48
  }
49
49
 
50
+ /**
51
+ * Fetch Packet 2 enrichment (reverse engineering + ISC scaffold).
52
+ * Called automatically by the prompt enricher hook when packet_2_status is "pending".
53
+ * Brief timeout — enrichment is usually pre-computed by the time we ask.
54
+ */
55
+ export async function fetchEnrichment(enrichmentId: string, timeoutMs: number = 2000): Promise<Record<string, unknown> | null> {
56
+ try {
57
+ const result = await callMcpToolDetailed<Record<string, unknown>>(
58
+ 'gramatr_get_enrichment',
59
+ { enrichment_id: enrichmentId, timeout_ms: timeoutMs },
60
+ timeoutMs + 1000, // HTTP timeout slightly longer than server timeout
61
+ );
62
+ if (result.data && result.data.status === 'ready') {
63
+ return result.data;
64
+ }
65
+ return null;
66
+ } catch {
67
+ return null;
68
+ }
69
+ }
70
+
50
71
  export function describeRoutingFailure(error: MctToolCallError): {
51
72
  title: string;
52
73
  detail: string;
@@ -25,6 +25,7 @@ import { getGitContext } from './lib/gmtr-hook-utils.ts';
25
25
  import {
26
26
  persistClassificationResult,
27
27
  routePrompt,
28
+ fetchEnrichment,
28
29
  shouldSkipPromptRouting,
29
30
  } from '../core/routing.ts';
30
31
  import type { RouteResponse } from '../core/types.ts';
@@ -155,7 +156,7 @@ function formatFailureWarning(failure: RouterFailure): string {
155
156
 
156
157
  // ── Format Intelligence Block ──
157
158
 
158
- function formatIntelligence(data: RouteResponse): string {
159
+ function formatIntelligence(data: RouteResponse, enrichment?: Record<string, unknown> | null): string {
159
160
  const c = data.classification || {};
160
161
  const ts = data.token_savings || {};
161
162
  const es = data.execution_summary || {};
@@ -450,6 +451,14 @@ function formatIntelligence(data: RouteResponse): string {
450
451
  }
451
452
  }
452
453
 
454
+ // Enrichment status — tell agent what happened with Packet 2
455
+ if (enrichment) {
456
+ // Enrichment was auto-fetched and merged — ISC scaffold + RE already in the output above
457
+ } else if (data.packet_2_status === 'pending' && data.enrichment_id) {
458
+ lines.push('');
459
+ lines.push(`Packet 2 (reverse engineering + ISC scaffold) is still generating. If needed, call gramatr_get_enrichment with enrichment_id="${data.enrichment_id}".`);
460
+ }
461
+
453
462
  return lines.join('\n');
454
463
  }
455
464
 
@@ -580,6 +589,29 @@ async function main() {
580
589
  lastFailure = null;
581
590
  }
582
591
 
592
+ // Auto-fetch Packet 2 enrichment if pending (reverse engineering + ISC scaffold)
593
+ // Brief wait — enrichment is usually pre-computed by the time Packet 1 returns.
594
+ // If it's not ready in 2s, inject what we have and tell the agent how to get it later.
595
+ let enrichment: Record<string, unknown> | null = null;
596
+ if (result && (result as any).packet_2_status === 'pending' && (result as any).enrichment_id) {
597
+ try {
598
+ enrichment = await fetchEnrichment((result as any).enrichment_id, 2000);
599
+ if (enrichment) {
600
+ // Merge enrichment into the classification so the existing formatting logic picks it up
601
+ const c = (result as any).classification;
602
+ if (c && enrichment.reverse_engineering) {
603
+ c.reverse_engineering = enrichment.reverse_engineering;
604
+ }
605
+ if (c && enrichment.isc_scaffold) {
606
+ c.isc_scaffold = enrichment.isc_scaffold;
607
+ }
608
+ if (enrichment.constraints_extracted) {
609
+ c.constraints_extracted = enrichment.constraints_extracted;
610
+ }
611
+ }
612
+ } catch { /* non-blocking — Packet 1 still delivers */ }
613
+ }
614
+
583
615
  // Emit status to stderr
584
616
  emitStatus(result, elapsed);
585
617
 
@@ -660,7 +692,7 @@ async function main() {
660
692
  }
661
693
 
662
694
  // Format and inject
663
- const context = formatIntelligence(result);
695
+ const context = formatIntelligence(result, enrichment);
664
696
 
665
697
  console.log(
666
698
  JSON.stringify({
@@ -482,8 +482,15 @@ async function main(): Promise<void> {
482
482
 
483
483
  // Opportunistic npm version check — prints a notification to stderr if a
484
484
  // newer gramatr is published. Never blocks or crashes the session start.
485
+ // Also injects context to stdout so Claude can proactively inform the user.
485
486
  try {
486
- await runVersionCheckAndNotify(VERSION);
487
+ const versionResult = await runVersionCheckAndNotify(VERSION);
488
+ if (versionResult?.isOutdated) {
489
+ emitStdout(`gramatr update available: v${versionResult.installedVersion} → v${versionResult.latestVersion}. ` +
490
+ `Inform the user that a gramatr update is available and recommend they exit Claude Code (/exit) ` +
491
+ `then run \`npx gramatr@latest install claude-code\` followed by \`claude --resume\` to get the latest features. ` +
492
+ `Hooks are loaded at session start so a restart is required for new hook code to take effect.`);
493
+ }
487
494
  } catch {
488
495
  // Silent — version check is strictly optional.
489
496
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gramatr/client",
3
- "version": "0.6.20",
3
+ "version": "0.6.21",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },