@joeytheman/gemini-mcp-tool 1.3.0 → 2.2.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.
Files changed (115) hide show
  1. package/README.md +123 -34
  2. package/dist/__tests__/ask-gemini.test.js +99 -37
  3. package/dist/__tests__/ask-gemini.test.js.map +1 -1
  4. package/dist/__tests__/live-pass.test.d.ts +2 -0
  5. package/dist/__tests__/live-pass.test.d.ts.map +1 -0
  6. package/dist/__tests__/live-pass.test.js +725 -0
  7. package/dist/__tests__/live-pass.test.js.map +1 -0
  8. package/dist/__tests__/screen-review.test.d.ts +2 -0
  9. package/dist/__tests__/screen-review.test.d.ts.map +1 -0
  10. package/dist/__tests__/screen-review.test.js +444 -0
  11. package/dist/__tests__/screen-review.test.js.map +1 -0
  12. package/dist/__tests__/server.test.js +17 -12
  13. package/dist/__tests__/server.test.js.map +1 -1
  14. package/dist/__tests__/simple-tools.test.d.ts +2 -0
  15. package/dist/__tests__/simple-tools.test.d.ts.map +1 -0
  16. package/dist/__tests__/simple-tools.test.js +25 -0
  17. package/dist/__tests__/simple-tools.test.js.map +1 -0
  18. package/dist/__tests__/tools.test.js +63 -0
  19. package/dist/__tests__/tools.test.js.map +1 -1
  20. package/dist/__tests__/utils/agyExecutor.test.d.ts +2 -0
  21. package/dist/__tests__/utils/agyExecutor.test.d.ts.map +1 -0
  22. package/dist/__tests__/utils/agyExecutor.test.js +616 -0
  23. package/dist/__tests__/utils/agyExecutor.test.js.map +1 -0
  24. package/dist/__tests__/utils/agyTranscriptRecovery.test.d.ts +2 -0
  25. package/dist/__tests__/utils/agyTranscriptRecovery.test.d.ts.map +1 -0
  26. package/dist/__tests__/utils/agyTranscriptRecovery.test.js +127 -0
  27. package/dist/__tests__/utils/agyTranscriptRecovery.test.js.map +1 -0
  28. package/dist/__tests__/utils/chunkCache.test.js +26 -2
  29. package/dist/__tests__/utils/chunkCache.test.js.map +1 -1
  30. package/dist/__tests__/utils/commandExecutor.test.js +30 -6
  31. package/dist/__tests__/utils/commandExecutor.test.js.map +1 -1
  32. package/dist/__tests__/utils/fileReferences.test.d.ts +2 -0
  33. package/dist/__tests__/utils/fileReferences.test.d.ts.map +1 -0
  34. package/dist/__tests__/utils/fileReferences.test.js +87 -0
  35. package/dist/__tests__/utils/fileReferences.test.js.map +1 -0
  36. package/dist/__tests__/utils/gitStatusGuard.test.d.ts +2 -0
  37. package/dist/__tests__/utils/gitStatusGuard.test.d.ts.map +1 -0
  38. package/dist/__tests__/utils/gitStatusGuard.test.js +106 -0
  39. package/dist/__tests__/utils/gitStatusGuard.test.js.map +1 -0
  40. package/dist/__tests__/utils/responseCache.test.js +31 -11
  41. package/dist/__tests__/utils/responseCache.test.js.map +1 -1
  42. package/dist/__tests__/utils/test-helpers.d.ts +4 -0
  43. package/dist/__tests__/utils/test-helpers.d.ts.map +1 -1
  44. package/dist/__tests__/utils/test-helpers.js +16 -0
  45. package/dist/__tests__/utils/test-helpers.js.map +1 -1
  46. package/dist/constants.d.ts +68 -26
  47. package/dist/constants.d.ts.map +1 -1
  48. package/dist/constants.js +69 -28
  49. package/dist/constants.js.map +1 -1
  50. package/dist/index.js +2 -2
  51. package/dist/index.js.map +1 -1
  52. package/dist/tools/ask-gemini.tool.d.ts +34 -0
  53. package/dist/tools/ask-gemini.tool.d.ts.map +1 -1
  54. package/dist/tools/ask-gemini.tool.js +38 -22
  55. package/dist/tools/ask-gemini.tool.js.map +1 -1
  56. package/dist/tools/brainstorm.tool.d.ts.map +1 -1
  57. package/dist/tools/brainstorm.tool.js +5 -4
  58. package/dist/tools/brainstorm.tool.js.map +1 -1
  59. package/dist/tools/index.d.ts.map +1 -1
  60. package/dist/tools/index.js +3 -1
  61. package/dist/tools/index.js.map +1 -1
  62. package/dist/tools/live-pass.tool.d.ts +183 -0
  63. package/dist/tools/live-pass.tool.d.ts.map +1 -0
  64. package/dist/tools/live-pass.tool.js +548 -0
  65. package/dist/tools/live-pass.tool.js.map +1 -0
  66. package/dist/tools/registry.d.ts +4 -2
  67. package/dist/tools/registry.d.ts.map +1 -1
  68. package/dist/tools/registry.js +12 -9
  69. package/dist/tools/registry.js.map +1 -1
  70. package/dist/tools/screen-review.tool.d.ts +121 -0
  71. package/dist/tools/screen-review.tool.d.ts.map +1 -0
  72. package/dist/tools/screen-review.tool.js +415 -0
  73. package/dist/tools/screen-review.tool.js.map +1 -0
  74. package/dist/tools/simple-tools.d.ts.map +1 -1
  75. package/dist/tools/simple-tools.js +4 -2
  76. package/dist/tools/simple-tools.js.map +1 -1
  77. package/dist/utils/agyExecutor.d.ts +68 -0
  78. package/dist/utils/agyExecutor.d.ts.map +1 -0
  79. package/dist/utils/agyExecutor.js +443 -0
  80. package/dist/utils/agyExecutor.js.map +1 -0
  81. package/dist/utils/agyReport.d.ts +19 -0
  82. package/dist/utils/agyReport.d.ts.map +1 -0
  83. package/dist/utils/agyReport.js +62 -0
  84. package/dist/utils/agyReport.js.map +1 -0
  85. package/dist/utils/agyTranscriptRecovery.d.ts +16 -0
  86. package/dist/utils/agyTranscriptRecovery.d.ts.map +1 -0
  87. package/dist/utils/agyTranscriptRecovery.js +149 -0
  88. package/dist/utils/agyTranscriptRecovery.js.map +1 -0
  89. package/dist/utils/chunkCache.d.ts.map +1 -1
  90. package/dist/utils/chunkCache.js +19 -5
  91. package/dist/utils/chunkCache.js.map +1 -1
  92. package/dist/utils/commandExecutor.d.ts.map +1 -1
  93. package/dist/utils/commandExecutor.js +15 -7
  94. package/dist/utils/commandExecutor.js.map +1 -1
  95. package/dist/utils/fileReferences.d.ts +45 -0
  96. package/dist/utils/fileReferences.d.ts.map +1 -0
  97. package/dist/utils/fileReferences.js +169 -0
  98. package/dist/utils/fileReferences.js.map +1 -0
  99. package/dist/utils/gitStatusGuard.d.ts +35 -0
  100. package/dist/utils/gitStatusGuard.d.ts.map +1 -0
  101. package/dist/utils/gitStatusGuard.js +82 -0
  102. package/dist/utils/gitStatusGuard.js.map +1 -0
  103. package/dist/utils/responseCache.d.ts +3 -3
  104. package/dist/utils/responseCache.d.ts.map +1 -1
  105. package/dist/utils/responseCache.js +11 -6
  106. package/dist/utils/responseCache.js.map +1 -1
  107. package/package.json +10 -9
  108. package/dist/__tests__/utils/geminiExecutor.test.d.ts +0 -2
  109. package/dist/__tests__/utils/geminiExecutor.test.d.ts.map +0 -1
  110. package/dist/__tests__/utils/geminiExecutor.test.js +0 -337
  111. package/dist/__tests__/utils/geminiExecutor.test.js.map +0 -1
  112. package/dist/utils/geminiExecutor.d.ts +0 -17
  113. package/dist/utils/geminiExecutor.d.ts.map +0 -1
  114. package/dist/utils/geminiExecutor.js +0 -238
  115. package/dist/utils/geminiExecutor.js.map +0 -1
@@ -0,0 +1,62 @@
1
+ import { formatAgyUsageLine, formatConversationLine } from './agyExecutor.js';
2
+ /**
3
+ * Markdown rendering shared by the tools that turn an agy JSON envelope into a
4
+ * report. Everything here is text only: no image data ever reaches the caller.
5
+ */
6
+ const UNPARSABLE_EXCERPT_CHARS = 2000;
7
+ export function bulletList(items) {
8
+ return items.length > 0 ? items.map(item => `- ${item}`).join('\n') : '- none';
9
+ }
10
+ export function section(title, body) {
11
+ return `## ${title}\n${body}`;
12
+ }
13
+ /**
14
+ * The uninvited-edit warning, or '' when the working tree did not change.
15
+ * Every path that hands Gemini a shell must render this, including the ones
16
+ * that bail out early.
17
+ */
18
+ export function formatWorkingTreeSection(diff) {
19
+ const modified = diff.modified ?? [];
20
+ const changed = diff.added.length > 0 || diff.removed.length > 0 || modified.length > 0;
21
+ // A capped run must still say so: "nothing to report" and "we could not look
22
+ // at everything" are different answers, and silence would conflate them.
23
+ if (!changed && !diff.truncated)
24
+ return '';
25
+ return section('Working tree', [
26
+ changed
27
+ ? '- WARNING: the working tree changed during this run.'
28
+ : '- No change detected in the files that were checked.',
29
+ ...diff.added.map(line => `- added: ${line}`),
30
+ ...diff.removed.map(line => `- removed: ${line}`),
31
+ // A file that was already dirty before the run keeps its status line, so
32
+ // only its content hash reveals that it was rewritten.
33
+ ...modified.map(file => `- modified during run: ${file}`),
34
+ ...(diff.truncated ? ['- NOTE: too many dirty files to hash them all; content changes may be under-reported.'] : []),
35
+ ].join('\n'));
36
+ }
37
+ /**
38
+ * Rethrow an agy failure with any uninvited edits appended. A run that timed
39
+ * out or exited non-zero may still have used its shell first, and that warning
40
+ * must not be lost with the error.
41
+ */
42
+ export function failWithWorkingTree(error, diff) {
43
+ const warning = formatWorkingTreeSection(diff);
44
+ const message = error instanceof Error ? error.message : String(error);
45
+ throw warning ? new Error(`${message}\n\n${warning}`) : (error instanceof Error ? error : new Error(message));
46
+ }
47
+ /** Report for an envelope whose structured_output did not match the tool's result schema. */
48
+ export function renderUnparsableEnvelope(envelope, headline, issues, subtitle) {
49
+ const raw = envelope.structured_output !== undefined && envelope.structured_output !== null
50
+ ? JSON.stringify(envelope.structured_output)
51
+ : (envelope.response ?? '');
52
+ return [
53
+ envelope.conversation_id ? formatConversationLine(envelope.conversation_id) : '',
54
+ headline,
55
+ subtitle ?? '',
56
+ formatAgyUsageLine(envelope),
57
+ section('Why', '- Gemini did not return a result matching the schema this tool requested.'),
58
+ section('Schema issues', bulletList(issues)),
59
+ section('Raw answer (truncated)', raw.slice(0, UNPARSABLE_EXCERPT_CHARS)),
60
+ ].filter(Boolean).join('\n\n');
61
+ }
62
+ //# sourceMappingURL=agyReport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agyReport.js","sourceRoot":"","sources":["../../src/utils/agyReport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /** Output that should trigger transcript recovery: empty or the timeout sentinel. */
2
+ export declare function isRecoverableEmptyOutput(stdout: string): boolean;
3
+ /**
4
+ * Recover the latest model response for `conversationId` (or, absent one, for
5
+ * `cwd`) if it was produced after
6
+ * `runStartMs`. Returns the recovered text, or `null` if recovery is disabled,
7
+ * the cwd has no known conversation, the transcript is stale/missing, or any
8
+ * access fails. There is intentionally NO global "newest transcript" fallback —
9
+ * that could return another project's or session's answer.
10
+ */
11
+ export declare function recoverFromTranscript(opts: {
12
+ cwd?: string;
13
+ conversationId?: string;
14
+ runStartMs: number;
15
+ }): string | null;
16
+ //# sourceMappingURL=agyTranscriptRecovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agyTranscriptRecovery.d.ts","sourceRoot":"","sources":["../../src/utils/agyTranscriptRecovery.ts"],"names":[],"mappings":"AAmCA,qFAAqF;AACrF,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAGhE;AA8ED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAClE,MAAM,GAAG,IAAI,CA8Bf"}
@@ -0,0 +1,149 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import * as os from 'os';
4
+ import { Logger } from './logger.js';
5
+ import { AGY_INTERNAL } from '../constants.js';
6
+ /**
7
+ * Best-effort recovery of an agy response from its on-disk transcript.
8
+ *
9
+ * `agy --print` sometimes exits 0 having printed nothing (empty stdout) or only
10
+ * `Error: timed out waiting for response`, even though the model DID produce an
11
+ * answer — that answer is written to the conversation transcript. This module
12
+ * recovers it, keyed deterministically by working directory.
13
+ *
14
+ * FRAGILE: depends entirely on undocumented agy 1.0.13 internals (see
15
+ * AGY_INTERNAL). Every access degrades to `null` on any mismatch — it must
16
+ * never throw, and it must never return another conversation's answer.
17
+ */
18
+ // Allow a small slack between the transcript record's second-precision
19
+ // timestamp and our millisecond run-start clock.
20
+ const FRESHNESS_TOLERANCE_MS = 2000;
21
+ // agy conversation IDs are UUID-shaped. Dots are excluded outright: `.` and
22
+ // `..` are valid path components and would traverse out of the brain directory.
23
+ const CONVERSATION_ID = /^[A-Za-z0-9_-]+$/;
24
+ /** Output that should trigger transcript recovery: empty or the timeout sentinel. */
25
+ export function isRecoverableEmptyOutput(stdout) {
26
+ const trimmed = stdout.trim();
27
+ return trimmed === '' || trimmed === AGY_INTERNAL.TIMEOUT_SENTINEL;
28
+ }
29
+ function recoveryDisabled() {
30
+ return process.env[AGY_INTERNAL.DISABLE_RECOVERY_ENV] === 'true';
31
+ }
32
+ function agyRoot() {
33
+ return path.join(os.homedir(), ...AGY_INTERNAL.ROOT_SEGMENTS);
34
+ }
35
+ /** Candidate forms of cwd to match against last_conversations.json keys. */
36
+ function cwdLookupKeys(cwd) {
37
+ const keys = new Set([cwd]);
38
+ try {
39
+ keys.add(path.resolve(cwd));
40
+ }
41
+ catch { }
42
+ try {
43
+ keys.add(fs.realpathSync(cwd));
44
+ }
45
+ catch { }
46
+ return [...keys];
47
+ }
48
+ function lookupConversationId(cwd) {
49
+ const mapPath = path.join(agyRoot(), ...AGY_INTERNAL.LAST_CONVERSATIONS_SEGMENTS);
50
+ const map = JSON.parse(fs.readFileSync(mapPath, 'utf-8'));
51
+ for (const key of cwdLookupKeys(cwd)) {
52
+ if (typeof map[key] === 'string')
53
+ return map[key];
54
+ }
55
+ return null;
56
+ }
57
+ /** Unwrap a record's `content`, falling back to the raw string if it is not the nested JSON form. */
58
+ function unwrapContent(content) {
59
+ if (typeof content !== 'string')
60
+ return null;
61
+ const trimmed = content.trim();
62
+ if (!trimmed)
63
+ return null;
64
+ try {
65
+ const parsed = JSON.parse(trimmed);
66
+ if (parsed && typeof parsed === 'object' && typeof parsed.content === 'string') {
67
+ const inner = parsed.content.trim();
68
+ return inner || null;
69
+ }
70
+ }
71
+ catch {
72
+ // Not the nested JSON form — use the raw string.
73
+ }
74
+ return trimmed;
75
+ }
76
+ function isFinalResponse(rec) {
77
+ const f = AGY_INTERNAL.TRANSCRIPT_FINAL;
78
+ return rec.type === f.type && rec.source === f.source && rec.status === f.status;
79
+ }
80
+ function readLastResponse(transcriptPath, runStartMs) {
81
+ // readFileSync throws (-> caller returns null) when the transcript is missing.
82
+ const lines = fs.readFileSync(transcriptPath, 'utf-8').split('\n');
83
+ let last = null;
84
+ for (const line of lines) {
85
+ if (!line.trim())
86
+ continue;
87
+ let rec;
88
+ try {
89
+ rec = JSON.parse(line);
90
+ }
91
+ catch {
92
+ continue;
93
+ }
94
+ if (isFinalResponse(rec))
95
+ last = rec;
96
+ }
97
+ if (!last)
98
+ return null;
99
+ // Freshness: prefer the record's own timestamp; fall back to the file mtime
100
+ // (only stat'd when the record has no usable timestamp).
101
+ const createdMs = last.created_at ? Date.parse(last.created_at) : NaN;
102
+ const effectiveMs = Number.isNaN(createdMs) ? fs.statSync(transcriptPath).mtimeMs : createdMs;
103
+ if (effectiveMs < runStartMs - FRESHNESS_TOLERANCE_MS) {
104
+ Logger.debug('[recovery] last transcript response predates this run; treating as stale');
105
+ return null;
106
+ }
107
+ return unwrapContent(last.content);
108
+ }
109
+ /**
110
+ * Recover the latest model response for `conversationId` (or, absent one, for
111
+ * `cwd`) if it was produced after
112
+ * `runStartMs`. Returns the recovered text, or `null` if recovery is disabled,
113
+ * the cwd has no known conversation, the transcript is stale/missing, or any
114
+ * access fails. There is intentionally NO global "newest transcript" fallback —
115
+ * that could return another project's or session's answer.
116
+ */
117
+ export function recoverFromTranscript(opts) {
118
+ if (recoveryDisabled())
119
+ return null;
120
+ const cwd = opts.cwd || process.cwd();
121
+ try {
122
+ // An explicitly resumed conversation is authoritative; the cwd map only
123
+ // knows the LAST conversation for a directory. The ID reaches us from tool
124
+ // arguments and is joined into a path, so only agy's own id shape is
125
+ // accepted — never a traversal out of the brain directory.
126
+ const id = opts.conversationId?.trim() || lookupConversationId(cwd);
127
+ if (!id) {
128
+ Logger.debug(`[recovery] no conversation mapped for cwd ${cwd}`);
129
+ return null;
130
+ }
131
+ // Whatever the source, the id becomes a path component: validate it before
132
+ // any join. The cwd map is on disk and is no more trusted than an argument.
133
+ if (!CONVERSATION_ID.test(id)) {
134
+ Logger.debug(`[recovery] refusing malformed conversation id ${id}`);
135
+ return null;
136
+ }
137
+ const transcriptPath = path.join(agyRoot(), AGY_INTERNAL.BRAIN_DIR, id, ...AGY_INTERNAL.TRANSCRIPT_SEGMENTS);
138
+ const recovered = readLastResponse(transcriptPath, opts.runStartMs);
139
+ if (recovered) {
140
+ Logger.debug(`[recovery] recovered ${recovered.length} chars from transcript for ${id}`);
141
+ }
142
+ return recovered;
143
+ }
144
+ catch (error) {
145
+ Logger.debug(`[recovery] failed: ${error}`);
146
+ return null;
147
+ }
148
+ }
149
+ //# sourceMappingURL=agyTranscriptRecovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agyTranscriptRecovery.js","sourceRoot":"","sources":["../../src/utils/agyTranscriptRecovery.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"chunkCache.d.ts","sourceRoot":"","sources":["../../src/utils/chunkCache.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAsBnD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAwBvE;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,IAAI,CA0B9D;AA+DD,wBAAgB,aAAa,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAehG;AAED,wBAAgB,UAAU,IAAI,IAAI,CAejC"}
1
+ {"version":3,"file":"chunkCache.d.ts","sourceRoot":"","sources":["../../src/utils/chunkCache.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AA+BnD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAwBvE;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,IAAI,CAkC9D;AA+DD,wBAAgB,aAAa,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAehG;AAED,wBAAgB,UAAU,IAAI,IAAI,CAejC"}
@@ -3,9 +3,17 @@ import * as fs from 'fs';
3
3
  import * as path from 'path';
4
4
  import * as os from 'os';
5
5
  import { Logger } from './logger.js';
6
- const CACHE_DIR = path.join(os.tmpdir(), 'gemini-mcp-chunks');
6
+ const CACHE_DIR = path.join(os.tmpdir(), 'agy-mcp-chunks');
7
7
  const CACHE_TTL = 10 * 60 * 1000;
8
8
  const MAX_CACHE_FILES = 50;
9
+ // Cache keys are the first 8 hex chars of a SHA256 digest (see cacheChunks).
10
+ // Validate any user/model-supplied key against this before it ever touches the
11
+ // filesystem — an unvalidated key would let a traversal path escape CACHE_DIR
12
+ // and (on parse error / expiry) get fs.unlinkSync'd.
13
+ const CACHE_KEY_PATTERN = /^[0-9a-f]{8}$/;
14
+ function isValidCacheKey(cacheKey) {
15
+ return typeof cacheKey === 'string' && CACHE_KEY_PATTERN.test(cacheKey);
16
+ }
9
17
  function ensureCacheDir() {
10
18
  if (!fs.existsSync(CACHE_DIR)) {
11
19
  fs.mkdirSync(CACHE_DIR, { recursive: true });
@@ -46,6 +54,13 @@ export function cacheChunks(prompt, chunks) {
46
54
  * @returns The cached chunks or null if expired/not found
47
55
  */
48
56
  export function getChunks(cacheKey) {
57
+ // Reject malformed keys before constructing any path. A non-hex/wrong-length
58
+ // key (e.g. a traversal sequence) returns a cache miss, which surfaces as the
59
+ // friendly "cache miss" text in fetch-chunk rather than an MCP error.
60
+ if (!isValidCacheKey(cacheKey)) {
61
+ Logger.debug(`Rejected invalid cache key: ${cacheKey}`);
62
+ return null;
63
+ }
49
64
  const filePath = path.join(CACHE_DIR, `${cacheKey}.json`);
50
65
  try {
51
66
  if (!fs.existsSync(filePath)) {
@@ -62,11 +77,10 @@ export function getChunks(cacheKey) {
62
77
  return data.chunks;
63
78
  }
64
79
  catch (error) {
80
+ // Treat any read/parse failure as a cache miss. Do NOT delete the file: a
81
+ // transient parse error (e.g. a concurrent writer mid-write) must not nuke a
82
+ // file that may still be valid. Stale files are reaped by mtime elsewhere.
65
83
  Logger.debug(`Cache read error for ${cacheKey}: ${error}`);
66
- try {
67
- fs.unlinkSync(filePath); // Clean up bad file
68
- }
69
- catch { }
70
84
  return null;
71
85
  }
72
86
  }
@@ -1 +1 @@
1
- {"version":3,"file":"chunkCache.js","sourceRoot":"","sources":["../../src/utils/chunkCache.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"chunkCache.js","sourceRoot":"","sources":["../../src/utils/chunkCache.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"commandExecutor.d.ts","sourceRoot":"","sources":["../../src/utils/commandExecutor.ts"],"names":[],"mappings":"AAGA,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,EACxC,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,CAAC,CAmFjB"}
1
+ {"version":3,"file":"commandExecutor.d.ts","sourceRoot":"","sources":["../../src/utils/commandExecutor.ts"],"names":[],"mappings":"AAGA,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,EACxC,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,CAAC,CA2FjB"}
@@ -6,7 +6,7 @@ export async function executeCommand(command, args, onProgress, cwd) {
6
6
  Logger.commandExecution(command, args, startTime);
7
7
  const childProcess = spawn(command, args, {
8
8
  env: process.env,
9
- shell: process.platform === "win32",
9
+ shell: false,
10
10
  stdio: ["ignore", "pipe", "pipe"],
11
11
  ...(cwd && { cwd }),
12
12
  });
@@ -14,6 +14,7 @@ export async function executeCommand(command, args, onProgress, cwd) {
14
14
  const stdoutChunks = [];
15
15
  const stderrChunks = [];
16
16
  let isResolved = false;
17
+ let resourceExhausted = false;
17
18
  childProcess.stdout.on("data", (data) => {
18
19
  const chunk = data.toString();
19
20
  stdoutChunks.push(chunk);
@@ -26,8 +27,10 @@ export async function executeCommand(command, args, onProgress, cwd) {
26
27
  childProcess.stderr.on("data", (data) => {
27
28
  const chunk = data.toString();
28
29
  stderrChunks.push(chunk);
29
- // find RESOURCE_EXHAUSTED when Gemini Pro quota is exceeded
30
- if (chunk.includes("RESOURCE_EXHAUSTED")) {
30
+ // Surface resource/quota failures from the backend without attempting
31
+ // fallback. Build the structured error once, on the first marker.
32
+ if (!resourceExhausted && chunk.includes("RESOURCE_EXHAUSTED")) {
33
+ resourceExhausted = true;
31
34
  const stderrSoFar = stderrChunks.join('');
32
35
  const modelMatch = stderrSoFar.match(/Quota exceeded for quota metric '([^']+)'/);
33
36
  const statusMatch = stderrSoFar.match(/status["\s]*[:=]\s*(\d+)/);
@@ -42,11 +45,11 @@ export async function executeCommand(command, args, onProgress, cwd) {
42
45
  details: {
43
46
  model: model,
44
47
  reason: reason,
45
- statusText: "Too Many Requests -- > try using gemini-3-flash-preview by asking",
48
+ statusText: `Too Many Requests from Antigravity CLI backend`,
46
49
  }
47
50
  }
48
51
  };
49
- Logger.error(`Gemini Quota Error: ${JSON.stringify(errorJson, null, 2)}`);
52
+ Logger.error(`Antigravity resource error: ${JSON.stringify(errorJson, null, 2)}`);
50
53
  }
51
54
  });
52
55
  childProcess.on("error", (error) => {
@@ -62,14 +65,19 @@ export async function executeCommand(command, args, onProgress, cwd) {
62
65
  // Join array buffers efficiently
63
66
  const stdout = stdoutChunks.join('');
64
67
  const stderr = stderrChunks.join('');
65
- if (code === 0) {
68
+ // Treat an exit-0 run as failed only when it reported quota exhaustion
69
+ // AND produced no usable stdout — so a quota error with empty output is
70
+ // surfaced (not silently "recovered"), while a run that still returned a
71
+ // real answer is kept.
72
+ const quotaFailure = resourceExhausted && stdout.trim() === "";
73
+ if (code === 0 && !quotaFailure) {
66
74
  Logger.commandComplete(startTime, code, stdout.length);
67
75
  resolve(stdout.trim());
68
76
  }
69
77
  else {
70
78
  Logger.commandComplete(startTime, code);
71
79
  Logger.error(`Failed with exit code ${code}`);
72
- const errorMessage = stderr.trim() || "Unknown error";
80
+ const errorMessage = stderr.trim() || (resourceExhausted ? "RESOURCE_EXHAUSTED" : "Unknown error");
73
81
  reject(new Error(`Command failed with exit code ${code}: ${errorMessage}`));
74
82
  }
75
83
  }
@@ -1 +1 @@
1
- {"version":3,"file":"commandExecutor.js","sourceRoot":"","sources":["../../src/utils/commandExecutor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"commandExecutor.js","sourceRoot":"","sources":["../../src/utils/commandExecutor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * File-reference handling for agy prompts.
3
+ *
4
+ * Why this exists: `agy --print` resolves an `@file` reference ONLY when the
5
+ * file's directory is part of the workspace (`--add-dir`). cwd is never
6
+ * auto-registered. An `@file` outside any added dir makes agy hang until
7
+ * `--print-timeout`. So for every reference in a prompt that actually resolves
8
+ * to a real file under a trusted root (the working directory or an explicitly
9
+ * supplied includeDirectories entry), we register that root with `--add-dir`.
10
+ *
11
+ * We never inline file contents: agy reads the file itself, keeping the
12
+ * `--print` argv small and preserving the large-file use case (inlining would
13
+ * risk E2BIG). References that don't resolve under a trusted root are left
14
+ * untouched — the prompt still runs.
15
+ */
16
+ /** Normalize a comma-separated string or array into a trimmed, non-empty list. */
17
+ export declare function normalizeList(value?: string | string[]): string[];
18
+ /** True when `p` resolves to a filesystem root (e.g. `/` or `C:\`). */
19
+ export declare function isFilesystemRoot(p: string): boolean;
20
+ /**
21
+ * Resolve a tool's `workingDirectory` argument: it must be an absolute path to
22
+ * an existing directory that is not a filesystem root, because it becomes both
23
+ * agy's cwd and its workspace root.
24
+ */
25
+ export declare function resolveWorkingDirectory(requested: string): string;
26
+ /**
27
+ * `target` with its deepest EXISTING ancestor realpath'd, so a path that does
28
+ * not exist yet can still be compared against a realpath'd root.
29
+ */
30
+ export declare function realpathOfExistingPrefix(target: string): string;
31
+ /** True when `candidate` is `root` itself or lives under it. */
32
+ export declare function isWithin(root: string, candidate: string): boolean;
33
+ /**
34
+ * Normalize file references in a prompt and collect the `--add-dir` roots agy
35
+ * needs to read them. Returns the (possibly rewritten) prompt and the deduped
36
+ * list of trust roots that contain a resolved reference. Never throws.
37
+ */
38
+ export declare function resolveFileReferences(prompt: string, opts: {
39
+ cwd?: string;
40
+ includeDirectories?: string | string[];
41
+ }): {
42
+ prompt: string;
43
+ addDirs: string[];
44
+ };
45
+ //# sourceMappingURL=fileReferences.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileReferences.d.ts","sourceRoot":"","sources":["../../src/utils/fileReferences.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AAEH,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,CAIjE;AAgBD,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAOnD;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAejE;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAa/D;AAED,gEAAgE;AAChE,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAGjE;AAyCD;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;CAAE,GAC7D;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CA0BvC"}
@@ -0,0 +1,169 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { ERROR_MESSAGES } from '../constants.js';
4
+ /**
5
+ * File-reference handling for agy prompts.
6
+ *
7
+ * Why this exists: `agy --print` resolves an `@file` reference ONLY when the
8
+ * file's directory is part of the workspace (`--add-dir`). cwd is never
9
+ * auto-registered. An `@file` outside any added dir makes agy hang until
10
+ * `--print-timeout`. So for every reference in a prompt that actually resolves
11
+ * to a real file under a trusted root (the working directory or an explicitly
12
+ * supplied includeDirectories entry), we register that root with `--add-dir`.
13
+ *
14
+ * We never inline file contents: agy reads the file itself, keeping the
15
+ * `--print` argv small and preserving the large-file use case (inlining would
16
+ * risk E2BIG). References that don't resolve under a trusted root are left
17
+ * untouched — the prompt still runs.
18
+ */
19
+ /** Normalize a comma-separated string or array into a trimmed, non-empty list. */
20
+ export function normalizeList(value) {
21
+ if (!value)
22
+ return [];
23
+ const items = Array.isArray(value) ? value : value.split(',');
24
+ return items.map(item => item.trim()).filter(Boolean);
25
+ }
26
+ // `@` reference: an `@` that is NOT part of an email/scoped continuation
27
+ // (preceded by a word char, another `@`, `/`, or `.`), then a run of non-space,
28
+ // non-`@` characters. Existence under a trust root — not shape — gates whether
29
+ // it is treated as a file, so extensionless names (Makefile, LICENSE) work.
30
+ const AT_REF = /(?<![\w@/.])@([^\s@]+)/g;
31
+ // `file:` reference: `file:` at a word boundary, not a `file://` URL.
32
+ const FILE_REF = /(?<![\w])file:(?!\/\/)(\S+)/g;
33
+ const TRAILING_PUNCT = /[),.;:!?\]}'"`]+$/;
34
+ function stripTrailingPunct(token) {
35
+ return token.replace(TRAILING_PUNCT, '');
36
+ }
37
+ /** True when `p` resolves to a filesystem root (e.g. `/` or `C:\`). */
38
+ export function isFilesystemRoot(p) {
39
+ try {
40
+ const resolved = path.resolve(p);
41
+ return resolved === path.parse(resolved).root;
42
+ }
43
+ catch {
44
+ return false;
45
+ }
46
+ }
47
+ /**
48
+ * Resolve a tool's `workingDirectory` argument: it must be an absolute path to
49
+ * an existing directory that is not a filesystem root, because it becomes both
50
+ * agy's cwd and its workspace root.
51
+ */
52
+ export function resolveWorkingDirectory(requested) {
53
+ // Realpath'd, so that later containment checks compare like with like: on
54
+ // macOS /tmp/x and /private/tmp/x are the same directory but not the same
55
+ // string, and a frame reported under one would look OUTSIDE the other.
56
+ let real = null;
57
+ try {
58
+ const candidate = fs.realpathSync(path.resolve(requested));
59
+ if (fs.statSync(candidate).isDirectory())
60
+ real = candidate;
61
+ }
62
+ catch {
63
+ real = null;
64
+ }
65
+ if (!path.isAbsolute(requested) || !real || isFilesystemRoot(real)) {
66
+ throw new Error(`${ERROR_MESSAGES.INVALID_WORKING_DIRECTORY}. Received '${requested}'.`);
67
+ }
68
+ return real;
69
+ }
70
+ /**
71
+ * `target` with its deepest EXISTING ancestor realpath'd, so a path that does
72
+ * not exist yet can still be compared against a realpath'd root.
73
+ */
74
+ export function realpathOfExistingPrefix(target) {
75
+ let current = path.resolve(target);
76
+ const missing = [];
77
+ for (;;) {
78
+ try {
79
+ return path.join(fs.realpathSync(current), ...missing);
80
+ }
81
+ catch {
82
+ const parent = path.dirname(current);
83
+ if (parent === current)
84
+ return path.resolve(target);
85
+ missing.unshift(path.basename(current));
86
+ current = parent;
87
+ }
88
+ }
89
+ }
90
+ /** True when `candidate` is `root` itself or lives under it. */
91
+ export function isWithin(root, candidate) {
92
+ const rel = path.relative(root, candidate);
93
+ return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));
94
+ }
95
+ /**
96
+ * Realpath'd, existing directories that references may resolve under: the
97
+ * working directory plus each includeDirectories entry.
98
+ */
99
+ function resolveTrustRoots(cwd, includeDirectories) {
100
+ const baseCwd = cwd || process.cwd();
101
+ const candidates = [baseCwd, ...normalizeList(includeDirectories).map(d => path.resolve(baseCwd, d))];
102
+ const roots = [];
103
+ for (const candidate of candidates) {
104
+ try {
105
+ const real = fs.realpathSync(candidate);
106
+ if (fs.statSync(real).isDirectory())
107
+ roots.push(real);
108
+ }
109
+ catch {
110
+ // Non-existent / unreadable root — skip.
111
+ }
112
+ }
113
+ return [...new Set(roots)];
114
+ }
115
+ /**
116
+ * Resolve a token to the trust root it lives under. agy joins the reference
117
+ * path to an added dir, so the root that resolves the token is exactly what
118
+ * must be added. Returns null for tokens that don't resolve to a real file, or
119
+ * that escape every root via absolute/`..`/symlink.
120
+ */
121
+ function rootForToken(rawToken, roots) {
122
+ const token = stripTrailingPunct(rawToken);
123
+ if (!token)
124
+ return null;
125
+ for (const root of roots) {
126
+ try {
127
+ const real = fs.realpathSync(path.resolve(root, token));
128
+ if (isWithin(root, real) && fs.statSync(real).isFile())
129
+ return root;
130
+ }
131
+ catch {
132
+ // Does not resolve under this root — try the next one.
133
+ }
134
+ }
135
+ return null;
136
+ }
137
+ /**
138
+ * Normalize file references in a prompt and collect the `--add-dir` roots agy
139
+ * needs to read them. Returns the (possibly rewritten) prompt and the deduped
140
+ * list of trust roots that contain a resolved reference. Never throws.
141
+ */
142
+ export function resolveFileReferences(prompt, opts) {
143
+ const roots = resolveTrustRoots(opts.cwd, opts.includeDirectories);
144
+ if (roots.length === 0)
145
+ return { prompt, addDirs: [] };
146
+ const addDirs = new Set();
147
+ // Rewrite a `file:` reference to `@` ONLY when it resolves to a real file
148
+ // under a trusted root — otherwise we'd hand agy a dangling @ref that hangs.
149
+ // The converted token drops trailing punctuation so agy sees the exact path.
150
+ const rewritten = prompt.replace(FILE_REF, (match, rawToken) => {
151
+ const root = rootForToken(rawToken, roots);
152
+ if (!root)
153
+ return match;
154
+ addDirs.add(root);
155
+ return `@${stripTrailingPunct(rawToken)}`;
156
+ });
157
+ // Collect the trust root for every `@` reference that resolves.
158
+ AT_REF.lastIndex = 0;
159
+ let m;
160
+ while ((m = AT_REF.exec(rewritten)) !== null) {
161
+ if (addDirs.size >= roots.length)
162
+ break; // every root already registered
163
+ const root = rootForToken(m[1], roots);
164
+ if (root)
165
+ addDirs.add(root);
166
+ }
167
+ return { prompt: rewritten, addDirs: [...addDirs] };
168
+ }
169
+ //# sourceMappingURL=fileReferences.js.map