gramatr 0.3.58 → 0.3.59
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/bin/gmtr-login.ts +64 -28
- package/bin/gramatr.ts +4 -2
- package/bin/install.ts +17 -3
- package/core/auth.ts +4 -4
- package/desktop/build-mcpb.ts +1 -2
- package/hooks/GMTRPromptEnricher.hook.ts +2 -1
- package/hooks/GMTRRatingCapture.hook.ts +1 -1
- package/hooks/lib/gmtr-hook-utils.ts +1 -2
- package/hooks/lib/notify.ts +1 -1
- package/hooks/session-end.hook.ts +1 -1
- package/package.json +2 -1
package/bin/gmtr-login.ts
CHANGED
|
@@ -496,37 +496,54 @@ async function loginBrowser(): Promise<void> {
|
|
|
496
496
|
}
|
|
497
497
|
|
|
498
498
|
// ── CLI ──
|
|
499
|
+
//
|
|
500
|
+
// Defense in depth (Fix A' for Windows top-level await crash):
|
|
501
|
+
// All CLI code is wrapped in an async `main()` and invoked via a
|
|
502
|
+
// module-run guard. This keeps the module importable without firing
|
|
503
|
+
// side effects and avoids top-level await entirely, so the file stays
|
|
504
|
+
// safe even if the package ever loses `"type": "module"` or tsx
|
|
505
|
+
// changes its default target to CJS.
|
|
506
|
+
|
|
507
|
+
export async function main(): Promise<void> {
|
|
508
|
+
const args = process.argv.slice(2);
|
|
509
|
+
|
|
510
|
+
if (args.includes('--status') || args.includes('status')) {
|
|
511
|
+
await showStatus();
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
499
514
|
|
|
500
|
-
|
|
515
|
+
if (args.includes('--logout') || args.includes('logout')) {
|
|
516
|
+
await logout();
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
501
519
|
|
|
502
|
-
if (args.includes('--
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
520
|
+
if (args.includes('--token') || args.includes('-t')) {
|
|
521
|
+
const tokenIdx = args.indexOf('--token') !== -1 ? args.indexOf('--token') : args.indexOf('-t');
|
|
522
|
+
const token = args[tokenIdx + 1];
|
|
523
|
+
if (!token) {
|
|
524
|
+
// Interactive paste mode — like Claude's login
|
|
525
|
+
console.log('\n Paste your gramatr token below.');
|
|
526
|
+
console.log(' (API keys start with aios_sk_ or gmtr_sk_)\n');
|
|
527
|
+
process.stdout.write(' Token: ');
|
|
528
|
+
|
|
529
|
+
const { createInterface } = await import('readline');
|
|
530
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
531
|
+
const pastedToken = await new Promise<string>((resolve) => {
|
|
532
|
+
rl.on('line', (line: string) => { rl.close(); resolve(line.trim()); });
|
|
533
|
+
});
|
|
534
|
+
if (!pastedToken) {
|
|
535
|
+
console.log(' No token provided.\n');
|
|
536
|
+
process.exit(1);
|
|
537
|
+
}
|
|
538
|
+
await loginWithToken(pastedToken);
|
|
539
|
+
} else {
|
|
540
|
+
await loginWithToken(token);
|
|
523
541
|
}
|
|
524
|
-
|
|
525
|
-
} else {
|
|
526
|
-
await loginWithToken(token);
|
|
542
|
+
return;
|
|
527
543
|
}
|
|
528
|
-
|
|
529
|
-
|
|
544
|
+
|
|
545
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
546
|
+
console.log(`
|
|
530
547
|
gmtr-login — Authenticate with the gramatr server
|
|
531
548
|
|
|
532
549
|
Usage:
|
|
@@ -541,7 +558,26 @@ if (args.includes('--status') || args.includes('status')) {
|
|
|
541
558
|
Server: ${SERVER_BASE}
|
|
542
559
|
Dashboard: ${DASHBOARD_BASE}
|
|
543
560
|
`);
|
|
544
|
-
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
|
|
545
564
|
// Default: browser login flow
|
|
546
565
|
await loginBrowser();
|
|
547
566
|
}
|
|
567
|
+
|
|
568
|
+
// Module-run guard. Works both when invoked directly via
|
|
569
|
+
// `tsx bin/gmtr-login.ts` and when imported from another module
|
|
570
|
+
// (tests, programmatic use). Under ESM, import.meta.url is the
|
|
571
|
+
// canonical check; we also accept a path-suffix match as a belt.
|
|
572
|
+
const invokedAs = process.argv[1] || '';
|
|
573
|
+
const isMain =
|
|
574
|
+
import.meta.url === `file://${invokedAs}` ||
|
|
575
|
+
invokedAs.endsWith('gmtr-login.ts') ||
|
|
576
|
+
invokedAs.endsWith('gmtr-login.js');
|
|
577
|
+
|
|
578
|
+
if (isMain) {
|
|
579
|
+
main().catch((err) => {
|
|
580
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
581
|
+
process.exit(1);
|
|
582
|
+
});
|
|
583
|
+
}
|
package/bin/gramatr.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { spawnSync } from 'child_process';
|
|
4
4
|
import { existsSync } from 'fs';
|
|
5
|
+
import { createRequire } from 'module';
|
|
5
6
|
import { homedir } from 'os';
|
|
6
7
|
import { dirname, join } from 'path';
|
|
7
8
|
import { fileURLToPath } from 'url';
|
|
@@ -47,8 +48,9 @@ function runTs(script: string, extraArgs: string[] = []): void {
|
|
|
47
48
|
// Resolve tsx from this package's node_modules (not CWD) so `npx tsx` works
|
|
48
49
|
// even on hosts where the user hasn't globally installed tsx.
|
|
49
50
|
try {
|
|
50
|
-
|
|
51
|
-
const
|
|
51
|
+
// ESM-safe: createRequire gives us require.resolve for finding tsx on disk.
|
|
52
|
+
const req = createRequire(import.meta.url);
|
|
53
|
+
const tsxCli = join(dirname(req.resolve('tsx/package.json')), 'dist', 'cli.mjs');
|
|
52
54
|
run(process.execPath, [tsxCli, script, ...extraArgs]);
|
|
53
55
|
} catch {
|
|
54
56
|
// Fallback: global npx tsx
|
package/bin/install.ts
CHANGED
|
@@ -123,9 +123,23 @@ function dirSize(dir: string): string {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
function which(cmd: string): string | null {
|
|
126
|
+
// Platform-aware: `command -v` is a POSIX shell builtin that does not exist
|
|
127
|
+
// on Windows cmd.exe / PowerShell. Use `where` on Windows.
|
|
128
|
+
// stdio: ['ignore', 'pipe', 'ignore'] suppresses the "not found" stderr
|
|
129
|
+
// noise that previously leaked to the user's terminal during install.
|
|
130
|
+
// Closes #483 Bug 1.
|
|
131
|
+
const probe = process.platform === 'win32' ? `where ${cmd}` : `command -v ${cmd}`;
|
|
126
132
|
try {
|
|
127
|
-
|
|
128
|
-
|
|
133
|
+
const out = execSync(probe, {
|
|
134
|
+
encoding: 'utf8',
|
|
135
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
136
|
+
});
|
|
137
|
+
// `where` on Windows returns multiple lines when the binary is on PATH
|
|
138
|
+
// multiple times; take the first match.
|
|
139
|
+
return out.trim().split(/\r?\n/)[0] || null;
|
|
140
|
+
} catch {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
129
143
|
}
|
|
130
144
|
|
|
131
145
|
function copyFileIfExists(src: string, dest: string, executable = false): boolean {
|
|
@@ -375,7 +389,7 @@ async function handleAuth(legacyToken: string): Promise<{ url: string; token: st
|
|
|
375
389
|
log('');
|
|
376
390
|
log(e?.message || String(e));
|
|
377
391
|
log('');
|
|
378
|
-
log('Authentication skipped — run `gramatr login` later to authenticate');
|
|
392
|
+
log('Authentication skipped — run `npx gramatr@latest login` later to authenticate');
|
|
379
393
|
log('');
|
|
380
394
|
}
|
|
381
395
|
|
package/core/auth.ts
CHANGED
|
@@ -121,13 +121,13 @@ function spawnOAuthLogin(): { ok: boolean; reason?: string } {
|
|
|
121
121
|
const HEADLESS_ERROR =
|
|
122
122
|
"No gramatr credentials found. Set one of:\n" +
|
|
123
123
|
" - GRAMATR_API_KEY environment variable\n" +
|
|
124
|
-
" - Run: gramatr login (interactive, recommended)\n" +
|
|
125
|
-
" - Run: gramatr add-api-key (for headless / CI use)\n" +
|
|
124
|
+
" - Run: npx gramatr@latest login (interactive, recommended)\n" +
|
|
125
|
+
" - Run: npx gramatr@latest add-api-key (for headless / CI use)\n" +
|
|
126
126
|
"Then re-run the install.";
|
|
127
127
|
|
|
128
128
|
const OAUTH_FAILED_ERROR =
|
|
129
|
-
'OAuth login failed. Run "gramatr login" to retry, ' +
|
|
130
|
-
'or "gramatr add-api-key" to use an API key instead.';
|
|
129
|
+
'OAuth login failed. Run "npx gramatr@latest login" to retry, ' +
|
|
130
|
+
'or "npx gramatr@latest add-api-key" to use an API key instead.';
|
|
131
131
|
|
|
132
132
|
/**
|
|
133
133
|
* Resolve a gramatr auth token, OAuth-first.
|
package/desktop/build-mcpb.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* Usage: bun desktop/build-mcpb.ts [--out <path>]
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
15
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
|
|
16
16
|
import { join, dirname } from 'path';
|
|
17
17
|
import { fileURLToPath } from 'url';
|
|
18
18
|
|
|
@@ -65,7 +65,6 @@ interface McpbManifest {
|
|
|
65
65
|
function readPackageVersion(): string {
|
|
66
66
|
try {
|
|
67
67
|
const pkgPath = join(clientDir, 'package.json');
|
|
68
|
-
const { readFileSync } = require('fs');
|
|
69
68
|
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
70
69
|
return pkg.version || '0.0.0';
|
|
71
70
|
} catch {
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* - Token savings metadata
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
+
import { readFileSync } from 'fs';
|
|
23
24
|
import { getGitContext } from './lib/gmtr-hook-utils.ts';
|
|
24
25
|
import {
|
|
25
26
|
persistClassificationResult,
|
|
@@ -51,7 +52,7 @@ function resolveProjectId(): string | null {
|
|
|
51
52
|
// Read from the context file written by session-start.sh
|
|
52
53
|
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
53
54
|
const contextPath = `${home}/.claude/current-project-context.json`;
|
|
54
|
-
const context = JSON.parse(
|
|
55
|
+
const context = JSON.parse(readFileSync(contextPath, 'utf8'));
|
|
55
56
|
const remote = context.git_remote;
|
|
56
57
|
if (!remote || remote === 'no-remote') return null;
|
|
57
58
|
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import { appendFileSync, mkdirSync, existsSync } from 'fs';
|
|
27
|
+
import { spawn } from 'child_process';
|
|
27
28
|
import { join, dirname } from 'path';
|
|
28
29
|
import { getGmtrDir } from './lib/paths';
|
|
29
30
|
|
|
@@ -140,7 +141,6 @@ function notifyLowRating(rating: number, comment?: string): void {
|
|
|
140
141
|
const msg = comment
|
|
141
142
|
? `Rating ${rating}/10: ${comment}`
|
|
142
143
|
: `Rating ${rating}/10 received`;
|
|
143
|
-
const { spawn } = require('child_process');
|
|
144
144
|
spawn('osascript', ['-e', `display notification "${msg}" with title "gramatr" subtitle "Low Rating Alert"`], {
|
|
145
145
|
stdio: 'ignore',
|
|
146
146
|
detached: true,
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { execSync } from 'child_process';
|
|
11
|
-
import { readFileSync, writeFileSync, renameSync, mkdirSync, existsSync } from 'fs';
|
|
11
|
+
import { readFileSync, writeFileSync, renameSync, mkdirSync, existsSync, appendFileSync } from 'fs';
|
|
12
12
|
import { join, basename } from 'path';
|
|
13
13
|
|
|
14
14
|
// ── Types ──
|
|
@@ -680,7 +680,6 @@ export function appendLine(filePath: string, line: string): void {
|
|
|
680
680
|
if (dir && !existsSync(dir)) {
|
|
681
681
|
mkdirSync(dir, { recursive: true });
|
|
682
682
|
}
|
|
683
|
-
const { appendFileSync } = require('fs');
|
|
684
683
|
appendFileSync(filePath, line + '\n', 'utf8');
|
|
685
684
|
}
|
|
686
685
|
|
package/hooks/lib/notify.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { platform } from 'os';
|
|
13
|
+
import { spawn } from 'child_process';
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Send a local push notification.
|
|
@@ -24,7 +25,6 @@ export function notify(subtitle: string, message: string): void {
|
|
|
24
25
|
const safeMsg = message.replace(/"/g, '\\"');
|
|
25
26
|
const safeSub = subtitle.replace(/"/g, '\\"');
|
|
26
27
|
|
|
27
|
-
const { spawn } = require('child_process');
|
|
28
28
|
spawn('osascript', ['-e',
|
|
29
29
|
`display notification "${safeMsg}" with title "gramatr" subtitle "${safeSub}"`,
|
|
30
30
|
], { stdio: 'ignore', detached: true }).unref();
|
|
@@ -94,7 +94,7 @@ async function main(): Promise<void> {
|
|
|
94
94
|
// Build session summary from git data
|
|
95
95
|
let branch = 'unknown';
|
|
96
96
|
try {
|
|
97
|
-
const { execSync } =
|
|
97
|
+
const { execSync } = await import('child_process');
|
|
98
98
|
branch = execSync('git rev-parse --abbrev-ref HEAD', {
|
|
99
99
|
encoding: 'utf8',
|
|
100
100
|
stdio: ['pipe', 'pipe', 'pipe'],
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gramatr",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.59",
|
|
4
4
|
"description": "grāmatr — context engineering layer for AI coding agents. Every prompt gets a pre-computed intelligence packet: decision routing, capability audit, behavioral directives, memory pre-load, and ISC scaffolds. Continuity across sessions for Claude Code, Codex, and Gemini CLI.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
8
9
|
"url": "https://github.com/gramatr/gramatr.git",
|