jev-agent-tools 0.1.4 → 0.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 (131) hide show
  1. package/CHANGELOG.md +70 -1
  2. package/CONTRIBUTING.md +40 -0
  3. package/README.md +42 -9
  4. package/SECURITY.md +27 -0
  5. package/dist/adapters/analysis-context.js +75 -0
  6. package/dist/adapters/ask-files.js +189 -0
  7. package/dist/adapters/ask-proof.js +144 -0
  8. package/dist/adapters/ask-syntax.js +385 -0
  9. package/dist/adapters/canonical-path.js +17 -0
  10. package/dist/adapters/command.js +181 -0
  11. package/dist/adapters/docs.js +172 -0
  12. package/dist/adapters/exec.js +207 -0
  13. package/dist/adapters/files.js +293 -0
  14. package/dist/adapters/find.js +122 -0
  15. package/dist/adapters/git-base.js +26 -0
  16. package/dist/adapters/git-inventory.js +71 -0
  17. package/dist/adapters/git.js +439 -0
  18. package/dist/adapters/locate-file.js +159 -0
  19. package/dist/adapters/output-lines.js +46 -0
  20. package/dist/adapters/private-storage.js +98 -0
  21. package/dist/adapters/risk-callers.js +426 -0
  22. package/dist/adapters/runner-version.js +78 -0
  23. package/dist/adapters/shell.js +76 -0
  24. package/dist/adapters/syntax.js +187 -0
  25. package/dist/adapters/test-inventory.js +131 -0
  26. package/dist/adapters/usage.js +20 -0
  27. package/dist/adapters/utf8.js +47 -0
  28. package/dist/configuration.js +257 -0
  29. package/dist/constants.js +119 -0
  30. package/dist/core/ask-closure.js +282 -0
  31. package/dist/core/ask-proof.js +1 -0
  32. package/dist/core/ask-references.js +194 -0
  33. package/dist/core/asks.js +436 -0
  34. package/dist/core/batches.js +65 -0
  35. package/dist/core/command-output.js +224 -0
  36. package/dist/core/diff.js +178 -0
  37. package/dist/core/docs.js +302 -0
  38. package/dist/core/find.js +108 -0
  39. package/dist/core/git.js +1 -0
  40. package/dist/core/imports.js +550 -0
  41. package/dist/core/integrity.js +45 -0
  42. package/dist/core/lexical.js +132 -0
  43. package/dist/core/locate.js +169 -0
  44. package/dist/core/output.js +120 -0
  45. package/dist/core/pointer.js +29 -0
  46. package/dist/core/risk-callers.js +851 -0
  47. package/dist/core/runner-version.js +45 -0
  48. package/dist/core/sections.js +230 -0
  49. package/dist/core/state.js +44 -0
  50. package/dist/core/syntax.js +1 -0
  51. package/dist/core/test-commands.js +334 -0
  52. package/dist/core/test-coverage.js +74 -0
  53. package/dist/core/test-discovery.js +1382 -0
  54. package/dist/core/test-evidence.js +527 -0
  55. package/dist/core/test-state.js +81 -0
  56. package/dist/core/truncate.js +12 -0
  57. package/dist/core/units.js +349 -0
  58. package/dist/describe.js +23 -0
  59. package/dist/guide.js +33 -0
  60. package/dist/host.js +24 -0
  61. package/dist/jev/client.js +434 -0
  62. package/dist/jev/pool.js +54 -0
  63. package/dist/jev/types.js +1 -0
  64. package/dist/mcp/main.js +124 -0
  65. package/dist/mcp/protocol.js +187 -0
  66. package/dist/mcp/tools.js +116 -0
  67. package/dist/presets/docs.js +62 -0
  68. package/dist/presets/risk.js +179 -0
  69. package/dist/presets/spec.js +81 -0
  70. package/dist/presets/witnesses.js +249 -0
  71. package/dist/render.js +42 -0
  72. package/dist/result.js +3 -0
  73. package/dist/runtime.js +1 -0
  74. package/dist/session.js +147 -0
  75. package/dist/texts/ask-files.js +1 -0
  76. package/dist/texts/ask.js +2 -0
  77. package/dist/texts/check-diff.js +17 -0
  78. package/dist/texts/configuration.js +1 -0
  79. package/dist/texts/find.js +14 -0
  80. package/dist/texts/guide.js +16 -0
  81. package/dist/texts/locate.js +10 -0
  82. package/dist/texts/select-tests.js +2 -0
  83. package/dist/tools/ask-files.js +217 -0
  84. package/dist/tools/ask-schema.js +70 -0
  85. package/dist/tools/ask.js +686 -0
  86. package/dist/tools/check-diff.js +402 -0
  87. package/dist/tools/docs-check.js +299 -0
  88. package/dist/tools/find.js +389 -0
  89. package/dist/tools/locate.js +303 -0
  90. package/dist/tools/select-tests.js +567 -0
  91. package/dist/tools/spec-check.js +166 -0
  92. package/docs/adr/0001-strict-typescript-pure-core-offline-tests.md +31 -0
  93. package/docs/adr/0002-one-http-protocol-across-hosts.md +17 -0
  94. package/docs/adr/0003-explicit-scope-conservative-automation.md +19 -0
  95. package/docs/adr/0004-compiled-typed-intents.md +19 -0
  96. package/docs/adr/0005-evidence-construction-before-judgment.md +19 -0
  97. package/docs/adr/0006-visible-uncertainty-constrained-controls.md +21 -0
  98. package/docs/adr/0007-bounded-evidence-visible-limits.md +21 -0
  99. package/docs/adr/0008-static-test-discovery-conservative-plans.md +19 -0
  100. package/docs/adr/0009-session-cache-requested-model-identity.md +17 -0
  101. package/docs/adr/0010-mcp-server-thin-host.md +23 -0
  102. package/docs/agent-instructions.md +91 -0
  103. package/docs/design.md +3 -3
  104. package/docs/mcp.md +231 -0
  105. package/package.json +19 -4
  106. package/server.json +57 -0
  107. package/src/adapters/canonical-path.ts +18 -0
  108. package/src/adapters/command.ts +7 -4
  109. package/src/adapters/exec.ts +226 -0
  110. package/src/adapters/private-storage.ts +143 -0
  111. package/src/adapters/risk-callers.ts +4 -2
  112. package/src/adapters/shell.ts +97 -0
  113. package/src/configuration.ts +39 -12
  114. package/src/constants.ts +11 -0
  115. package/src/core/command-output.ts +17 -1
  116. package/src/host.ts +11 -0
  117. package/src/jev/client.ts +12 -0
  118. package/src/jev/types.ts +6 -0
  119. package/src/mcp/main.ts +135 -0
  120. package/src/mcp/protocol.ts +282 -0
  121. package/src/mcp/tools.ts +166 -0
  122. package/src/session.ts +59 -0
  123. package/src/setup.ts +13 -5
  124. package/src/tools/ask-files.ts +5 -7
  125. package/src/tools/ask.ts +26 -22
  126. package/src/tools/check-diff.ts +8 -5
  127. package/src/tools/docs-check.ts +1 -0
  128. package/src/tools/find.ts +5 -2
  129. package/src/tools/locate.ts +5 -8
  130. package/src/tools/select-tests.ts +7 -4
  131. package/src/tools/spec-check.ts +1 -0
@@ -0,0 +1,349 @@
1
+ import { UNIT_CONTEXT_LINES, UNIT_MAX_CHARS } from "../constants.js";
2
+ import { isTestFile } from "./diff.js";
3
+ import { truncate } from "./truncate.js";
4
+ const code = /\.(?:[cm]?[jt]sx?|py)$/;
5
+ function lines(text) {
6
+ return text === null ? [] : text.split("\n");
7
+ }
8
+ function overlaps(start, end, point, count) {
9
+ return count
10
+ ? point <= end && point + count - 1 >= start
11
+ : point > start && point <= end;
12
+ }
13
+ /** Conservative fallback: unknown regions remain hunks, never disappear. */
14
+ export function heuristicDeclarations(path, text) {
15
+ const source = lines(text);
16
+ const result = [];
17
+ const python = path.endsWith(".py");
18
+ for (let i = 0; i < source.length; i++) {
19
+ const line = source[i] ?? "";
20
+ const match = python
21
+ ? /^(\s*)(?:async\s+)?def\s+(\w+)/.exec(line)
22
+ : /^\s*(?:export\s+(?:default\s+)?)?(?:async\s+)?function\s+(\w+)/.exec(line);
23
+ const declaration = python
24
+ ? /^(\w+)\s*(?::[^=]+)?=/.exec(line)
25
+ : /^\s*(?:export\s+)?(?:const|let|var|type|interface|enum)\s+(\w+)/.exec(line);
26
+ if (!match && !declaration)
27
+ continue;
28
+ let start = i;
29
+ let end = i;
30
+ if (python && match) {
31
+ const indent = (match[1] ?? "").length;
32
+ while (start > 0 && (source[start - 1] ?? "").trim().startsWith("@"))
33
+ start--;
34
+ while (end + 1 < source.length &&
35
+ (!(source[end + 1] ?? "").trim() ||
36
+ (source[end + 1] ?? "").search(/\S/) > indent))
37
+ end++;
38
+ }
39
+ else if (!python) {
40
+ let depth = 0;
41
+ let opened = false;
42
+ for (let j = i; j < source.length; j++) {
43
+ for (const char of source[j] ?? "") {
44
+ if (char === "{") {
45
+ depth++;
46
+ opened = true;
47
+ }
48
+ else if (char === "}")
49
+ depth--;
50
+ }
51
+ end = j;
52
+ if ((opened && depth <= 0) ||
53
+ (!opened && /;\s*$/.test(source[j] ?? "")))
54
+ break;
55
+ if (!opened && j === i && !match)
56
+ break;
57
+ }
58
+ }
59
+ result.push({
60
+ kind: match ? "function" : "declaration",
61
+ name: match
62
+ ? (match[python ? 2 : 1] ?? "function")
63
+ : (declaration?.[1] ?? "declaration"),
64
+ exported: python
65
+ ? !line.trim().startsWith("def _")
66
+ : /\bexport\b/.test(line),
67
+ start: start + 1,
68
+ end: end + 1,
69
+ });
70
+ i = end;
71
+ }
72
+ return result;
73
+ }
74
+ export function buildUnits(files, parser) {
75
+ const units = [];
76
+ const limits = [];
77
+ for (const file of files) {
78
+ if (isTestFile(file.path))
79
+ continue;
80
+ if (file.binary) {
81
+ limits.push({ kind: "binary_ignored", file: file.path });
82
+ continue;
83
+ }
84
+ if (file.limitation) {
85
+ limits.push({ kind: file.limitation, file: file.path });
86
+ if (file.limitation === "too_large" ||
87
+ file.limitation === "not UTF-8 text") {
88
+ for (const hunk of file.hunks) {
89
+ const id = `u${String(units.length + 1).padStart(3, "0")}`;
90
+ const before = /^[A?]/.test(file.status)
91
+ ? null
92
+ : (hunk.beforeText ?? null);
93
+ const after = file.status === "D" ? null : (hunk.afterText ?? null);
94
+ const budget = before === null
95
+ ? 0
96
+ : after === null
97
+ ? UNIT_MAX_CHARS
98
+ : Math.floor(UNIT_MAX_CHARS / 2);
99
+ units.push({
100
+ id,
101
+ kind: "hunk",
102
+ file: file.path,
103
+ name: `${file.path}:${hunk.afterStart}`,
104
+ exported: false,
105
+ before: before === null ? null : truncate(before, budget),
106
+ after: after === null
107
+ ? null
108
+ : truncate(after, UNIT_MAX_CHARS - Math.min(before?.length ?? 0, budget)),
109
+ beforeRange: before === null
110
+ ? null
111
+ : {
112
+ start: hunk.beforeStart,
113
+ end: hunk.beforeStart + hunk.beforeCount - 1,
114
+ },
115
+ afterRange: after === null
116
+ ? null
117
+ : {
118
+ start: hunk.afterStart,
119
+ end: hunk.afterStart + hunk.afterCount - 1,
120
+ },
121
+ });
122
+ if (hunk.truncated ||
123
+ (before?.length ?? 0) + (after?.length ?? 0) > UNIT_MAX_CHARS)
124
+ limits.push({ kind: "truncated", file: file.path, unit: id });
125
+ }
126
+ }
127
+ continue;
128
+ }
129
+ const oldLines = lines(file.before);
130
+ const newLines = lines(file.after);
131
+ const changes = file.hunks.flatMap((hunk) => hunk.changes);
132
+ if (!changes.length &&
133
+ (file.before !== file.after || /^[RC]/.test(file.status)))
134
+ changes.push({
135
+ beforeStart: 1,
136
+ beforeCount: oldLines.length,
137
+ afterStart: 1,
138
+ afterCount: newLines.length,
139
+ });
140
+ if (!changes.length)
141
+ continue;
142
+ const add = (kind, name, exported, oldRange, newRange) => {
143
+ const excerpt = (source, range) => {
144
+ if (!range)
145
+ return null;
146
+ return source.slice(range.start - 1, range.end).join("\n");
147
+ };
148
+ let before = excerpt(oldLines, oldRange);
149
+ let after = excerpt(newLines, newRange);
150
+ const id = `u${String(units.length + 1).padStart(3, "0")}`;
151
+ const total = (before?.length ?? 0) + (after?.length ?? 0);
152
+ if (total > UNIT_MAX_CHARS) {
153
+ const beforeBudget = before === null
154
+ ? 0
155
+ : after === null
156
+ ? UNIT_MAX_CHARS
157
+ : Math.floor(UNIT_MAX_CHARS / 2);
158
+ if (before !== null)
159
+ before = truncate(before, beforeBudget);
160
+ if (after !== null)
161
+ after = truncate(after, UNIT_MAX_CHARS - (before?.length ?? 0));
162
+ limits.push({ kind: "truncated", file: file.path, unit: id });
163
+ }
164
+ units.push({
165
+ id,
166
+ kind,
167
+ file: file.path,
168
+ name,
169
+ exported,
170
+ before,
171
+ after,
172
+ beforeRange: oldRange,
173
+ afterRange: newRange,
174
+ });
175
+ };
176
+ if (!code.test(file.path) &&
177
+ (file.before?.length ?? 0) + (file.after?.length ?? 0) <= UNIT_MAX_CHARS) {
178
+ add("file", file.path, false, file.before === null ? null : { start: 1, end: oldLines.length }, file.after === null ? null : { start: 1, end: newLines.length });
179
+ continue;
180
+ }
181
+ const declarations = (text, path) => {
182
+ if (text === null || !code.test(path))
183
+ return [];
184
+ if (parser?.supports(path)) {
185
+ const result = parser.declarations(path, text);
186
+ for (const limit of result.limits) {
187
+ if (!limits.some((existing) => existing.kind === limit.kind &&
188
+ existing.file === path &&
189
+ (limit.kind !== "parse_partial" ||
190
+ (existing.kind === "parse_partial" &&
191
+ existing.declaration === limit.declaration))))
192
+ limits.push({ ...limit, file: path });
193
+ }
194
+ if (!result.limits.some((limit) => limit.kind === "parse_failed"))
195
+ return result.declarations;
196
+ }
197
+ else if (!limits.some((limit) => limit.kind === "grammar_absent" && limit.file === file.path))
198
+ limits.push({
199
+ kind: "grammar_absent",
200
+ file: file.path,
201
+ language: path.endsWith(".py") ? "Python" : "JavaScript/TypeScript",
202
+ });
203
+ return heuristicDeclarations(path, text);
204
+ };
205
+ const beforeDeclarations = declarations(file.before, file.oldPath);
206
+ const afterDeclarations = declarations(file.after, file.path);
207
+ const key = (d) => `${d.type ?? d.kind}:${d.name}`;
208
+ const pairedAfter = new Set();
209
+ const selected = [];
210
+ for (const before of beforeDeclarations) {
211
+ const after = afterDeclarations.find((d) => !pairedAfter.has(d) && key(d) === key(before));
212
+ if (after)
213
+ pairedAfter.add(after);
214
+ selected.push({ before, after });
215
+ }
216
+ for (const pair of selected) {
217
+ const before = pair.before;
218
+ if (pair.after ||
219
+ before?.kind !== "function" ||
220
+ before.body === undefined)
221
+ continue;
222
+ const after = afterDeclarations.find((d) => !pairedAfter.has(d) &&
223
+ d.kind === "function" &&
224
+ d.type === before.type &&
225
+ d.body === before.body);
226
+ if (after) {
227
+ pair.after = after;
228
+ pairedAfter.add(after);
229
+ }
230
+ }
231
+ for (const after of afterDeclarations)
232
+ if (!pairedAfter.has(after))
233
+ selected.push({ after });
234
+ const touched = (d, side) => d !== undefined &&
235
+ changes.some((c) => overlaps(d.start, d.end, c[`${side}Start`], c[`${side}Count`]));
236
+ const changed = selected.filter((pair) => touched(pair.before, "before") || touched(pair.after, "after"));
237
+ if (changed.some((p) => p.before?.kind === "function" && !p.after) &&
238
+ changed.some((p) => p.after?.kind === "function" && !p.before))
239
+ limits.push({ kind: "rename_unpaired", file: file.path });
240
+ for (const pair of changed) {
241
+ const declaration = pair.after ?? pair.before;
242
+ if (!declaration)
243
+ continue;
244
+ const name = declaration.name;
245
+ const size = (pair.before
246
+ ? oldLines.slice(pair.before.start - 1, pair.before.end).join("\n")
247
+ .length
248
+ : 0) +
249
+ (pair.after
250
+ ? newLines.slice(pair.after.start - 1, pair.after.end).join("\n")
251
+ .length
252
+ : 0);
253
+ const large = size > UNIT_MAX_CHARS;
254
+ const relevant = changes.filter((c) => (pair.before &&
255
+ overlaps(pair.before.start, pair.before.end, c.beforeStart, c.beforeCount)) ||
256
+ (pair.after &&
257
+ overlaps(pair.after.start, pair.after.end, c.afterStart, c.afterCount)));
258
+ const zones = [];
259
+ for (const change of relevant) {
260
+ const previous = zones[zones.length - 1];
261
+ const last = previous?.[previous.length - 1];
262
+ if (last &&
263
+ change.beforeStart <=
264
+ last.beforeStart + last.beforeCount + 2 * UNIT_CONTEXT_LINES &&
265
+ change.afterStart <=
266
+ last.afterStart + last.afterCount + 2 * UNIT_CONTEXT_LINES)
267
+ previous?.push(change);
268
+ else
269
+ zones.push([change]);
270
+ }
271
+ for (const zone of large ? zones : [relevant]) {
272
+ const range = (d, side) => {
273
+ if (!d)
274
+ return null;
275
+ if (!large)
276
+ return { start: d.start, end: d.end };
277
+ return {
278
+ start: Math.max(d.start, Math.min(...zone.map((c) => c[`${side}Start`])) -
279
+ UNIT_CONTEXT_LINES),
280
+ end: Math.min(d.end, Math.max(...zone.map((c) => c[`${side}Start`] + Math.max(1, c[`${side}Count`]) - 1)) + UNIT_CONTEXT_LINES),
281
+ };
282
+ };
283
+ add(large ? "slice" : declaration.kind, name, declaration.exported, range(pair.before, "before"), range(pair.after, "after"));
284
+ if (!large)
285
+ continue;
286
+ const unit = units[units.length - 1];
287
+ if (!unit)
288
+ continue;
289
+ const omission = file.path.endsWith(".py")
290
+ ? "# … omitted …"
291
+ : "// … omitted …";
292
+ for (const side of ["before", "after"]) {
293
+ const d = pair[side];
294
+ const r = unit[`${side}Range`];
295
+ const source = side === "before" ? oldLines : newLines;
296
+ if (!d || !r || unit[side] === null)
297
+ continue;
298
+ const signatureEnd = d.signatureEnd ?? d.start;
299
+ if (r.start > d.start) {
300
+ const signature = source
301
+ .slice(d.start - 1, Math.min(signatureEnd, r.start - 1))
302
+ .join("\n");
303
+ unit[side] =
304
+ `${signature}\n${r.start > signatureEnd + 1 ? `${omission}\n` : ""}${unit[side]}`;
305
+ }
306
+ if (r.end < d.end)
307
+ unit[side] += `\n${omission}`;
308
+ }
309
+ if ((unit.before?.length ?? 0) + (unit.after?.length ?? 0) >
310
+ UNIT_MAX_CHARS) {
311
+ const budget = unit.after === null
312
+ ? UNIT_MAX_CHARS
313
+ : unit.before === null
314
+ ? 0
315
+ : Math.floor(UNIT_MAX_CHARS / 2);
316
+ if (unit.before !== null)
317
+ unit.before = truncate(unit.before, budget);
318
+ if (unit.after !== null)
319
+ unit.after = truncate(unit.after, UNIT_MAX_CHARS - (unit.before?.length ?? 0));
320
+ if (!limits.some((l) => l.kind === "truncated" && l.unit === unit.id))
321
+ limits.push({ kind: "truncated", file: file.path, unit: unit.id });
322
+ }
323
+ }
324
+ }
325
+ for (const change of changes) {
326
+ const contains = (declarations, source, start, count) => count === 0 ||
327
+ source
328
+ .slice(Math.max(0, start - 1), start + count - 1)
329
+ .every((line, offset) => !line.trim() ||
330
+ declarations.some((d) => d.start <= start + offset && d.end >= start + offset));
331
+ const covered = (file.before === null ||
332
+ contains(beforeDeclarations, oldLines, change.beforeStart, change.beforeCount)) &&
333
+ (file.after === null ||
334
+ contains(afterDeclarations, newLines, change.afterStart, change.afterCount));
335
+ if (covered)
336
+ continue;
337
+ const range = (start, count, length) => ({
338
+ start: Math.max(1, start - UNIT_CONTEXT_LINES),
339
+ end: Math.min(length, start + Math.max(1, count) - 1 + UNIT_CONTEXT_LINES),
340
+ });
341
+ add("hunk", `${file.path}:${change.afterStart}`, false, file.before === null
342
+ ? null
343
+ : range(change.beforeStart, change.beforeCount, oldLines.length), file.after === null
344
+ ? null
345
+ : range(change.afterStart, change.afterCount, newLines.length));
346
+ }
347
+ }
348
+ return { units, limits };
349
+ }
@@ -0,0 +1,23 @@
1
+ import * as constants from "./constants.js";
2
+ import { ASK_DESCRIPTION } from "./texts/ask.js";
3
+ export function interpolate(template, names) {
4
+ const values = {
5
+ ...constants,
6
+ BAND_BOOL_GRAY_A: constants.BAND_BOOL_GRAY_A.toFixed(2),
7
+ BAND_BOOL_YES_MIN: constants.BAND_BOOL_YES_MIN.toFixed(2),
8
+ BAND_CHOICE_VERDICT_MIN: constants.BAND_CHOICE_VERDICT_MIN.toFixed(2),
9
+ ...Object.fromEntries(Object.entries(names).map(([key, value]) => [`names.${key}`, value])),
10
+ };
11
+ const rendered = template.replace(/{{([^}]+)}}/g, (_match, key) => {
12
+ const value = values[key];
13
+ if (typeof value !== "string" && typeof value !== "number")
14
+ throw new Error(`Unknown description interpolation: ${key}`);
15
+ return String(value);
16
+ });
17
+ if (rendered.includes("{{"))
18
+ throw new Error("Unresolved description interpolation.");
19
+ return rendered;
20
+ }
21
+ export function describeAsk(names) {
22
+ return interpolate(ASK_DESCRIPTION, names);
23
+ }
package/dist/guide.js ADDED
@@ -0,0 +1,33 @@
1
+ import { interpolate } from "./describe.js";
2
+ import { GUIDE_TEMPLATE } from "./texts/guide.js";
3
+ export class Guide {
4
+ delivered = false;
5
+ host;
6
+ text;
7
+ constructor(host) {
8
+ this.host = host;
9
+ this.text = interpolate(GUIDE_TEMPLATE, host.names);
10
+ }
11
+ attach(api) {
12
+ api.on("before_agent_start", (event) => {
13
+ if (!this.host.isOmp) {
14
+ if (event.systemPromptOptions.selectedTools.some((name) => name.startsWith("jev_")))
15
+ event.systemPromptOptions.sections.jev_guide = this.text;
16
+ else
17
+ delete event.systemPromptOptions.sections.jev_guide;
18
+ }
19
+ });
20
+ api.on("session_compact", () => {
21
+ this.reset();
22
+ });
23
+ }
24
+ deliver(ctx) {
25
+ if (this.host.isOmp && !this.delivered && ctx.addAdditionalContext) {
26
+ ctx.addAdditionalContext(this.text);
27
+ this.delivered = true;
28
+ }
29
+ }
30
+ reset() {
31
+ this.delivered = false;
32
+ }
33
+ }
package/dist/host.js ADDED
@@ -0,0 +1,24 @@
1
+ export function detectHost(api) {
2
+ const isOmp = api.pi !== undefined;
3
+ return {
4
+ isOmp,
5
+ names: {
6
+ grep: "grep",
7
+ byName: isOmp ? "glob" : "find",
8
+ semantic: isOmp
9
+ ? "find or jev_find_files, whichever is available"
10
+ : "jev_find_files",
11
+ },
12
+ };
13
+ }
14
+ /** MCP clients name their native tools differently; describe them generically. */
15
+ export function mcpHost() {
16
+ return {
17
+ isOmp: false,
18
+ names: {
19
+ grep: "your text search tool",
20
+ byName: "your file-name search tool",
21
+ semantic: "jev_find_files",
22
+ },
23
+ };
24
+ }