@gramatr/client 0.6.1 → 0.6.3
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/CLAUDE.md +1 -1
- package/README.md +2 -2
- package/bin/clear-creds.ts +1 -1
- package/bin/install.ts +13 -3
- package/bin/uninstall.ts +1 -1
- package/chatgpt/README.md +1 -1
- package/codex/README.md +2 -2
- package/codex/install.ts +8 -0
- package/core/migration.ts +120 -4
- package/core/version-check.ts +2 -2
- package/desktop/README.md +1 -1
- package/gemini/README.md +3 -3
- package/gemini/install.ts +8 -0
- package/hooks/session-start.hook.ts +1 -1
- package/package.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -10,7 +10,7 @@ ISC scaffold, capability audit, phase templates, and composed agents.
|
|
|
10
10
|
**Memory:** Use gramatr MCP tools (`search_semantic`, `create_entity`, `add_observation`),
|
|
11
11
|
not local markdown files.
|
|
12
12
|
|
|
13
|
-
**Identity:** Read from
|
|
13
|
+
**Identity:** Read from `~/.gramatr/settings.json` — `daidentity` for your name,
|
|
14
14
|
`principal` for the user's name.
|
|
15
15
|
|
|
16
16
|
**If the server is unreachable:** Use 7-phase structure (OBSERVE → THINK → PLAN → BUILD →
|
package/README.md
CHANGED
|
@@ -72,14 +72,14 @@ The client never stores intelligence locally. The server delivers everything: be
|
|
|
72
72
|
## What gets installed
|
|
73
73
|
|
|
74
74
|
```
|
|
75
|
-
|
|
75
|
+
~/.gramatr/ # Client runtime
|
|
76
76
|
hooks/ # 8 lifecycle hooks
|
|
77
77
|
core/ # Shared routing + session logic
|
|
78
78
|
bin/ # Status line, login, utilities
|
|
79
79
|
CLAUDE.md # Minimal behavioral framework
|
|
80
80
|
~/.claude/settings.json # Hook configuration (merged, not overwritten)
|
|
81
81
|
~/.claude.json # MCP server registration
|
|
82
|
-
~/.
|
|
82
|
+
~/.gramatr.json # Auth token (canonical source)
|
|
83
83
|
```
|
|
84
84
|
|
|
85
85
|
## Commands
|
package/bin/clear-creds.ts
CHANGED
|
@@ -40,7 +40,7 @@ function showHelp(): void {
|
|
|
40
40
|
log(`gramatr clear-creds — Remove every stored gramatr credential
|
|
41
41
|
|
|
42
42
|
Usage:
|
|
43
|
-
gramatr clear-creds Sweep ~/.gramatr.json + auth.api_key from
|
|
43
|
+
gramatr clear-creds Sweep ~/.gramatr.json + auth.api_key from ~/.gramatr/settings.json
|
|
44
44
|
|
|
45
45
|
After running, the next install or login will be forced through OAuth.
|
|
46
46
|
|
package/bin/install.ts
CHANGED
|
@@ -20,6 +20,11 @@ import { createInterface } from 'readline';
|
|
|
20
20
|
import { buildClaudeHooksFile } from '../core/install.ts';
|
|
21
21
|
import { VERSION } from '../core/version.ts';
|
|
22
22
|
import { resolveAuthToken } from '../core/auth.ts';
|
|
23
|
+
import {
|
|
24
|
+
sanitizeLegacyEnvBlock,
|
|
25
|
+
detectShellRcLegacyEnv,
|
|
26
|
+
formatShellRcLegacyWarning,
|
|
27
|
+
} from '../core/migration.ts';
|
|
23
28
|
|
|
24
29
|
// ── Constants ──
|
|
25
30
|
|
|
@@ -513,8 +518,8 @@ function updateClaudeSettings(url: string, token: string): void {
|
|
|
513
518
|
|
|
514
519
|
const settings = readJson(CLAUDE_SETTINGS);
|
|
515
520
|
|
|
516
|
-
// Env vars
|
|
517
|
-
settings.env = settings.env
|
|
521
|
+
// Env vars — sanitize legacy GMTR_* keys before writing (v0.6.2+ fix)
|
|
522
|
+
settings.env = sanitizeLegacyEnvBlock(settings.env);
|
|
518
523
|
settings.env.GRAMATR_DIR = CLIENT_DIR;
|
|
519
524
|
settings.env.GRAMATR_URL = url;
|
|
520
525
|
settings.env.PATH = `${HOME}/.gramatr/bin:/usr/local/bin:/usr/bin:/bin`;
|
|
@@ -584,7 +589,8 @@ function registerMcpServer(url: string, token: string): void {
|
|
|
584
589
|
|
|
585
590
|
// Register in ~/.claude.json
|
|
586
591
|
mergeJson(CLAUDE_JSON, (data) => {
|
|
587
|
-
|
|
592
|
+
// Sanitize legacy GMTR_* keys before writing (v0.6.2+ fix)
|
|
593
|
+
data.env = sanitizeLegacyEnvBlock(data.env);
|
|
588
594
|
if (token) data.env.GRAMATR_TOKEN = token;
|
|
589
595
|
delete data.env.AIOS_MCP_TOKEN;
|
|
590
596
|
|
|
@@ -737,6 +743,10 @@ async function main(): Promise<void> {
|
|
|
737
743
|
log(' 2. Already authenticated (token found in ~/.gramatr.json)');
|
|
738
744
|
}
|
|
739
745
|
log('');
|
|
746
|
+
|
|
747
|
+
// v0.6.2+: detect legacy GMTR_* exports in user shell rc files and warn
|
|
748
|
+
const rcHits = detectShellRcLegacyEnv(HOME);
|
|
749
|
+
if (rcHits.length > 0) log(formatShellRcLegacyWarning(rcHits));
|
|
740
750
|
} else {
|
|
741
751
|
log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
742
752
|
log(' Install completed with warnings');
|
package/bin/uninstall.ts
CHANGED
|
@@ -170,7 +170,7 @@ async function main(): Promise<void> {
|
|
|
170
170
|
try {
|
|
171
171
|
const settings = JSON.parse(readFileSync(claudeSettings, 'utf8'));
|
|
172
172
|
if (settings.hooks) {
|
|
173
|
-
// Remove hook entries that reference
|
|
173
|
+
// Remove hook entries that reference .gramatr or gramatr paths
|
|
174
174
|
for (const [event, hooks] of Object.entries(settings.hooks)) {
|
|
175
175
|
if (Array.isArray(hooks)) {
|
|
176
176
|
settings.hooks[event] = (hooks as any[]).filter(
|
package/chatgpt/README.md
CHANGED
|
@@ -19,7 +19,7 @@ bun chatgpt/install.ts
|
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
The installer will:
|
|
22
|
-
1. Find your API key from `~/.
|
|
22
|
+
1. Find your API key from `~/.gramatr.json`, `GRAMATR_API_KEY` env, or prompt you
|
|
23
23
|
2. Validate connectivity to the gramatr server
|
|
24
24
|
3. Detect your platform and locate the ChatGPT config file
|
|
25
25
|
4. Merge the gramatr MCP server entry without overwriting existing servers
|
package/codex/README.md
CHANGED
|
@@ -21,8 +21,8 @@ Install locally with:
|
|
|
21
21
|
- `pnpm --filter @gramatr/client install-codex`
|
|
22
22
|
|
|
23
23
|
The installer:
|
|
24
|
-
- syncs this Codex runtime into
|
|
25
|
-
- syncs the shared `gmtr-hook-utils.ts` dependency into
|
|
24
|
+
- syncs this Codex runtime into `~/.gramatr/codex`
|
|
25
|
+
- syncs the shared `gmtr-hook-utils.ts` dependency into `~/.gramatr/hooks/lib`
|
|
26
26
|
- merges `~/.codex/hooks.json`
|
|
27
27
|
- enables `codex_hooks` in `~/.codex/config.toml`
|
|
28
28
|
- upserts a managed gramatr block in `~/.codex/AGENTS.md`
|
package/codex/install.ts
CHANGED
|
@@ -9,6 +9,10 @@ import {
|
|
|
9
9
|
mergeHooksFile,
|
|
10
10
|
upsertManagedBlock,
|
|
11
11
|
} from './lib/codex-install-utils.ts';
|
|
12
|
+
import {
|
|
13
|
+
detectShellRcLegacyEnv,
|
|
14
|
+
formatShellRcLegacyWarning,
|
|
15
|
+
} from '../core/migration.ts';
|
|
12
16
|
|
|
13
17
|
const START_MARKER = '<!-- GMTR-CODEX-START -->';
|
|
14
18
|
const END_MARKER = '<!-- GMTR-CODEX-END -->';
|
|
@@ -107,6 +111,10 @@ export function main(): void {
|
|
|
107
111
|
log('');
|
|
108
112
|
log('Codex installer complete.');
|
|
109
113
|
log('Restart Codex or start a new session to load the updated hook configuration.');
|
|
114
|
+
|
|
115
|
+
// v0.6.2+: detect legacy GMTR_* exports in shell rc and warn
|
|
116
|
+
const rcHits = detectShellRcLegacyEnv(home);
|
|
117
|
+
if (rcHits.length > 0) log(formatShellRcLegacyWarning(rcHits));
|
|
110
118
|
}
|
|
111
119
|
|
|
112
120
|
// Run directly when executed as a script
|
package/core/migration.ts
CHANGED
|
@@ -308,6 +308,33 @@ function stripLegacyEntriesFromHookEvent(value: unknown): unknown {
|
|
|
308
308
|
.filter(Boolean);
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
+
/**
|
|
312
|
+
* Env-var key prefixes that were used by earlier gramatr versions and are
|
|
313
|
+
* now either renamed or removed. Any key matching one of these prefixes that
|
|
314
|
+
* survives in a user's config file across an upgrade is a legacy leak and
|
|
315
|
+
* must be scrubbed before we write the new env block.
|
|
316
|
+
*
|
|
317
|
+
* Issue: v0.6.0 renamed GMTR_* → GRAMATR_* but the installer's env merge
|
|
318
|
+
* only SET new keys without removing old ones. Users upgrading from v0.5.x
|
|
319
|
+
* kept the stale GMTR_DIR, GMTR_URL, GMTR_TOKEN in their config files
|
|
320
|
+
* forever. v0.6.2 fixes this by sanitizing on every install.
|
|
321
|
+
*/
|
|
322
|
+
export const LEGACY_ENV_KEY_PREFIXES: ReadonlyArray<string> = ['GMTR_'];
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Strip any env-var key matching a legacy prefix. Returns a new object;
|
|
326
|
+
* input is not mutated. Safe on null/undefined (returns {}).
|
|
327
|
+
*/
|
|
328
|
+
export function sanitizeLegacyEnvBlock(env: unknown): JsonObject {
|
|
329
|
+
if (!isRecord(env)) return {};
|
|
330
|
+
const next: JsonObject = {};
|
|
331
|
+
for (const [key, value] of Object.entries(env)) {
|
|
332
|
+
if (LEGACY_ENV_KEY_PREFIXES.some((prefix) => key.startsWith(prefix))) continue;
|
|
333
|
+
next[key] = value;
|
|
334
|
+
}
|
|
335
|
+
return next;
|
|
336
|
+
}
|
|
337
|
+
|
|
311
338
|
export function sanitizeClaudeSettings(
|
|
312
339
|
settings: JsonObject,
|
|
313
340
|
clientDir: string,
|
|
@@ -326,14 +353,103 @@ export function sanitizeClaudeSettings(
|
|
|
326
353
|
}
|
|
327
354
|
|
|
328
355
|
next.hooks = hooks;
|
|
356
|
+
// Sanitize legacy env keys (v0.6.2+)
|
|
357
|
+
next.env = sanitizeLegacyEnvBlock(next.env);
|
|
329
358
|
return next;
|
|
330
359
|
}
|
|
331
360
|
|
|
332
361
|
export function sanitizeClaudeJson(claudeJson: JsonObject): JsonObject {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
362
|
+
const next = { ...claudeJson };
|
|
363
|
+
if (isRecord(claudeJson.mcpServers)) {
|
|
364
|
+
const mcpServers = { ...claudeJson.mcpServers };
|
|
365
|
+
delete mcpServers.aios;
|
|
366
|
+
next.mcpServers = mcpServers;
|
|
367
|
+
}
|
|
368
|
+
// Sanitize legacy env keys (v0.6.2+)
|
|
369
|
+
next.env = sanitizeLegacyEnvBlock(claudeJson.env);
|
|
370
|
+
return next;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// ── Shell rc legacy env detection (v0.6.2+) ──────────────────────────────
|
|
374
|
+
//
|
|
375
|
+
// Users who added `export GMTR_TOKEN=...` lines to their shell rc files
|
|
376
|
+
// before v0.6.0 will keep those vars live across every new shell until they
|
|
377
|
+
// manually edit the files. Our installer cannot mutate user dotfiles
|
|
378
|
+
// without permission, but it CAN detect and warn.
|
|
379
|
+
|
|
380
|
+
export interface ShellRcLegacyHit {
|
|
381
|
+
file: string;
|
|
382
|
+
lineNumber: number;
|
|
383
|
+
line: string;
|
|
384
|
+
variableName: string;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const SHELL_RC_CANDIDATES: ReadonlyArray<string> = [
|
|
388
|
+
'.bashrc',
|
|
389
|
+
'.bash_profile',
|
|
390
|
+
'.zshrc',
|
|
391
|
+
'.zprofile',
|
|
392
|
+
'.profile',
|
|
393
|
+
];
|
|
394
|
+
|
|
395
|
+
const LEGACY_EXPORT_RE = /^\s*export\s+(GMTR_[A-Z_]+)=/;
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Scan shell rc files for `export GMTR_*=...` lines. Returns a list of hits,
|
|
399
|
+
* each with file path, line number, raw line, and the variable name. Never
|
|
400
|
+
* mutates any file — read-only detection.
|
|
401
|
+
*/
|
|
402
|
+
export function detectShellRcLegacyEnv(homeDir: string): ShellRcLegacyHit[] {
|
|
403
|
+
const out: ShellRcLegacyHit[] = [];
|
|
404
|
+
for (const basename of SHELL_RC_CANDIDATES) {
|
|
405
|
+
const path = `${homeDir}/${basename}`;
|
|
406
|
+
if (!existsSync(path)) continue;
|
|
407
|
+
let content: string;
|
|
408
|
+
try {
|
|
409
|
+
content = readFileSync(path, 'utf8');
|
|
410
|
+
} catch {
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
const lines = content.split(/\r?\n/);
|
|
414
|
+
for (let i = 0; i < lines.length; i++) {
|
|
415
|
+
const match = lines[i].match(LEGACY_EXPORT_RE);
|
|
416
|
+
if (match) {
|
|
417
|
+
out.push({
|
|
418
|
+
file: path,
|
|
419
|
+
lineNumber: i + 1,
|
|
420
|
+
line: lines[i],
|
|
421
|
+
variableName: match[1],
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return out;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Format shell-rc legacy hits as a human-readable block of warning text.
|
|
431
|
+
* Returns an empty string if there are no hits. Writes no files.
|
|
432
|
+
*/
|
|
433
|
+
export function formatShellRcLegacyWarning(hits: ShellRcLegacyHit[]): string {
|
|
434
|
+
if (hits.length === 0) return '';
|
|
435
|
+
const lines: string[] = [];
|
|
436
|
+
lines.push('');
|
|
437
|
+
lines.push('⚠ Legacy GMTR_* exports detected in your shell rc files:');
|
|
438
|
+
lines.push('');
|
|
439
|
+
for (const hit of hits) {
|
|
440
|
+
lines.push(` ${hit.file}:${hit.lineNumber}`);
|
|
441
|
+
lines.push(` ${hit.line.trim()}`);
|
|
442
|
+
}
|
|
443
|
+
const uniqueVars = Array.from(new Set(hits.map((h) => h.variableName)));
|
|
444
|
+
lines.push('');
|
|
445
|
+
lines.push(' These vars are no longer read by gramatr (since v0.6.0) but');
|
|
446
|
+
lines.push(' remain in your shell environment on every login. To clean up:');
|
|
447
|
+
lines.push('');
|
|
448
|
+
lines.push(' 1. Edit the files above and delete the matching export lines');
|
|
449
|
+
lines.push(` 2. Run in each open terminal: unset ${uniqueVars.join(' ')}`);
|
|
450
|
+
lines.push(' 3. Or just start a fresh shell session');
|
|
451
|
+
lines.push('');
|
|
452
|
+
return lines.join('\n');
|
|
337
453
|
}
|
|
338
454
|
|
|
339
455
|
export function findStaleArtifacts(
|
package/core/version-check.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* version-check.ts — opportunistic npm registry version check.
|
|
3
3
|
*
|
|
4
4
|
* Queries https://registry.npmjs.org/gramatr/latest on a 3s timeout, caches
|
|
5
|
-
* the result for one hour under ~/.
|
|
5
|
+
* the result for one hour under ~/.gramatr/.cache/version-check.json, and
|
|
6
6
|
* reports whether the installed client is behind the published version.
|
|
7
7
|
*
|
|
8
8
|
* Design constraints (see issue #468 sibling work):
|
|
@@ -56,7 +56,7 @@ export function compareVersions(a: string, b: string): number {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
export function getCachePath(home: string = homedir()): string {
|
|
59
|
-
return join(home, '.
|
|
59
|
+
return join(home, '.gramatr', '.cache', 'version-check.json');
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
function readCache(path: string): CacheFile | null {
|
package/desktop/README.md
CHANGED
|
@@ -12,7 +12,7 @@ bun packages/client/desktop/install.ts
|
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
The installer:
|
|
15
|
-
1. Resolves your API key from `~/.
|
|
15
|
+
1. Resolves your API key from `~/.gramatr.json`, `GRAMATR_API_KEY` env, or prompts
|
|
16
16
|
2. Validates connectivity to the gramatr server
|
|
17
17
|
3. Detects platform (macOS or Windows)
|
|
18
18
|
4. Reads existing `claude_desktop_config.json` without overwriting other MCP servers
|
package/gemini/README.md
CHANGED
|
@@ -62,7 +62,7 @@ echo "GRAMATR_API_KEY=your-key-here" > ~/.gemini/extensions/gramatr/.env
|
|
|
62
62
|
|
|
63
63
|
gramatr requires a Bearer token for all MCP calls. The installer handles this by:
|
|
64
64
|
|
|
65
|
-
1. Checking `~/.
|
|
65
|
+
1. Checking `~/.gramatr.json` for an existing token (shared with Claude Code / Codex)
|
|
66
66
|
2. Checking the `GRAMATR_API_KEY` environment variable
|
|
67
67
|
3. Prompting for a token interactively
|
|
68
68
|
|
|
@@ -72,7 +72,7 @@ To authenticate before installing, run:
|
|
|
72
72
|
bun packages/client/bin/gmtr-login.ts
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
This stores the token in `~/.
|
|
75
|
+
This stores the token in `~/.gramatr.json`, which the installer reads automatically.
|
|
76
76
|
|
|
77
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
|
|
|
@@ -92,4 +92,4 @@ After installing and restarting Gemini CLI:
|
|
|
92
92
|
> @gramatr search for recent learning signals
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
If the MCP server responds, the extension is working. If you see auth errors, re-run the installer or check `~/.
|
|
95
|
+
If the MCP server responds, the extension is working. If you see auth errors, re-run the installer or check `~/.gramatr.json`.
|
package/gemini/install.ts
CHANGED
|
@@ -17,6 +17,10 @@ import {
|
|
|
17
17
|
getGramatrExtensionDir,
|
|
18
18
|
readStoredApiKey,
|
|
19
19
|
} from './lib/gemini-install-utils.ts';
|
|
20
|
+
import {
|
|
21
|
+
detectShellRcLegacyEnv,
|
|
22
|
+
formatShellRcLegacyWarning,
|
|
23
|
+
} from '../core/migration.ts';
|
|
20
24
|
|
|
21
25
|
function log(message: string): void {
|
|
22
26
|
process.stdout.write(`${message}\n`);
|
|
@@ -272,6 +276,10 @@ export async function main(): Promise<void> {
|
|
|
272
276
|
log('');
|
|
273
277
|
log(' Restart Gemini CLI to load the extension.');
|
|
274
278
|
log('');
|
|
279
|
+
|
|
280
|
+
// v0.6.2+: detect legacy GMTR_* exports in shell rc and warn
|
|
281
|
+
const rcHits = detectShellRcLegacyEnv(home);
|
|
282
|
+
if (rcHits.length > 0) log(formatShellRcLegacyWarning(rcHits));
|
|
275
283
|
}
|
|
276
284
|
|
|
277
285
|
// Run directly when executed as a script
|
|
@@ -321,7 +321,7 @@ async function main(): Promise<void> {
|
|
|
321
321
|
log('This will:');
|
|
322
322
|
log(` 1. Search gramatr for existing project: ${git.projectName}`);
|
|
323
323
|
log(' 2. Create project entity if not found');
|
|
324
|
-
log(' 3. Link entity to .
|
|
324
|
+
log(' 3. Link entity to .gramatr.json');
|
|
325
325
|
log(' 4. Enable full memory persistence');
|
|
326
326
|
log('');
|
|
327
327
|
} else {
|