@gramatr/client 0.5.1 → 0.6.0

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.
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Three modes:
6
6
  * 1. Interactive prompt: gramatr add-api-key
7
- * 2. Piped stdin: echo "gmtr_sk_..." | gramatr add-api-key
7
+ * 2. Piped stdin: echo "gramatr_sk_..." | gramatr add-api-key
8
8
  * 3. Env-sourced: gramatr add-api-key --from-env GRAMATR_API_KEY
9
9
  *
10
10
  * The key is validated against the gramatr server before being written
@@ -22,11 +22,11 @@ import { createInterface } from "readline";
22
22
  function gmtrJsonPath(): string {
23
23
  return join(process.env.HOME || process.env.USERPROFILE || homedir(), ".gmtr.json");
24
24
  }
25
- const SERVER_BASE = (process.env.GMTR_URL || "https://api.gramatr.com").replace(/\/mcp\/?$/, "");
25
+ const SERVER_BASE = (process.env.GRAMATR_URL || "https://api.gramatr.com").replace(/\/mcp\/?$/, "");
26
26
 
27
- // Accept gmtr_sk_, gmtr_pk_, aios_sk_, aios_pk_ (legacy), and Firebase-style
27
+ // Accept gramatr_sk_, gramatr_pk_, aios_sk_, aios_pk_ (legacy), and Firebase-style
28
28
  // long opaque tokens (length >= 32, base64url-ish characters).
29
- const KEY_FORMAT = /^(gmtr|aios)_(sk|pk)_[A-Za-z0-9_-]+$/;
29
+ const KEY_FORMAT = /^(gramatr|gmtr|aios)_(sk|pk)_[A-Za-z0-9_-]+$/;
30
30
  const LEGACY_OPAQUE = /^[A-Za-z0-9_.-]{32,}$/;
31
31
 
32
32
  function log(msg: string = ""): void {
@@ -63,7 +63,7 @@ function showHelp(): void {
63
63
 
64
64
  Usage:
65
65
  gramatr add-api-key Interactive prompt for the key
66
- echo "gmtr_sk_..." | gramatr add-api-key Read key from piped stdin
66
+ echo "gramatr_sk_..." | gramatr add-api-key Read key from piped stdin
67
67
  gramatr add-api-key --from-env VAR Read key from named env variable
68
68
  gramatr add-api-key --force Skip server validation (offline use)
69
69
 
@@ -94,7 +94,7 @@ async function readPipedStdin(): Promise<string | null> {
94
94
  async function readInteractive(): Promise<string> {
95
95
  log("");
96
96
  log("Paste your gramatr API key below.");
97
- log("(Get one at https://gramatr.com/settings — keys start with gmtr_sk_)");
97
+ log("(Get one at https://gramatr.com/settings — keys start with gramatr_sk_)");
98
98
  log("");
99
99
  process.stdout.write(" Key: ");
100
100
  const rl = createInterface({ input: process.stdin, output: process.stdout });
@@ -168,7 +168,7 @@ function writeKey(key: string): void {
168
168
  }
169
169
  existing.token = key;
170
170
  existing.token_type =
171
- key.startsWith("gmtr_sk_") || key.startsWith("aios_sk_") ? "api_key" : "oauth";
171
+ key.startsWith("gramatr_sk_") || key.startsWith("aios_sk_") ? "api_key" : "oauth";
172
172
  existing.authenticated_at = new Date().toISOString();
173
173
  writeFileSync(gmtrJsonPath(), `${JSON.stringify(existing, null, 2)}\n`, "utf8");
174
174
  try {
@@ -214,7 +214,7 @@ export async function main(argv: string[] = process.argv.slice(2)): Promise<numb
214
214
 
215
215
  // Format validation
216
216
  if (!validateFormat(key)) {
217
- err("ERROR: key format is invalid. Expected gmtr_sk_... or gmtr_pk_...");
217
+ err("ERROR: key format is invalid. Expected gramatr_sk_... or gramatr_pk_...");
218
218
  return 1;
219
219
  }
220
220
 
@@ -14,8 +14,8 @@ function main(): void {
14
14
  }
15
15
 
16
16
  const apply = process.argv.includes('--apply');
17
- const includeOptionalUx = process.env.GMTR_ENABLE_OPTIONAL_CLAUDE_UX === '1';
18
- const clientDir = process.env.GMTR_DIR || join(home, 'gmtr-client');
17
+ const includeOptionalUx = process.env.GRAMATR_ENABLE_OPTIONAL_CLAUDE_UX === '1';
18
+ const clientDir = process.env.GRAMATR_DIR || join(home, 'gmtr-client');
19
19
  runLegacyMigration({
20
20
  homeDir: home,
21
21
  clientDir,
@@ -10,7 +10,7 @@
10
10
  * 1. ~/.gmtr.json — deleted entirely
11
11
  * 2. ~/gmtr-client/settings.json `auth.api_key` — stripped, file preserved
12
12
  *
13
- * Env vars (GRAMATR_API_KEY, GMTR_TOKEN) cannot be cleared from a child
13
+ * Env vars (GRAMATR_API_KEY, GRAMATR_TOKEN) cannot be cleared from a child
14
14
  * process — they live in the parent shell. We detect them and warn.
15
15
  *
16
16
  * Not-logged-in is not an error: exits 0 with a clean message.
@@ -28,7 +28,7 @@ function gmtrJsonPath(): string {
28
28
  }
29
29
 
30
30
  function legacySettingsPath(): string {
31
- const gmtrDir = process.env.GMTR_DIR || join(getHome(), "gmtr-client");
31
+ const gmtrDir = process.env.GRAMATR_DIR || join(getHome(), "gmtr-client");
32
32
  return join(gmtrDir, "settings.json");
33
33
  }
34
34
 
@@ -44,7 +44,7 @@ Usage:
44
44
 
45
45
  After running, the next install or login will be forced through OAuth.
46
46
 
47
- Env vars (GRAMATR_API_KEY, GMTR_TOKEN) cannot be unset from this process.
47
+ Env vars (GRAMATR_API_KEY, GRAMATR_TOKEN) cannot be unset from this process.
48
48
  If they are set, this command warns and tells you the shell command to run.`);
49
49
  }
50
50
 
@@ -86,7 +86,7 @@ export function clearAll(): ClearResult {
86
86
  }
87
87
 
88
88
  // 3. Env vars — detect only, cannot mutate parent shell
89
- for (const v of ["GRAMATR_API_KEY", "GMTR_TOKEN"]) {
89
+ for (const v of ["GRAMATR_API_KEY", "GRAMATR_TOKEN"]) {
90
90
  if (process.env[v]) result.envVarsSet.push(v);
91
91
  }
92
92
 
package/bin/gmtr-login.ts CHANGED
@@ -24,7 +24,7 @@ import { createServer, type IncomingMessage, type ServerResponse } from 'http';
24
24
 
25
25
  const HOME = process.env.HOME || process.env.USERPROFILE || '';
26
26
  const CONFIG_PATH = join(HOME, '.gmtr.json');
27
- const DEFAULT_SERVER = process.env.GMTR_URL || 'https://api.gramatr.com/mcp';
27
+ const DEFAULT_SERVER = process.env.GRAMATR_URL || 'https://api.gramatr.com/mcp';
28
28
  // Strip /mcp suffix to get base URL
29
29
  const SERVER_BASE = DEFAULT_SERVER.replace(/\/mcp\/?$/, '');
30
30
  const DASHBOARD_BASE = process.env.GMTR_DASHBOARD_URL || (() => {
@@ -327,7 +327,7 @@ async function loginWithToken(token: string): Promise<void> {
327
327
  if (result.valid) {
328
328
  const config = readConfig();
329
329
  config.token = token;
330
- config.token_type = token.startsWith('aios_sk_') || token.startsWith('gmtr_sk_') ? 'api_key' : 'oauth';
330
+ config.token_type = token.startsWith('aios_sk_') || token.startsWith('gramatr_sk_') ? 'api_key' : 'oauth';
331
331
  config.authenticated_at = new Date().toISOString();
332
332
  writeConfig(config);
333
333
  console.log(' ✓ Token valid. Saved to ~/.gmtr.json');
@@ -539,7 +539,7 @@ export async function main(): Promise<void> {
539
539
  if (!token) {
540
540
  // Interactive paste mode — like Claude's login
541
541
  console.log('\n Paste your gramatr token below.');
542
- console.log(' (API keys start with aios_sk_ or gmtr_sk_)\n');
542
+ console.log(' (API keys start with aios_sk_ or gramatr_sk_)\n');
543
543
  process.stdout.write(' Token: ');
544
544
 
545
545
  const { createInterface } = await import('readline');
package/bin/gramatr.ts CHANGED
@@ -205,11 +205,11 @@ function installTarget(targetId: IntegrationTargetId): void {
205
205
 
206
206
  function migrate(apply: boolean, deep: boolean = false): void {
207
207
  const homeDir = homedir();
208
- const clientDir = process.env.GMTR_DIR || join(homeDir, 'gmtr-client');
208
+ const clientDir = process.env.GRAMATR_DIR || join(homeDir, 'gmtr-client');
209
209
  runLegacyMigration({
210
210
  homeDir,
211
211
  clientDir,
212
- includeOptionalUx: process.env.GMTR_ENABLE_OPTIONAL_CLAUDE_UX === '1',
212
+ includeOptionalUx: process.env.GRAMATR_ENABLE_OPTIONAL_CLAUDE_UX === '1',
213
213
  apply,
214
214
  log,
215
215
  });
@@ -220,7 +220,7 @@ function migrate(apply: boolean, deep: boolean = false): void {
220
220
  }
221
221
 
222
222
  function doctor(): void {
223
- const gmtrDir = process.env.GMTR_DIR || join(homedir(), 'gmtr-client');
223
+ const gmtrDir = process.env.GRAMATR_DIR || join(homedir(), 'gmtr-client');
224
224
  const stale = findStaleArtifacts(homedir(), gmtrDir, existsSync);
225
225
  for (const line of formatDoctorLines(detectTargets(), gmtrDir, existsSync(gmtrDir), stale)) log(line);
226
226
  renderRemoteGuidance();
@@ -228,14 +228,14 @@ function doctor(): void {
228
228
 
229
229
  function upgrade(): void {
230
230
  const homeDir = homedir();
231
- const clientDir = process.env.GMTR_DIR || join(homeDir, 'gmtr-client');
231
+ const clientDir = process.env.GRAMATR_DIR || join(homeDir, 'gmtr-client');
232
232
  const stale = findStaleArtifacts(homeDir, clientDir, existsSync);
233
233
  if (stale.length > 0) {
234
234
  log('Cleaning stale legacy artifacts before upgrade...');
235
235
  runLegacyMigration({
236
236
  homeDir,
237
237
  clientDir,
238
- includeOptionalUx: process.env.GMTR_ENABLE_OPTIONAL_CLAUDE_UX === '1',
238
+ includeOptionalUx: process.env.GRAMATR_ENABLE_OPTIONAL_CLAUDE_UX === '1',
239
239
  apply: true,
240
240
  log,
241
241
  });
package/bin/install.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * Usage:
7
7
  * npx tsx install.ts # Universal (Node + tsx)
8
8
  * bun install.ts # If bun is available
9
- * GMTR_TOKEN=xxx npx tsx install.ts # Headless with token
9
+ * GRAMATR_TOKEN=xxx npx tsx install.ts # Headless with token
10
10
  * npx tsx install.ts --yes # Non-interactive (auto-accept defaults)
11
11
  */
12
12
 
@@ -284,19 +284,43 @@ function installClientFiles(): void {
284
284
  copyFileIfExists(join(SCRIPT_DIR, 'bin/render-claude-hooks.ts'), join(CLIENT_DIR, 'bin/render-claude-hooks.ts'), true);
285
285
 
286
286
  // Bin lib helpers (statusline shim depends on these — see #495)
287
- // Copies git.ts, config.ts, stdin.ts and any future helpers without enumerating each file.
287
+ // Walks the directory but filters out test files / __tests__ subdir so
288
+ // we don't ship .test.ts to user installs.
288
289
  const binLibSrc = join(SCRIPT_DIR, 'bin', 'lib');
289
290
  if (existsSync(binLibSrc)) {
290
- copyDir(binLibSrc, join(CLIENT_DIR, 'bin', 'lib'));
291
- log('OK Installed bin/lib (statusline shim helpers)');
291
+ mkdirSync(join(CLIENT_DIR, 'bin', 'lib'), { recursive: true });
292
+ let binLibCount = 0;
293
+ for (const f of readdirSync(binLibSrc)) {
294
+ const src = join(binLibSrc, f);
295
+ if (!statSync(src).isFile()) continue;
296
+ if (!f.endsWith('.ts')) continue;
297
+ if (f.endsWith('.test.ts')) continue;
298
+ copyFileIfExists(src, join(CLIENT_DIR, 'bin', 'lib', f));
299
+ binLibCount++;
300
+ }
301
+ log(`OK Installed bin/lib (${binLibCount} statusline shim helpers)`);
292
302
  }
293
303
  log('OK Installed bin (statusline, gmtr-login, render-claude-hooks)');
294
304
 
295
- // Core dependencies (routing.ts required by GMTRPromptEnricher hook)
296
- for (const f of ['install.ts', 'version.ts', 'types.ts', 'routing.ts']) {
297
- copyFileIfExists(join(SCRIPT_DIR, 'core', f), join(CLIENT_DIR, 'core', f));
305
+ // Core dependencies directory walk so new hook deps don't silently break.
306
+ // Hook imports include: routing.ts (enricher), session.ts (session-start),
307
+ // version-check.ts (session-start), version.ts (session-start), types.ts (transitive).
308
+ // Past bugs (#495 bin/lib, this same class repeated): hardcoded file lists drift
309
+ // when a hook grows a new core import. Walking the directory + filtering tests
310
+ // makes the installer self-healing.
311
+ const coreSrc = join(SCRIPT_DIR, 'core');
312
+ if (existsSync(coreSrc)) {
313
+ let coreCount = 0;
314
+ for (const f of readdirSync(coreSrc)) {
315
+ const src = join(coreSrc, f);
316
+ if (!statSync(src).isFile()) continue;
317
+ if (!f.endsWith('.ts')) continue;
318
+ if (f.endsWith('.test.ts')) continue;
319
+ copyFileIfExists(src, join(CLIENT_DIR, 'core', f));
320
+ coreCount++;
321
+ }
322
+ log(`OK Installed ${coreCount} core modules`);
298
323
  }
299
- log('OK Installed core modules');
300
324
 
301
325
  // package.json — copied so core/version.ts can resolve the installed
302
326
  // version at runtime. Single source of truth; see core/version.ts.
@@ -484,15 +508,15 @@ function updateClaudeSettings(url: string, token: string): void {
484
508
  cpSync(CLAUDE_SETTINGS, backup);
485
509
  log(`OK Backed up settings to ${basename(backup)}`);
486
510
 
487
- const includeOptionalUx = process.env.GMTR_ENABLE_OPTIONAL_CLAUDE_UX === '1';
511
+ const includeOptionalUx = process.env.GRAMATR_ENABLE_OPTIONAL_CLAUDE_UX === '1';
488
512
  const hooksConfig = buildClaudeHooksFile(CLIENT_DIR, { includeOptionalUx });
489
513
 
490
514
  const settings = readJson(CLAUDE_SETTINGS);
491
515
 
492
516
  // Env vars
493
517
  settings.env = settings.env || {};
494
- settings.env.GMTR_DIR = CLIENT_DIR;
495
- settings.env.GMTR_URL = url;
518
+ settings.env.GRAMATR_DIR = CLIENT_DIR;
519
+ settings.env.GRAMATR_URL = url;
496
520
  settings.env.PATH = `${HOME}/.gmtr/bin:/usr/local/bin:/usr/bin:/bin`;
497
521
  if (token) settings.env.AIOS_MCP_TOKEN = token;
498
522
 
@@ -515,7 +539,7 @@ function updateClaudeSettings(url: string, token: string): void {
515
539
  if (includeOptionalUx) {
516
540
  log('OK Optional Claude UX hooks enabled');
517
541
  } else {
518
- log('OK Thin-client hook set (set GMTR_ENABLE_OPTIONAL_CLAUDE_UX=1 for optional hooks)');
542
+ log('OK Thin-client hook set (set GRAMATR_ENABLE_OPTIONAL_CLAUDE_UX=1 for optional hooks)');
519
543
  }
520
544
 
521
545
  // Status line
@@ -561,14 +585,14 @@ function registerMcpServer(url: string, token: string): void {
561
585
  // Register in ~/.claude.json
562
586
  mergeJson(CLAUDE_JSON, (data) => {
563
587
  data.env = data.env || {};
564
- if (token) data.env.GMTR_TOKEN = token;
588
+ if (token) data.env.GRAMATR_TOKEN = token;
565
589
  delete data.env.AIOS_MCP_TOKEN;
566
590
 
567
591
  data.mcpServers = data.mcpServers || {};
568
592
  data.mcpServers.gramatr = {
569
593
  type: 'http',
570
594
  url,
571
- headers: { Authorization: 'Bearer ${GMTR_TOKEN}' },
595
+ headers: { Authorization: 'Bearer ${GRAMATR_TOKEN}' },
572
596
  autoApprove: true,
573
597
  };
574
598
  return data;
@@ -594,16 +618,26 @@ function verify(url: string, token: string): boolean {
594
618
  }
595
619
  };
596
620
 
597
- // Critical files
621
+ // Critical files — every runtime hook + bin import target must be present.
622
+ // If you add a new ../core/X.ts or ./lib/X.ts import in hooks/ or bin/, add the
623
+ // corresponding entry here so a partial install fails loudly instead of
624
+ // exploding at hook execution time. The hook-imports-shipped.test.ts
625
+ // regression guard catches missing entries at PR review.
598
626
  for (const f of [
599
- 'hooks/GMTRToolTracker.hook.ts',
600
- 'hooks/GMTRPromptEnricher.hook.ts',
601
- 'hooks/GMTRRatingCapture.hook.ts',
602
- 'hooks/GMTRSecurityValidator.hook.ts',
627
+ 'hooks/gramatr-tool-tracker.hook.ts',
628
+ 'hooks/gramatr-prompt-enricher.hook.ts',
629
+ 'hooks/gramatr-rating-capture.hook.ts',
630
+ 'hooks/gramatr-security-validator.hook.ts',
603
631
  'hooks/lib/notify.ts',
604
632
  'core/routing.ts',
633
+ 'core/session.ts',
634
+ 'core/version.ts',
635
+ 'core/version-check.ts',
636
+ 'core/types.ts',
637
+ 'core/install.ts',
605
638
  'bin/statusline.ts',
606
639
  'bin/gmtr-login.ts',
640
+ 'bin/render-claude-hooks.ts',
607
641
  'bin/lib/git.ts',
608
642
  'bin/lib/config.ts',
609
643
  'bin/lib/stdin.ts',
package/bin/lib/config.ts CHANGED
@@ -8,8 +8,8 @@
8
8
  * so it can be loaded in a 2s shim without touching disk more than once.
9
9
  *
10
10
  * Resolution:
11
- * URL — GMTR_URL → ~/.gmtr.json.url → https://api.gramatr.com
12
- * Token — GMTR_TOKEN → AIOS_MCP_TOKEN → ~/.gmtr.json.token → null
11
+ * URL — GRAMATR_URL → ~/.gmtr.json.url → https://api.gramatr.com
12
+ * Token — GRAMATR_TOKEN → AIOS_MCP_TOKEN → ~/.gmtr.json.token → null
13
13
  */
14
14
  import { existsSync, readFileSync } from 'fs';
15
15
  import { homedir } from 'os';
@@ -40,15 +40,15 @@ export function getGramatrConfig(): GramatrConfig {
40
40
  const json = readGmtrJson();
41
41
 
42
42
  const rawUrl =
43
- process.env.GMTR_URL ||
43
+ process.env.GRAMATR_URL ||
44
44
  (typeof json?.url === 'string' && json.url) ||
45
45
  DEFAULT_URL;
46
- // GMTR_URL is conventionally the MCP endpoint (e.g. https://api.gramatr.com/mcp).
46
+ // GRAMATR_URL is conventionally the MCP endpoint (e.g. https://api.gramatr.com/mcp).
47
47
  // REST calls need the API base — strip a trailing /mcp segment.
48
48
  const url = (rawUrl as string).replace(/\/mcp\/?$/, '');
49
49
 
50
50
  const token =
51
- process.env.GMTR_TOKEN ||
51
+ process.env.GRAMATR_TOKEN ||
52
52
  process.env.AIOS_MCP_TOKEN ||
53
53
  (typeof json?.token === 'string' && json.token) ||
54
54
  null;
@@ -8,7 +8,7 @@ function main(): void {
8
8
  throw new Error('Usage: render-claude-hooks.ts <client-dir>');
9
9
  }
10
10
 
11
- const includeOptionalUx = process.env.GMTR_ENABLE_OPTIONAL_CLAUDE_UX === '1';
11
+ const includeOptionalUx = process.env.GRAMATR_ENABLE_OPTIONAL_CLAUDE_UX === '1';
12
12
  const hooks = buildClaudeHooksFile(clientDir, { includeOptionalUx });
13
13
  process.stdout.write(`${JSON.stringify(hooks.hooks, null, 2)}\n`);
14
14
  }
package/bin/uninstall.ts CHANGED
@@ -53,7 +53,7 @@ function removeGramatrFromJson(filePath: string, keys: string[]): boolean {
53
53
  delete data[key];
54
54
  changed = true;
55
55
  }
56
- // Handle nested keys like "env.GMTR_TOKEN"
56
+ // Handle nested keys like "env.GRAMATR_TOKEN"
57
57
  if (key.includes('.')) {
58
58
  const [parent, child] = key.split('.');
59
59
  if (data[parent] && child in data[parent]) {
@@ -194,7 +194,7 @@ async function main(): Promise<void> {
194
194
 
195
195
  // 5. Remove gramatr from ~/.claude.json (MCP server + env vars)
196
196
  if (existsSync(claudeJson)) {
197
- const cleaned = removeGramatrFromJson(claudeJson, ['env.GMTR_TOKEN', 'env.AIOS_MCP_TOKEN']);
197
+ const cleaned = removeGramatrFromJson(claudeJson, ['env.GRAMATR_TOKEN', 'env.AIOS_MCP_TOKEN']);
198
198
  if (cleaned) log(' OK Removed gramatr MCP server + env vars from ~/.claude.json');
199
199
  }
200
200
 
@@ -64,7 +64,7 @@ async function main(): Promise<void> {
64
64
  // Step 2: Validate server connectivity
65
65
  log('');
66
66
  log('Step 2: Validating server connectivity...');
67
- const serverUrl = process.env.GMTR_URL || DEFAULT_MCP_URL;
67
+ const serverUrl = process.env.GRAMATR_URL || DEFAULT_MCP_URL;
68
68
  const serverReachable = await validateServer(serverUrl);
69
69
  if (serverReachable) {
70
70
  log(` OK Server reachable at ${serverUrl.replace(/\/mcp$/, '')}`);
package/codex/install.ts CHANGED
@@ -47,7 +47,7 @@ export function main(): void {
47
47
  throw new Error('HOME is not set');
48
48
  }
49
49
 
50
- const gmtrDir = process.env.GMTR_DIR || join(home, 'gmtr-client');
50
+ const gmtrDir = process.env.GRAMATR_DIR || join(home, 'gmtr-client');
51
51
  const codexHome = join(home, '.codex');
52
52
  const hooksPath = join(codexHome, 'hooks.json');
53
53
  const configPath = join(codexHome, 'config.toml');
package/core/auth.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * Resolution chain (first non-empty wins):
9
9
  * 1. GRAMATR_API_KEY env var
10
- * 2. GMTR_TOKEN env var (legacy)
10
+ * 2. GRAMATR_TOKEN env var (legacy)
11
11
  * 3. ~/.gmtr.json `token` field
12
12
  * 4. ~/gmtr-client/settings.json `auth.api_key` (legacy, skips placeholder)
13
13
  * 5. If interactive + TTY: spawn gmtr-login.ts (OAuth)
@@ -48,13 +48,15 @@ function gmtrJsonPath(): string {
48
48
  }
49
49
 
50
50
  function legacySettingsPath(): string {
51
- const gmtrDir = process.env.GMTR_DIR || join(getHome(), "gmtr-client");
51
+ const gmtrDir = process.env.GRAMATR_DIR || join(getHome(), "gmtr-client");
52
52
  return join(gmtrDir, "settings.json");
53
53
  }
54
54
 
55
55
  function tokenFromEnv(): string | null {
56
+ // GRAMATR_API_KEY is the canonical env var for long-lived API keys.
57
+ // Legacy GMTR_TOKEN slot was removed in v0.6.0; see #512 for the full
58
+ // auth architecture refactor (split API keys from OAuth refresh tokens).
56
59
  if (process.env.GRAMATR_API_KEY) return process.env.GRAMATR_API_KEY;
57
- if (process.env.GMTR_TOKEN) return process.env.GMTR_TOKEN;
58
60
  return null;
59
61
  }
60
62
 
@@ -88,7 +90,7 @@ function findGmtrLoginScript(): string | null {
88
90
  }
89
91
  // Fallback to installed client dir
90
92
  const installedCandidate = join(
91
- process.env.GMTR_DIR || join(getHome(), "gmtr-client"),
93
+ process.env.GRAMATR_DIR || join(getHome(), "gmtr-client"),
92
94
  "bin",
93
95
  "gmtr-login.ts",
94
96
  );
package/core/feedback.ts CHANGED
@@ -33,7 +33,7 @@ export async function submitPendingClassificationFeedback(
33
33
  }
34
34
 
35
35
  const result = await callMcpToolDetailed(
36
- 'gmtr_classification_feedback',
36
+ 'gramatr_classification_feedback',
37
37
  {
38
38
  timestamp: last.timestamp,
39
39
  was_correct: true,
package/core/install.ts CHANGED
@@ -27,13 +27,13 @@ interface ClaudeHookOptions {
27
27
  }
28
28
 
29
29
  const CLAUDE_HOOKS: HookSpec[] = [
30
- { event: 'PreToolUse', matcher: 'Bash', relativeCommand: 'hooks/GMTRSecurityValidator.hook.ts' },
31
- { event: 'PreToolUse', matcher: 'Edit', relativeCommand: 'hooks/GMTRSecurityValidator.hook.ts' },
32
- { event: 'PreToolUse', matcher: 'Write', relativeCommand: 'hooks/GMTRSecurityValidator.hook.ts' },
33
- { event: 'PreToolUse', matcher: 'Read', relativeCommand: 'hooks/GMTRSecurityValidator.hook.ts' },
34
- { event: 'PostToolUse', matcher: 'mcp__.*gramatr.*__', relativeCommand: 'hooks/GMTRToolTracker.hook.ts' },
35
- { event: 'UserPromptSubmit', relativeCommand: 'hooks/GMTRRatingCapture.hook.ts' },
36
- { event: 'UserPromptSubmit', relativeCommand: 'hooks/GMTRPromptEnricher.hook.ts' },
30
+ { event: 'PreToolUse', matcher: 'Bash', relativeCommand: 'hooks/gramatr-security-validator.hook.ts' },
31
+ { event: 'PreToolUse', matcher: 'Edit', relativeCommand: 'hooks/gramatr-security-validator.hook.ts' },
32
+ { event: 'PreToolUse', matcher: 'Write', relativeCommand: 'hooks/gramatr-security-validator.hook.ts' },
33
+ { event: 'PreToolUse', matcher: 'Read', relativeCommand: 'hooks/gramatr-security-validator.hook.ts' },
34
+ { event: 'PostToolUse', matcher: 'mcp__.*gramatr.*__', relativeCommand: 'hooks/gramatr-tool-tracker.hook.ts' },
35
+ { event: 'UserPromptSubmit', relativeCommand: 'hooks/gramatr-rating-capture.hook.ts' },
36
+ { event: 'UserPromptSubmit', relativeCommand: 'hooks/gramatr-prompt-enricher.hook.ts' },
37
37
  { event: 'SessionStart', relativeCommand: 'hooks/session-start.hook.ts' },
38
38
  { event: 'SessionEnd', relativeCommand: 'hooks/session-end.hook.ts' },
39
39
  { event: 'Stop', relativeCommand: 'hooks/StopOrchestrator.hook.ts' },
package/core/routing.ts CHANGED
@@ -30,7 +30,7 @@ export async function routePrompt(options: {
30
30
  statuslineSize?: 'small' | 'medium' | 'large';
31
31
  }): Promise<{ route: RouteResponse | null; error: MctToolCallError | null }> {
32
32
  const result = await callMcpToolDetailed<RouteResponse>(
33
- 'gmtr_route_request',
33
+ 'gramatr_route_request',
34
34
  {
35
35
  prompt: options.prompt,
36
36
  ...(options.projectId ? { project_id: options.projectId } : {}),
@@ -79,7 +79,7 @@ export function describeRoutingFailure(error: MctToolCallError): {
79
79
  return {
80
80
  title: 'Routing request failed before intelligence could be injected.',
81
81
  detail: error.detail,
82
- action: `Inspect the response from ${resolveMcpUrl()} and the gmtr_route_request handler.`,
82
+ action: `Inspect the response from ${resolveMcpUrl()} and the gramatr_route_request handler.`,
83
83
  };
84
84
  }
85
85
  }
package/core/session.ts CHANGED
@@ -28,7 +28,7 @@ export interface CurrentProjectContextPayload {
28
28
  project_name: string;
29
29
  working_directory: string;
30
30
  session_start: string;
31
- gmtr_config_path: string;
31
+ gramatr_config_path: string;
32
32
  project_entity_id: string | null;
33
33
  action_required: string;
34
34
  project_id?: string;
@@ -114,7 +114,7 @@ export async function startRemoteSession(options: {
114
114
  directory: string;
115
115
  }): Promise<SessionStartResponse | null> {
116
116
  return (await callMcpTool(
117
- 'gmtr_session_start',
117
+ 'gramatr_session_start',
118
118
  {
119
119
  client_type: options.clientType,
120
120
  project_id: options.projectId,
@@ -129,7 +129,7 @@ export async function startRemoteSession(options: {
129
129
 
130
130
  export async function loadProjectHandoff(projectId: string): Promise<HandoffResponse | null> {
131
131
  return (await callMcpTool(
132
- 'gmtr_load_handoff',
132
+ 'gramatr_load_handoff',
133
133
  { project_id: projectId },
134
134
  15000,
135
135
  )) as HandoffResponse | null;
@@ -143,7 +143,7 @@ export function persistSessionRegistration(rootDir: string, response: SessionSta
143
143
  if (!config) return null;
144
144
  config.current_session = config.current_session || {};
145
145
  if (normalized.interactionId) config.current_session.interaction_id = normalized.interactionId;
146
- if (normalized.entityId) config.current_session.gmtr_entity_id = normalized.entityId;
146
+ if (normalized.entityId) config.current_session.gramatr_entity_id = normalized.entityId;
147
147
  writeGmtrConfig(rootDir, config);
148
148
  return config;
149
149
  }
@@ -176,7 +176,7 @@ export function buildGitProjectContextPayload(options: {
176
176
  git_remote: options.git.remote,
177
177
  working_directory: options.workingDirectory,
178
178
  session_start: options.sessionStart,
179
- gmtr_config_path: join(options.git.root, '.gmtr', 'settings.json'),
179
+ gramatr_config_path: join(options.git.root, '.gmtr', 'settings.json'),
180
180
  project_entity_id: options.projectEntityId,
181
181
  restore_needed: options.restoreNeeded,
182
182
  action_required: 'check_or_create_project_entity',
@@ -195,8 +195,8 @@ export function buildNonGitProjectContextPayload(options: {
195
195
  project_name: basename(options.cwd),
196
196
  working_directory: options.cwd,
197
197
  session_start: options.sessionStart,
198
- gmtr_config_path: join(options.cwd, '.gmtr', 'settings.json'),
198
+ gramatr_config_path: join(options.cwd, '.gmtr', 'settings.json'),
199
199
  project_entity_id: options.projectEntityId,
200
- action_required: 'gmtr_init_needed',
200
+ action_required: 'gramatr_init_needed',
201
201
  };
202
202
  }
@@ -64,7 +64,7 @@ async function main(): Promise<void> {
64
64
  // Step 2: Validate server connectivity
65
65
  log('');
66
66
  log('Step 2: Validating server connectivity...');
67
- const serverUrl = process.env.GMTR_URL || DEFAULT_MCP_URL;
67
+ const serverUrl = process.env.GRAMATR_URL || DEFAULT_MCP_URL;
68
68
  const serverReachable = await validateServer(serverUrl);
69
69
  if (serverReachable) {
70
70
  log(` OK Server reachable at ${serverUrl.replace(/\/mcp$/, '')}`);
package/gemini/README.md CHANGED
@@ -74,7 +74,7 @@ bun packages/client/bin/gmtr-login.ts
74
74
 
75
75
  This stores the token in `~/.gmtr.json`, which the installer reads automatically.
76
76
 
77
- API keys start with `gmtr_sk_` and can be created at [gramatr.com](https://gramatr.com) or via the `gmtr_create_api_key` MCP tool.
77
+ API keys start with `gramatr_sk_` and can be created at [gramatr.com](https://gramatr.com) or via the `gramatr_create_api_key` MCP tool.
78
78
 
79
79
  ## Hook Event Mapping
80
80
 
package/gemini/install.ts CHANGED
@@ -46,7 +46,7 @@ async function promptForApiKey(): Promise<string | null> {
46
46
  log(' gramatr requires authentication.');
47
47
  log(' Options:');
48
48
  log(' 1. Run `bun gmtr-login.ts` first to authenticate via browser');
49
- log(' 2. Paste an API key below (starts with gmtr_sk_)');
49
+ log(' 2. Paste an API key below (starts with gramatr_sk_)');
50
50
  log('');
51
51
  process.stdout.write(' API Key (enter to skip): ');
52
52
 
@@ -154,7 +154,7 @@ async function resolveApiKey(home: string): Promise<string | null> {
154
154
  }
155
155
  config.token = prompted;
156
156
  config.token_type =
157
- prompted.startsWith('gmtr_sk_') || prompted.startsWith('aios_sk_')
157
+ prompted.startsWith('gramatr_sk_') || prompted.startsWith('aios_sk_')
158
158
  ? 'api_key'
159
159
  : 'oauth';
160
160
  config.authenticated_at = new Date().toISOString();
@@ -67,7 +67,7 @@ export function buildExtensionManifest(): GeminiExtensionManifest {
67
67
  name: 'API Key',
68
68
  envVar: 'GRAMATR_API_KEY',
69
69
  sensitive: true,
70
- description: 'gramatr API key (starts with gmtr_sk_). Get one at gramatr.com or via gmtr-login.',
70
+ description: 'gramatr API key (starts with gramatr_sk_). Get one at gramatr.com or via gmtr-login.',
71
71
  },
72
72
  ],
73
73
  };
@@ -36,7 +36,7 @@ async function main() {
36
36
 
37
37
  // Read server URL and token for direct HTTP feedback
38
38
  const gmtrDir = getGmtrDir();
39
- let serverUrl = process.env.GMTR_URL || 'https://api.gramatr.com/mcp';
39
+ let serverUrl = process.env.GRAMATR_URL || 'https://api.gramatr.com/mcp';
40
40
  let token = '';
41
41
 
42
42
  try {
@@ -61,7 +61,7 @@ async function main() {
61
61
  id: 1,
62
62
  method: 'tools/call',
63
63
  params: {
64
- name: 'gmtr_classification_feedback',
64
+ name: 'gramatr_classification_feedback',
65
65
  arguments: {
66
66
  original_prompt: parsed.userMessages?.[0] || '',
67
67
  session_id: hookInput.session_id,
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * gmtr-tool-tracker-utils.ts — Pure utility functions for the GMTR Tool Tracker hook.
3
3
  *
4
- * Extracted from GMTRToolTracker.hook.ts for testability. These are pure functions
4
+ * Extracted from gramatr-tool-tracker.hook.ts for testability. These are pure functions
5
5
  * with no Bun dependencies, no I/O, no side effects.
6
6
  */
7
7
 
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * GMTRPromptEnricher.hook.ts — grāmatr UserPromptSubmit Hook
3
+ * gramatr-prompt-enricher.hook.ts — grāmatr UserPromptSubmit Hook
4
4
  *
5
5
  * Fires before the agent processes a user prompt. Calls the grāmatr
6
6
  * decision router to pre-classify the request, then injects
7
7
  * the intelligence as additionalContext so the agent sees it.
8
8
  *
9
9
  * TRIGGER: UserPromptSubmit
10
- * TIMEOUT: 15s default (configurable via GMTR_TIMEOUT)
10
+ * TIMEOUT: 15s default (configurable via GRAMATR_TIMEOUT)
11
11
  * SAFETY: Never blocks. On any error, prompt passes through unmodified.
12
12
  *
13
13
  * What it injects:
@@ -42,8 +42,8 @@ interface HookInput {
42
42
 
43
43
  // ── Configuration ──
44
44
 
45
- const TIMEOUT_MS = parseInt(process.env.GMTR_TIMEOUT || '30000', 10);
46
- const ENABLED = process.env.GMTR_ENRICH !== '0'; // disable with GMTR_ENRICH=0
45
+ const TIMEOUT_MS = parseInt(process.env.GRAMATR_TIMEOUT || '30000', 10);
46
+ const ENABLED = process.env.GRAMATR_ENRICH !== '0'; // disable with GRAMATR_ENRICH=0
47
47
 
48
48
  // ── Project ID Resolution (Issue #76 — project-scoped memory) ──
49
49
 
@@ -398,9 +398,9 @@ function formatIntelligence(data: RouteResponse): string {
398
398
  lines.push(` Context: ${ca.context_summary || 'memory-aware'}`);
399
399
  lines.push(' ACTION: Use the Task tool with subagent_type="general-purpose" and inject this system prompt:');
400
400
  lines.push(' --- AGENT SYSTEM PROMPT START ---');
401
- // Truncate to avoid overwhelming the context — full prompt available via gmtr_invoke_agent
401
+ // Truncate to avoid overwhelming the context — full prompt available via gramatr_invoke_agent
402
402
  const promptPreview = (ca.system_prompt || '').substring(0, 800);
403
- lines.push(` ${promptPreview}${(ca.system_prompt || '').length > 800 ? '... [truncated — use gmtr_invoke_agent for full prompt]' : ''}`);
403
+ lines.push(` ${promptPreview}${(ca.system_prompt || '').length > 800 ? '... [truncated — use gramatr_invoke_agent for full prompt]' : ''}`);
404
404
  lines.push(' --- AGENT SYSTEM PROMPT END ---');
405
405
  }
406
406
  }
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * GMTRRatingCapture.hook.ts — gramatr-native Rating Capture (UserPromptSubmit)
3
+ * gramatr-rating-capture.hook.ts — gramatr-native Rating Capture (UserPromptSubmit)
4
4
  *
5
5
  * Captures explicit ratings (1-10 pattern) from user prompts and writes
6
- * to $GMTR_DIR/.state/ratings.jsonl for sparkline consumption.
6
+ * to $GRAMATR_DIR/.state/ratings.jsonl for sparkline consumption.
7
7
  *
8
8
  * TRIGGER: UserPromptSubmit
9
9
  *
@@ -15,7 +15,7 @@
15
15
  * - Self-contained: only depends on ./lib/paths
16
16
  *
17
17
  * SIDE EFFECTS:
18
- * - Writes to: $GMTR_DIR/.state/ratings.jsonl
18
+ * - Writes to: $GRAMATR_DIR/.state/ratings.jsonl
19
19
  * - macOS notification for low ratings (<= 3)
20
20
  *
21
21
  * PERFORMANCE:
@@ -100,13 +100,13 @@ function parseExplicitRating(prompt: string): { rating: number; comment?: string
100
100
  function writeRating(entry: RatingEntry): void {
101
101
  if (!existsSync(STATE_DIR)) mkdirSync(STATE_DIR, { recursive: true });
102
102
  appendFileSync(RATINGS_FILE, JSON.stringify(entry) + '\n', 'utf-8');
103
- console.error(`[GMTRRatingCapture] Wrote rating ${entry.rating} to ${RATINGS_FILE}`);
103
+ console.error(`[gramatr-rating-capture] Wrote rating ${entry.rating} to ${RATINGS_FILE}`);
104
104
  }
105
105
 
106
106
  // ── Push to GMTR Server (fire-and-forget) ──
107
107
 
108
108
  function pushToServer(entry: RatingEntry): void {
109
- const gmtrUrl = process.env.GMTR_URL || 'https://api.gramatr.com/mcp';
109
+ const gmtrUrl = process.env.GRAMATR_URL || 'https://api.gramatr.com/mcp';
110
110
  const apiBase = gmtrUrl.replace(/\/mcp$/, '/api/v1');
111
111
 
112
112
  fetch(`${apiBase}/feedback`, {
@@ -124,9 +124,9 @@ function pushToServer(entry: RatingEntry): void {
124
124
  })
125
125
  .then((res) => {
126
126
  if (res.ok) {
127
- console.error(`[GMTRRatingCapture] Pushed rating ${entry.rating} to server`);
127
+ console.error(`[gramatr-rating-capture] Pushed rating ${entry.rating} to server`);
128
128
  } else {
129
- console.error(`[GMTRRatingCapture] Server push failed: HTTP ${res.status}`);
129
+ console.error(`[gramatr-rating-capture] Server push failed: HTTP ${res.status}`);
130
130
  }
131
131
  })
132
132
  .catch(() => {
@@ -169,7 +169,7 @@ async function main() {
169
169
  process.exit(0);
170
170
  }
171
171
 
172
- console.error(`[GMTRRatingCapture] Explicit rating: ${result.rating}${result.comment ? ` - ${result.comment}` : ''}`);
172
+ console.error(`[gramatr-rating-capture] Explicit rating: ${result.rating}${result.comment ? ` - ${result.comment}` : ''}`);
173
173
 
174
174
  const entry: RatingEntry = {
175
175
  timestamp: new Date().toISOString(),
@@ -190,7 +190,7 @@ async function main() {
190
190
 
191
191
  process.exit(0);
192
192
  } catch (err) {
193
- console.error(`[GMTRRatingCapture] Error: ${err}`);
193
+ console.error(`[gramatr-rating-capture] Error: ${err}`);
194
194
  process.exit(0);
195
195
  }
196
196
  }
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * GMTRSecurityValidator.hook.ts — gramatr-native Security Validation (PreToolUse)
3
+ * gramatr-security-validator.hook.ts — gramatr-native Security Validation (PreToolUse)
4
4
  *
5
5
  * Validates Bash commands and file operations against security patterns
6
6
  * before execution. Prevents destructive operations and protects sensitive files.
@@ -10,7 +10,7 @@
10
10
  * This is the gramatr-native replacement for PAI's SecurityValidator.hook.ts.
11
11
  * Key differences:
12
12
  * - Inline default patterns (no yaml dependency, no PAI skill dir)
13
- * - Logs to $GMTR_DIR/.state/security/ (not PAI MEMORY/SECURITY/)
13
+ * - Logs to $GRAMATR_DIR/.state/security/ (not PAI MEMORY/SECURITY/)
14
14
  * - Self-contained: only depends on ./lib/paths
15
15
  *
16
16
  * OUTPUT:
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * GMTRToolTracker.hook.ts — grāmatr PostToolUse Hook
3
+ * gramatr-tool-tracker.hook.ts — grāmatr PostToolUse Hook
4
4
  *
5
5
  * Fires after any GMTR MCP tool call. Extracts execution metrics from
6
6
  * tool_result and surfaces them as a user-visible status line.
@@ -146,7 +146,7 @@ async function main() {
146
146
  try { stats = JSON.parse(readFileSync(statsFile, 'utf8')); } catch {}
147
147
 
148
148
  // Track search count
149
- if (shortName.includes('search') || shortName === 'gmtr_execute_intent') {
149
+ if (shortName.includes('search') || shortName === 'gramatr_execute_intent') {
150
150
  stats.search_count = ((stats.search_count as number) || 0) + 1;
151
151
  }
152
152
 
@@ -46,7 +46,7 @@ export interface GmtrConfig {
46
46
  last_updated?: string;
47
47
  token_limit?: number;
48
48
  interaction_id?: string;
49
- gmtr_entity_id?: string;
49
+ gramatr_entity_id?: string;
50
50
  helper_pid?: number | null;
51
51
  last_classification?: {
52
52
  timestamp?: string;
@@ -342,7 +342,7 @@ export function createDefaultConfig(options: {
342
342
  *
343
343
  * Priority:
344
344
  * 1. ~/.gmtr.json (canonical, gramatr-owned, vendor-agnostic)
345
- * 2. GMTR_TOKEN env var (CI, headless override)
345
+ * 2. GRAMATR_TOKEN env var (CI, headless override)
346
346
  * 3. ~/gmtr-client/settings.json auth.api_key (legacy, will be migrated)
347
347
  *
348
348
  * Token is NEVER stored in ~/.claude.json, ~/.codex/, or ~/.gemini/.
@@ -357,15 +357,15 @@ export function resolveAuthToken(): string | null {
357
357
  // No config file or parse error
358
358
  }
359
359
 
360
- // 2. GMTR_TOKEN env var (CI, headless, or shell profile override)
361
- if (process.env.GMTR_TOKEN) return process.env.GMTR_TOKEN;
360
+ // 2. GRAMATR_TOKEN env var (CI, headless, or shell profile override)
361
+ if (process.env.GRAMATR_TOKEN) return process.env.GRAMATR_TOKEN;
362
362
 
363
363
  // 3. Legacy: AIOS_MCP_TOKEN env var (deprecated)
364
364
  if (process.env.AIOS_MCP_TOKEN) return process.env.AIOS_MCP_TOKEN;
365
365
 
366
366
  // 4. Legacy: ~/gmtr-client/settings.json (will be migrated to ~/.gmtr.json)
367
367
  try {
368
- const gmtrDir = process.env.GMTR_DIR || join(HOME, 'gmtr-client');
368
+ const gmtrDir = process.env.GRAMATR_DIR || join(HOME, 'gmtr-client');
369
369
  const settingsPath = join(gmtrDir, 'settings.json');
370
370
  const settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
371
371
  if (settings.auth?.api_key && settings.auth.api_key !== 'REPLACE_WITH_YOUR_API_KEY') {
@@ -382,12 +382,12 @@ export function resolveAuthToken(): string | null {
382
382
 
383
383
  /**
384
384
  * Resolve MCP server URL from config files.
385
- * Priority: ~/gmtr-client/settings.json > GMTR_URL env > ~/.claude.json > default
385
+ * Priority: ~/gmtr-client/settings.json > GRAMATR_URL env > ~/.claude.json > default
386
386
  */
387
387
  export function resolveMcpUrl(): string {
388
388
  // 1. ~/gmtr-client/settings.json (canonical, vendor-agnostic)
389
389
  try {
390
- const gmtrDir = process.env.GMTR_DIR || join(HOME, 'gmtr-client');
390
+ const gmtrDir = process.env.GRAMATR_DIR || join(HOME, 'gmtr-client');
391
391
  const settingsPath = join(gmtrDir, 'settings.json');
392
392
  const settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
393
393
  if (settings.auth?.server_url) return settings.auth.server_url;
@@ -396,7 +396,7 @@ export function resolveMcpUrl(): string {
396
396
  }
397
397
 
398
398
  // 2. Environment variable
399
- if (process.env.GMTR_URL) return process.env.GMTR_URL;
399
+ if (process.env.GRAMATR_URL) return process.env.GRAMATR_URL;
400
400
 
401
401
  // 3. ~/.claude.json (Claude-specific MCP config)
402
402
  try {
@@ -10,8 +10,8 @@ import { readFileSync, existsSync } from 'fs';
10
10
  import { join } from 'path';
11
11
 
12
12
  const HOME = process.env.HOME!;
13
- const GMTR_DIR = process.env.GMTR_DIR || join(HOME, 'gmtr-client');
14
- const GMTR_SETTINGS_PATH = join(GMTR_DIR, 'settings.json'); // gramatr-owned, vendor-agnostic
13
+ const GRAMATR_DIR = process.env.GRAMATR_DIR || join(HOME, 'gmtr-client');
14
+ const GMTR_SETTINGS_PATH = join(GRAMATR_DIR, 'settings.json'); // gramatr-owned, vendor-agnostic
15
15
  const CLAUDE_SETTINGS_PATH = join(HOME, '.claude/settings.json'); // legacy fallback only
16
16
 
17
17
  // Default identity (fallback if settings.json doesn't have identity section)
@@ -28,15 +28,15 @@ export function expandPath(path: string): string {
28
28
 
29
29
  /**
30
30
  * Get the GMTR base directory (expanded)
31
- * Priority: GMTR_DIR env var → PAI_DIR env var (migration) → ~/.claude
31
+ * Priority: GRAMATR_DIR env var → PAI_DIR env var (migration) → ~/.claude
32
32
  */
33
33
  export function getGmtrDir(): string {
34
- const envGmtrDir = process.env.GMTR_DIR;
34
+ const envGmtrDir = process.env.GRAMATR_DIR;
35
35
  if (envGmtrDir) {
36
36
  return expandPath(envGmtrDir);
37
37
  }
38
38
 
39
- // Migration fallback: honor PAI_DIR if GMTR_DIR not set yet
39
+ // Migration fallback: honor PAI_DIR if GRAMATR_DIR not set yet
40
40
  const envPaiDir = process.env.PAI_DIR;
41
41
  if (envPaiDir) {
42
42
  return expandPath(envPaiDir);
@@ -49,15 +49,15 @@ export function getGmtrDir(): string {
49
49
 
50
50
  /**
51
51
  * Get the settings.json path
52
- * Always ~/.claude/settings.json — NOT relative to GMTR_DIR.
53
- * GMTR_DIR is the client payload directory (~/gmtr-client), not ~/.claude.
52
+ * Always ~/.claude/settings.json — NOT relative to GRAMATR_DIR.
53
+ * GRAMATR_DIR is the client payload directory (~/gmtr-client), not ~/.claude.
54
54
  */
55
55
  export function getSettingsPath(): string {
56
56
  return join(homedir(), '.claude', 'settings.json');
57
57
  }
58
58
 
59
59
  /**
60
- * Get a path relative to GMTR_DIR
60
+ * Get a path relative to GRAMATR_DIR
61
61
  */
62
62
  export function gmtrPath(...segments: string[]): string {
63
63
  return join(getGmtrDir(), ...segments);
@@ -123,17 +123,17 @@ async function main(): Promise<void> {
123
123
  }
124
124
 
125
125
  // Get session state from config
126
- const sessionEntityId = config?.current_session?.gmtr_entity_id || '';
126
+ const sessionEntityId = config?.current_session?.gramatr_entity_id || '';
127
127
  const interactionId = config?.current_session?.interaction_id || '';
128
128
 
129
129
  log('');
130
130
  log('Saving session state to gramatr...');
131
131
 
132
- // Session lifecycle only — gmtr_session_end records git summary on the session entity.
133
- // Handoffs are saved by the AGENT in the LEARN phase via gmtr_save_handoff (HARD GATE).
132
+ // Session lifecycle only — gramatr_session_end records git summary on the session entity.
133
+ // Handoffs are saved by the AGENT in the LEARN phase via gramatr_save_handoff (HARD GATE).
134
134
  // The hook does NOT save handoffs — it lacks conversation context.
135
135
  try {
136
- const rawResult = await callMcpToolRaw('gmtr_session_end', {
136
+ const rawResult = await callMcpToolRaw('gramatr_session_end', {
137
137
  entity_id: sessionEntityId || sessionId,
138
138
  session_id: sessionId,
139
139
  interaction_id: interactionId,
@@ -171,7 +171,7 @@ function displayBanner(): void {
171
171
  // ── Sync Ratings (background, non-blocking) ──
172
172
 
173
173
  function syncRatingsInBackground(): void {
174
- const syncScript = join(process.env.GMTR_DIR || join(process.env.HOME || '', 'gmtr-client'), 'hooks', 'sync-ratings.hook.ts');
174
+ const syncScript = join(process.env.GRAMATR_DIR || join(process.env.HOME || '', 'gmtr-client'), 'hooks', 'sync-ratings.hook.ts');
175
175
  if (existsSync(syncScript)) {
176
176
  try {
177
177
  const npxBin = process.platform === 'win32' ? 'npx.cmd' : 'npx';
@@ -336,9 +336,9 @@ async function main(): Promise<void> {
336
336
  log(' - Show observation timestamps');
337
337
  log(`3. Load related entities from \`${gmtrConfigPath}\`:`);
338
338
  log(' - Check .related_entities for linked databases, people, services, concepts');
339
- log(' - Optionally fetch details for key related entities (use gmtr_execute_intent with detail_level: summary)');
339
+ log(' - Optionally fetch details for key related entities (use gramatr_execute_intent with detail_level: summary)');
340
340
  log('4. Keep summary concise - just enough context to resume work');
341
- log(' NOTE: Using intelligent tools (gmtr_execute_intent) provides 80-95% token reduction vs direct get_entities calls');
341
+ log(' NOTE: Using intelligent tools (gramatr_execute_intent) provides 80-95% token reduction vs direct get_entities calls');
342
342
  log('');
343
343
  }
344
344
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gramatr/client",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },