@glbgpt/cli 0.1.0-test.5 → 0.1.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 (140) hide show
  1. package/README.md +79 -6
  2. package/dist/src/auth/authClient.d.ts +21 -2
  3. package/dist/src/auth/authClient.js +62 -2
  4. package/dist/src/auth/authClient.js.map +1 -1
  5. package/dist/src/auth/loginFlow.js +5 -2
  6. package/dist/src/auth/loginFlow.js.map +1 -1
  7. package/dist/src/auth/loopbackServer.d.ts +2 -1
  8. package/dist/src/auth/loopbackServer.js +6 -3
  9. package/dist/src/auth/loopbackServer.js.map +1 -1
  10. package/dist/src/commands/accountCommand.js +10 -4
  11. package/dist/src/commands/accountCommand.js.map +1 -1
  12. package/dist/src/commands/configCommand.d.ts +5 -0
  13. package/dist/src/commands/configCommand.js +15 -2
  14. package/dist/src/commands/configCommand.js.map +1 -1
  15. package/dist/src/commands/execCommand.d.ts +6 -0
  16. package/dist/src/commands/execCommand.js +61 -9
  17. package/dist/src/commands/execCommand.js.map +1 -1
  18. package/dist/src/commands/loginCommand.js +9 -6
  19. package/dist/src/commands/loginCommand.js.map +1 -1
  20. package/dist/src/commands/mediaCommand.js +3 -2
  21. package/dist/src/commands/mediaCommand.js.map +1 -1
  22. package/dist/src/commands/skillCommand.d.ts +30 -0
  23. package/dist/src/commands/skillCommand.js +160 -0
  24. package/dist/src/commands/skillCommand.js.map +1 -0
  25. package/dist/src/config/configFile.d.ts +12 -0
  26. package/dist/src/config/configFile.js +12 -0
  27. package/dist/src/config/configFile.js.map +1 -1
  28. package/dist/src/config/env.d.ts +18 -0
  29. package/dist/src/config/env.js +57 -7
  30. package/dist/src/config/env.js.map +1 -1
  31. package/dist/src/core/catalog.d.ts +8 -1
  32. package/dist/src/core/catalog.js +13 -2
  33. package/dist/src/core/catalog.js.map +1 -1
  34. package/dist/src/core/contextPrune.d.ts +13 -0
  35. package/dist/src/core/contextPrune.js +61 -0
  36. package/dist/src/core/contextPrune.js.map +1 -0
  37. package/dist/src/core/httpChatBackend.js +22 -5
  38. package/dist/src/core/httpChatBackend.js.map +1 -1
  39. package/dist/src/core/runChat.d.ts +9 -5
  40. package/dist/src/core/runChat.js +10 -7
  41. package/dist/src/core/runChat.js.map +1 -1
  42. package/dist/src/core/session.d.ts +11 -0
  43. package/dist/src/core/session.js +20 -1
  44. package/dist/src/core/session.js.map +1 -1
  45. package/dist/src/core/toolRegistry.d.ts +14 -3
  46. package/dist/src/core/toolRegistry.js +24 -15
  47. package/dist/src/core/toolRegistry.js.map +1 -1
  48. package/dist/src/core/turnLoop.d.ts +4 -0
  49. package/dist/src/core/turnLoop.js +54 -13
  50. package/dist/src/core/turnLoop.js.map +1 -1
  51. package/dist/src/core/types.d.ts +6 -0
  52. package/dist/src/core/updateCheck.d.ts +46 -0
  53. package/dist/src/core/updateCheck.js +139 -0
  54. package/dist/src/core/updateCheck.js.map +1 -0
  55. package/dist/src/exec/media.d.ts +32 -0
  56. package/dist/src/exec/media.js +70 -0
  57. package/dist/src/exec/media.js.map +1 -0
  58. package/dist/src/exec/output.d.ts +16 -0
  59. package/dist/src/exec/output.js +41 -0
  60. package/dist/src/exec/output.js.map +1 -1
  61. package/dist/src/fs/globTool.d.ts +8 -0
  62. package/dist/src/fs/globTool.js +117 -0
  63. package/dist/src/fs/globTool.js.map +1 -0
  64. package/dist/src/fs/grepTool.d.ts +4 -0
  65. package/dist/src/fs/grepTool.js +146 -0
  66. package/dist/src/fs/grepTool.js.map +1 -0
  67. package/dist/src/fs/index.d.ts +8 -0
  68. package/dist/src/fs/index.js +13 -0
  69. package/dist/src/fs/index.js.map +1 -0
  70. package/dist/src/fs/limits.d.ts +34 -0
  71. package/dist/src/fs/limits.js +74 -0
  72. package/dist/src/fs/limits.js.map +1 -0
  73. package/dist/src/fs/listDirTool.d.ts +2 -0
  74. package/dist/src/fs/listDirTool.js +68 -0
  75. package/dist/src/fs/listDirTool.js.map +1 -0
  76. package/dist/src/fs/readFileTool.d.ts +2 -0
  77. package/dist/src/fs/readFileTool.js +102 -0
  78. package/dist/src/fs/readFileTool.js.map +1 -0
  79. package/dist/src/fs/walk.d.ts +11 -0
  80. package/dist/src/fs/walk.js +38 -0
  81. package/dist/src/fs/walk.js.map +1 -0
  82. package/dist/src/fs/workspaceGuard.d.ts +27 -0
  83. package/dist/src/fs/workspaceGuard.js +96 -0
  84. package/dist/src/fs/workspaceGuard.js.map +1 -0
  85. package/dist/src/index.js +47 -3
  86. package/dist/src/index.js.map +1 -1
  87. package/dist/src/mcp/mediaTools.d.ts +63 -0
  88. package/dist/src/mcp/mediaTools.js +385 -0
  89. package/dist/src/mcp/mediaTools.js.map +1 -0
  90. package/dist/src/mcp/server.d.ts +6 -2
  91. package/dist/src/mcp/server.js +19 -9
  92. package/dist/src/mcp/server.js.map +1 -1
  93. package/dist/src/media/aiApiClient.d.ts +5 -0
  94. package/dist/src/media/aiApiClient.js +12 -1
  95. package/dist/src/media/aiApiClient.js.map +1 -1
  96. package/dist/src/media/budget.d.ts +42 -0
  97. package/dist/src/media/budget.js +95 -0
  98. package/dist/src/media/budget.js.map +1 -0
  99. package/dist/src/media/executor.d.ts +9 -0
  100. package/dist/src/media/executor.js +1 -1
  101. package/dist/src/media/executor.js.map +1 -1
  102. package/dist/src/media/generateImageTool.d.ts +8 -2
  103. package/dist/src/media/generateImageTool.js +51 -3
  104. package/dist/src/media/generateImageTool.js.map +1 -1
  105. package/dist/src/media/promptSanitizer.d.ts +11 -0
  106. package/dist/src/media/promptSanitizer.js +33 -0
  107. package/dist/src/media/promptSanitizer.js.map +1 -0
  108. package/dist/src/media/taskManager.d.ts +8 -1
  109. package/dist/src/media/taskManager.js +1 -1
  110. package/dist/src/media/taskManager.js.map +1 -1
  111. package/dist/src/tui/App.js +30 -21
  112. package/dist/src/tui/App.js.map +1 -1
  113. package/dist/src/tui/ParamEditor.d.ts +11 -2
  114. package/dist/src/tui/ParamEditor.js +17 -6
  115. package/dist/src/tui/ParamEditor.js.map +1 -1
  116. package/dist/src/tui/atPopup.d.ts +8 -0
  117. package/dist/src/tui/atPopup.js +1 -1
  118. package/dist/src/tui/atPopup.js.map +1 -1
  119. package/dist/src/tui/chatController.d.ts +58 -2
  120. package/dist/src/tui/chatController.js +231 -40
  121. package/dist/src/tui/chatController.js.map +1 -1
  122. package/dist/src/tui/models.d.ts +6 -0
  123. package/dist/src/tui/models.js +14 -6
  124. package/dist/src/tui/models.js.map +1 -1
  125. package/dist/src/tui/run.js +50 -16
  126. package/dist/src/tui/run.js.map +1 -1
  127. package/dist/src/util/brokenPipe.d.ts +10 -0
  128. package/dist/src/util/brokenPipe.js +19 -0
  129. package/dist/src/util/brokenPipe.js.map +1 -0
  130. package/package.json +3 -2
  131. package/skills/globalgpt/SKILL.md +94 -0
  132. package/skills/globalgpt/references/troubleshooting.md +34 -0
  133. package/skills/globalgpt-coding/SKILL.md +65 -0
  134. package/skills/globalgpt-coding/references/troubleshooting.md +34 -0
  135. package/skills/globalgpt-image/SKILL.md +109 -0
  136. package/skills/globalgpt-image/references/recipes.md +52 -0
  137. package/skills/globalgpt-image/references/troubleshooting.md +49 -0
  138. package/skills/globalgpt-video/SKILL.md +109 -0
  139. package/skills/globalgpt-video/references/recipes.md +50 -0
  140. package/skills/globalgpt-video/references/troubleshooting.md +49 -0
@@ -0,0 +1,139 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { configDir } from '../config/paths.js';
4
+ import { getConfigValueSync } from '../config/configFile.js';
5
+ import { VERSION } from '../version.js';
6
+ /**
7
+ * Advise-only update check (spec: 2026-07-20-update-check-design.md).
8
+ *
9
+ * Codex-shaped: a 20h-TTL file cache is all the startup path ever reads; when it
10
+ * is stale a background registry fetch refreshes it for the NEXT start, so the
11
+ * TUI is never blocked by the network and every failure is silent. One deliberate
12
+ * divergence: our channel lives on prerelease versions (0.1.0-test.N), so the
13
+ * comparison implements semver prerelease precedence instead of ignoring it.
14
+ * The registry is fetched over HTTPS directly — never by shelling npm — which
15
+ * sidesteps project-level .npmrc registry redirection entirely.
16
+ */
17
+ export const UPDATE_CHECK_TTL_MS = 20 * 60 * 60 * 1000;
18
+ const REGISTRY_URL = 'https://registry.npmjs.org/@glbgpt%2fcli';
19
+ const FETCH_TIMEOUT_MS = 5000;
20
+ /** The npm dist-tag this build tracks: prerelease test builds follow `test`. */
21
+ export function channelForVersion(version) {
22
+ return version.includes('-test.') ? 'test' : 'latest';
23
+ }
24
+ export function upgradeCommand(channel) {
25
+ return `npm i -g @glbgpt/cli@${channel}`;
26
+ }
27
+ function parse(version) {
28
+ // Build metadata is constrained to the semver charset (not `.*`): version strings
29
+ // from the registry get echoed into the terminal, and a lax tail would let a
30
+ // poisoned response smuggle ANSI control sequences into that output.
31
+ const m = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.exec(version);
32
+ if (!m)
33
+ return null;
34
+ return {
35
+ core: [Number(m[1]), Number(m[2]), Number(m[3])],
36
+ pre: m[4] ? m[4].split('.') : null,
37
+ };
38
+ }
39
+ /**
40
+ * Semver precedence: >0 when `a` is newer than `b`, 0 when equal or either side
41
+ * is malformed (malformed must never read as "newer" — a registry hiccup would
42
+ * otherwise nag every start). Numeric prerelease identifiers compare numerically
43
+ * (test.10 > test.9); a release outranks its own prereleases.
44
+ */
45
+ export function compareVersions(a, b) {
46
+ const pa = parse(a);
47
+ const pb = parse(b);
48
+ if (!pa || !pb)
49
+ return 0;
50
+ for (let i = 0; i < 3; i++) {
51
+ if (pa.core[i] !== pb.core[i])
52
+ return pa.core[i] - pb.core[i];
53
+ }
54
+ if (!pa.pre && !pb.pre)
55
+ return 0;
56
+ if (!pa.pre)
57
+ return 1; // release > prerelease
58
+ if (!pb.pre)
59
+ return -1;
60
+ const len = Math.max(pa.pre.length, pb.pre.length);
61
+ for (let i = 0; i < len; i++) {
62
+ const x = pa.pre[i];
63
+ const y = pb.pre[i];
64
+ if (x === undefined)
65
+ return -1; // shorter prerelease = lower precedence
66
+ if (y === undefined)
67
+ return 1;
68
+ const xn = /^\d+$/.test(x);
69
+ const yn = /^\d+$/.test(y);
70
+ if (xn && yn) {
71
+ if (Number(x) !== Number(y))
72
+ return Number(x) - Number(y);
73
+ }
74
+ else if (xn !== yn) {
75
+ return xn ? -1 : 1; // numeric identifiers rank below alphanumeric ones
76
+ }
77
+ else if (x !== y) {
78
+ return x < y ? -1 : 1;
79
+ }
80
+ }
81
+ return 0;
82
+ }
83
+ function isEnabled() {
84
+ const env = process.env.GLBGPT_UPDATE_CHECK;
85
+ if (env !== undefined)
86
+ return env !== 'false' && env !== '0';
87
+ return getConfigValueSync('update-check') !== 'false';
88
+ }
89
+ async function readCache(file) {
90
+ try {
91
+ const parsed = JSON.parse(await fs.readFile(file, 'utf8'));
92
+ if (typeof parsed.latestVersion === 'string' &&
93
+ typeof parsed.channel === 'string' &&
94
+ typeof parsed.lastCheckedAt === 'number') {
95
+ return parsed;
96
+ }
97
+ }
98
+ catch {
99
+ // missing or corrupt cache = no cache
100
+ }
101
+ return null;
102
+ }
103
+ async function refresh(cacheFile, channel, fetchImpl, now) {
104
+ const res = await fetchImpl(REGISTRY_URL, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
105
+ if (!res.ok)
106
+ return;
107
+ const json = (await res.json());
108
+ const latest = json['dist-tags']?.[channel];
109
+ if (typeof latest !== 'string' || !latest)
110
+ return;
111
+ const cache = { latestVersion: latest, channel, lastCheckedAt: now() };
112
+ await fs.mkdir(path.dirname(cacheFile), { recursive: true });
113
+ await fs.writeFile(cacheFile, JSON.stringify(cache) + '\n', 'utf8');
114
+ }
115
+ /**
116
+ * Cache-only answer for THIS start; spawns the background refresh when the cache
117
+ * is stale, missing, or from another channel. Returns a notice only when the
118
+ * cached latest genuinely outranks the running version.
119
+ */
120
+ export async function maybeCheckForUpdate(deps = {}) {
121
+ const current = deps.currentVersion ?? VERSION;
122
+ if (!(deps.enabled ?? isEnabled()) || current === '0.0.0')
123
+ return null;
124
+ const channel = channelForVersion(current);
125
+ const cacheFile = deps.cacheFile ?? path.join(configDir(), 'version-check.json');
126
+ const now = deps.now ?? Date.now;
127
+ const cache = await readCache(cacheFile);
128
+ const fresh = cache !== null && cache.channel === channel && now() - cache.lastCheckedAt < UPDATE_CHECK_TTL_MS;
129
+ if (!fresh) {
130
+ void refresh(cacheFile, channel, deps.fetchImpl ?? fetch, now).catch(() => {
131
+ // Failure silence is the contract: no log, no notice, retry next start.
132
+ });
133
+ }
134
+ if (cache !== null && cache.channel === channel && compareVersions(cache.latestVersion, current) > 0) {
135
+ return { latest: cache.latestVersion, command: upgradeCommand(channel) };
136
+ }
137
+ return null;
138
+ }
139
+ //# sourceMappingURL=updateCheck.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"updateCheck.js","sourceRoot":"","sources":["../../../src/core/updateCheck.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import { AiApiClient } from '../media/aiApiClient.js';
2
+ import { TaskManager } from '../media/taskManager.js';
3
+ import { TaskLedger } from '../media/taskLedger.js';
4
+ import { SessionBudget } from '../media/budget.js';
5
+ import type { ClientTool } from '../core/types.js';
6
+ export interface ExecMediaResult {
7
+ taskId: string;
8
+ status: 'done' | 'failed';
9
+ url?: string;
10
+ localPath?: string;
11
+ credits?: number;
12
+ reason?: string;
13
+ }
14
+ export interface ExecMedia {
15
+ tools: ClientTool[];
16
+ pendingCount(): number;
17
+ /** Wait for every in-flight task to reach a terminal state, then return all results. */
18
+ drain(): Promise<ExecMediaResult[]>;
19
+ }
20
+ /**
21
+ * Media generation for `glbgpt exec` (spec §3): the same in-chat generate tools the
22
+ * TUI uses, gated by the session budget instead of a confirmation dialog. The tools
23
+ * fire-and-forget their TaskManager registration; this wrapper tracks submitted
24
+ * task ids so runExec can wait for them before the process exits.
25
+ */
26
+ export declare function createExecMedia(opts: {
27
+ client: AiApiClient;
28
+ budget: SessionBudget;
29
+ outputDir: string;
30
+ taskManager?: TaskManager;
31
+ ledger?: TaskLedger;
32
+ }): ExecMedia;
@@ -0,0 +1,70 @@
1
+ import { TaskManager } from '../media/taskManager.js';
2
+ import { TaskLedger } from '../media/taskLedger.js';
3
+ import { budgetedExecutor } from '../media/budget.js';
4
+ import { createGenerateImageTool, createGenerateVideoTool } from '../media/generateImageTool.js';
5
+ /**
6
+ * Media generation for `glbgpt exec` (spec §3): the same in-chat generate tools the
7
+ * TUI uses, gated by the session budget instead of a confirmation dialog. The tools
8
+ * fire-and-forget their TaskManager registration; this wrapper tracks submitted
9
+ * task ids so runExec can wait for them before the process exits.
10
+ */
11
+ export function createExecMedia(opts) {
12
+ const taskManager = opts.taskManager ?? new TaskManager(opts.client, { outputDir: opts.outputDir });
13
+ const ledger = opts.ledger ?? new TaskLedger();
14
+ const executor = budgetedExecutor(opts.client, opts.budget);
15
+ const inflight = new Map();
16
+ const results = [];
17
+ let onIdle = null;
18
+ taskManager.subscribe((e) => {
19
+ if (e.type === 'task_done') {
20
+ results.push({
21
+ taskId: e.taskId,
22
+ status: 'done',
23
+ url: e.outputUrl,
24
+ localPath: e.localPath,
25
+ credits: inflight.get(e.taskId)?.credits,
26
+ });
27
+ }
28
+ else if (e.type === 'task_failed') {
29
+ results.push({ taskId: e.taskId, status: 'failed', reason: e.reason, credits: inflight.get(e.taskId)?.credits });
30
+ }
31
+ else {
32
+ return; // task_progress
33
+ }
34
+ inflight.delete(e.taskId);
35
+ if (inflight.size === 0 && onIdle) {
36
+ onIdle();
37
+ onIdle = null;
38
+ }
39
+ });
40
+ /** Record the task id a successful submit returns, so drain() knows what to wait for. */
41
+ const tracked = (tool) => ({
42
+ ...tool,
43
+ async execute(args) {
44
+ const r = await tool.execute(args);
45
+ const rec = r;
46
+ if (typeof rec.task_id === 'string') {
47
+ inflight.set(rec.task_id, {
48
+ credits: typeof rec.estimated_credits === 'number' ? rec.estimated_credits : undefined,
49
+ });
50
+ }
51
+ return r;
52
+ },
53
+ });
54
+ return {
55
+ tools: [
56
+ tracked(createGenerateImageTool({ executor, taskManager, ledger })),
57
+ tracked(createGenerateVideoTool({ executor, taskManager, ledger })),
58
+ ],
59
+ pendingCount: () => inflight.size,
60
+ async drain() {
61
+ if (inflight.size > 0) {
62
+ await new Promise((resolve) => {
63
+ onIdle = resolve;
64
+ });
65
+ }
66
+ return results;
67
+ },
68
+ };
69
+ }
70
+ //# sourceMappingURL=media.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media.js","sourceRoot":"","sources":["../../../src/exec/media.ts"],"names":[],"mappings":"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"}
@@ -33,6 +33,22 @@ export declare class ExecOutput {
33
33
  finishResult(structured: unknown | null): void;
34
34
  /** Turn failed. Keeps the active output structure and returns via a non-zero exit upstream. */
35
35
  finishError(message: string): void;
36
+ /** A tool call started. jsonl gets an event line; text/json a stderr one-liner. */
37
+ toolBegin(name: string, args: Record<string, unknown>): void;
38
+ /**
39
+ * A tool call finished. Full content is deliberately omitted (a read can approach 1MB;
40
+ * consumers get activity, the model gets content).
41
+ */
42
+ toolEnd(name: string, result: Record<string, unknown>): void;
43
+ /** A media generation reached a terminal state after the turn (exec waits for these before exit). */
44
+ mediaResult(r: {
45
+ taskId: string;
46
+ status: 'done' | 'failed';
47
+ url?: string;
48
+ localPath?: string;
49
+ credits?: number;
50
+ reason?: string;
51
+ }): void;
36
52
  /** Accumulated assistant text (used to write the --output-last-message file). */
37
53
  getContent(): string;
38
54
  private line;
@@ -73,6 +73,47 @@ export class ExecOutput {
73
73
  this.sinks.err(`${message}\n`);
74
74
  }
75
75
  }
76
+ /** A tool call started. jsonl gets an event line; text/json a stderr one-liner. */
77
+ toolBegin(name, args) {
78
+ if (this.mode === 'jsonl') {
79
+ this.line({ type: 'tool_call', name, args });
80
+ }
81
+ else {
82
+ const arg = typeof args.path === 'string' ? args.path : typeof args.pattern === 'string' ? args.pattern : '';
83
+ this.sinks.err(`⚒ ${name}${arg ? `(${arg})` : ''}\n`);
84
+ }
85
+ }
86
+ /**
87
+ * A tool call finished. Full content is deliberately omitted (a read can approach 1MB;
88
+ * consumers get activity, the model gets content).
89
+ */
90
+ toolEnd(name, result) {
91
+ const summary = typeof result.summary === 'string' ? result.summary : undefined;
92
+ const error = typeof result.error === 'string' ? result.error : undefined;
93
+ if (this.mode === 'jsonl') {
94
+ this.line({ type: 'tool_result', name, ...(summary ? { summary } : {}), ...(error ? { error } : {}) });
95
+ }
96
+ else {
97
+ this.sinks.err(` ⎿ ${error ?? summary ?? 'done'}\n`);
98
+ }
99
+ }
100
+ /** A media generation reached a terminal state after the turn (exec waits for these before exit). */
101
+ mediaResult(r) {
102
+ if (this.mode === 'jsonl') {
103
+ this.line({
104
+ type: 'media_result',
105
+ task_id: r.taskId,
106
+ status: r.status,
107
+ ...(r.url ? { url: r.url } : {}),
108
+ ...(r.localPath ? { local_path: r.localPath } : {}),
109
+ ...(r.credits !== undefined ? { credits: r.credits } : {}),
110
+ ...(r.reason ? { reason: r.reason } : {}),
111
+ });
112
+ }
113
+ else {
114
+ this.sinks.err(r.status === 'done' ? `⚒ media saved: ${r.localPath}\n` : `⚒ media failed: ${r.reason}\n`);
115
+ }
116
+ }
76
117
  /** Accumulated assistant text (used to write the --output-last-message file). */
77
118
  getContent() {
78
119
  return this.content;
@@ -1 +1 @@
1
- {"version":3,"file":"output.js","sourceRoot":"","sources":["../../../src/exec/output.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../../../src/exec/output.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { ClientTool } from '../core/types.js';
2
+ /**
3
+ * Translate a glob pattern to a RegExp over "/"-separated relative paths.
4
+ * Supported: `**` (any depth, including zero for a leading `**` + separator), `*` (within one
5
+ * segment), `?` (one char). Everything else is matched literally.
6
+ */
7
+ export declare function globToRegExp(pattern: string): RegExp;
8
+ export declare function createGlobTool(root: string): ClientTool;
@@ -0,0 +1,117 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { GuardError, resolveInWorkspace, sanitizeFsError } from './workspaceGuard.js';
4
+ import { walkFiles } from './walk.js';
5
+ import { MAX_GLOB_RESULTS, MAX_GLOB_SCAN } from './limits.js';
6
+ /**
7
+ * Translate a glob pattern to a RegExp over "/"-separated relative paths.
8
+ * Supported: `**` (any depth, including zero for a leading `**` + separator), `*` (within one
9
+ * segment), `?` (one char). Everything else is matched literally.
10
+ */
11
+ export function globToRegExp(pattern) {
12
+ let re = '';
13
+ let i = 0;
14
+ while (i < pattern.length) {
15
+ const c = pattern[i];
16
+ if (c === '*') {
17
+ if (pattern[i + 1] === '*') {
18
+ // `**/` at a boundary may match zero directories.
19
+ if (pattern[i + 2] === '/') {
20
+ re += '(?:.*/)?';
21
+ i += 3;
22
+ }
23
+ else {
24
+ re += '.*';
25
+ i += 2;
26
+ }
27
+ }
28
+ else {
29
+ re += '[^/]*';
30
+ i += 1;
31
+ }
32
+ }
33
+ else if (c === '?') {
34
+ re += '[^/]';
35
+ i += 1;
36
+ }
37
+ else {
38
+ re += c.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
39
+ i += 1;
40
+ }
41
+ }
42
+ return new RegExp(`^${re}$`);
43
+ }
44
+ export function createGlobTool(root) {
45
+ return {
46
+ name: 'glob',
47
+ readOnly: true,
48
+ description: 'Find files inside the workspace by glob pattern (e.g. "**/*.pdf", "docs/*.md"). ' +
49
+ `Returns matching paths, newest modified first (top ${MAX_GLOB_RESULTS}; larger result sets are ` +
50
+ 'truncated with a note). node_modules/.git etc. are never searched.',
51
+ parameters: {
52
+ type: 'object',
53
+ properties: {
54
+ pattern: { type: 'string', description: 'Glob pattern over workspace-relative paths.' },
55
+ path: { type: 'string', description: 'Directory to search from (default workspace root).' },
56
+ },
57
+ required: ['pattern'],
58
+ },
59
+ async execute(args) {
60
+ if (typeof args.pattern !== 'string' || !args.pattern.trim()) {
61
+ return { error: 'Missing required "pattern" argument.' };
62
+ }
63
+ const startArg = typeof args.path === 'string' && args.path.trim() ? args.path : '.';
64
+ try {
65
+ const start = await resolveInWorkspace(root, startArg);
66
+ const rx = globToRegExp(args.pattern);
67
+ const matches = [];
68
+ for await (const f of walkFiles(root, start)) {
69
+ // Match against "/"-separated rel paths for cross-platform stability. Try BOTH the
70
+ // root-relative path (so `pattern: 'docs/*.md'` works from the root) and the path
71
+ // relative to the resolved start dir (so `pattern: '*.md', path: 'docs'` also works,
72
+ // matching the over-cap error's own advice to narrow via `path`). Results always stay
73
+ // root-relative so they remain valid read_file inputs either way.
74
+ const relFromRoot = f.rel.split(path.sep).join('/');
75
+ const relFromStart = path.relative(start, f.abs).split(path.sep).join('/');
76
+ if (!rx.test(relFromRoot) && !rx.test(relFromStart))
77
+ continue;
78
+ const stat = await fs.stat(f.abs);
79
+ matches.push({ rel: f.rel, mtime: stat.mtimeMs });
80
+ // Hard scan bound so a pathological workspace can't stat forever; the note
81
+ // below reports the cap so the model knows the count is a floor, not a total.
82
+ if (matches.length >= MAX_GLOB_SCAN)
83
+ break;
84
+ }
85
+ if (matches.length === 0)
86
+ return { error: `No files match: ${args.pattern}` };
87
+ matches.sort((a, b) => b.mtime - a.mtime);
88
+ // Discovery results are cheap (a path list) — over-cap truncates with a note
89
+ // instead of erroring, saving the model a retry round-trip.
90
+ if (matches.length > MAX_GLOB_RESULTS) {
91
+ // When the scan cap stopped collection we cannot know whether more matches exist
92
+ // (an exact-2000 workspace would make "2000+" a false claim) — say what we know.
93
+ const scanCapped = matches.length >= MAX_GLOB_SCAN;
94
+ const shown = matches.slice(0, MAX_GLOB_RESULTS);
95
+ const note = scanCapped
96
+ ? `(Results truncated: showing the ${MAX_GLOB_RESULTS} newest of the first ${MAX_GLOB_SCAN} matches scanned. Narrow the pattern or search a subdirectory.)`
97
+ : `(Results truncated: showing the ${MAX_GLOB_RESULTS} newest of ${matches.length} matches. Narrow the pattern or search a subdirectory.)`;
98
+ return {
99
+ summary: `${MAX_GLOB_RESULTS} files (truncated)`,
100
+ content: [...shown.map((m) => m.rel), note].join('\n'),
101
+ };
102
+ }
103
+ return {
104
+ summary: `${matches.length} files`,
105
+ content: matches.map((m) => m.rel).join('\n'),
106
+ };
107
+ }
108
+ catch (err) {
109
+ if (err instanceof GuardError)
110
+ return { error: err.message };
111
+ const message = err instanceof Error ? err.message : String(err);
112
+ return { error: `glob failed: ${sanitizeFsError(message, root)}` };
113
+ }
114
+ },
115
+ };
116
+ }
117
+ //# sourceMappingURL=globTool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"globTool.js","sourceRoot":"","sources":["../../../src/fs/globTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtF,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE9D;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;QAC1B,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACrB,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YACd,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBAC3B,kDAAkD;gBAClD,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;oBAC3B,EAAE,IAAI,UAAU,CAAC;oBACjB,CAAC,IAAI,CAAC,CAAC;gBACT,CAAC;qBAAM,CAAC;oBACN,EAAE,IAAI,IAAI,CAAC;oBACX,CAAC,IAAI,CAAC,CAAC;gBACT,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,EAAE,IAAI,OAAO,CAAC;gBACd,CAAC,IAAI,CAAC,CAAC;YACT,CAAC;QACH,CAAC;aAAM,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YACrB,EAAE,IAAI,MAAM,CAAC;YACb,CAAC,IAAI,CAAC,CAAC;QACT,CAAC;aAAM,CAAC;YACN,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;YAC/C,CAAC,IAAI,CAAC,CAAC;QACT,CAAC;IACH,CAAC;IACD,OAAO,IAAI,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,IAAI;QACd,WAAW,EACT,kFAAkF;YAClF,sDAAsD,gBAAgB,2BAA2B;YACjG,oEAAoE;QACtE,UAAU,EAAE;YACV,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,6CAA6C,EAAE;gBACvF,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,oDAAoD,EAAE;aAC5F;YACD,QAAQ,EAAE,CAAC,SAAS,CAAC;SACtB;QACD,KAAK,CAAC,OAAO,CAAC,IAAI;YAChB,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;gBAC7D,OAAO,EAAE,KAAK,EAAE,sCAAsC,EAAE,CAAC;YAC3D,CAAC;YACD,MAAM,QAAQ,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;YACrF,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;gBACvD,MAAM,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBACtC,MAAM,OAAO,GAA0C,EAAE,CAAC;gBAC1D,IAAI,KAAK,EAAE,MAAM,CAAC,IAAI,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;oBAC7C,mFAAmF;oBACnF,kFAAkF;oBAClF,qFAAqF;oBACrF,sFAAsF;oBACtF,kEAAkE;oBAClE,MAAM,WAAW,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;oBACpD,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;oBAC3E,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC;wBAAE,SAAS;oBAC9D,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;oBAClC,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;oBAClD,2EAA2E;oBAC3E,8EAA8E;oBAC9E,IAAI,OAAO,CAAC,MAAM,IAAI,aAAa;wBAAE,MAAM;gBAC7C,CAAC;gBACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;oBAAE,OAAO,EAAE,KAAK,EAAE,mBAAmB,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;gBAC9E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;gBAC1C,6EAA6E;gBAC7E,4DAA4D;gBAC5D,IAAI,OAAO,CAAC,MAAM,GAAG,gBAAgB,EAAE,CAAC;oBACtC,iFAAiF;oBACjF,iFAAiF;oBACjF,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,IAAI,aAAa,CAAC;oBACnD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;oBACjD,MAAM,IAAI,GAAG,UAAU;wBACrB,CAAC,CAAC,mCAAmC,gBAAgB,wBAAwB,aAAa,iEAAiE;wBAC3J,CAAC,CAAC,mCAAmC,gBAAgB,cAAc,OAAO,CAAC,MAAM,yDAAyD,CAAC;oBAC7I,OAAO;wBACL,OAAO,EAAE,GAAG,gBAAgB,oBAAoB;wBAChD,OAAO,EAAE,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;qBACvD,CAAC;gBACJ,CAAC;gBACD,OAAO;oBACL,OAAO,EAAE,GAAG,OAAO,CAAC,MAAM,QAAQ;oBAClC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;iBAC9C,CAAC;YACJ,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,GAAG,YAAY,UAAU;oBAAE,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC;gBAC7D,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACjE,OAAO,EAAE,KAAK,EAAE,gBAAgB,eAAe,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;YACrE,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { ClientTool } from '../core/types.js';
2
+ export declare function createGrepTool(root: string, opts?: {
3
+ timeoutMs?: number;
4
+ }): ClientTool;
@@ -0,0 +1,146 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+ import vm from 'node:vm';
4
+ import { GuardError, resolveInWorkspace, sanitizeFsError } from './workspaceGuard.js';
5
+ import { walkFiles } from './walk.js';
6
+ import { globToRegExp } from './globTool.js';
7
+ import { GREP_TIMEOUT_MS, MAX_FILE_SIZE_BYTES, MAX_GREP_LINE_CHARS, MAX_GREP_MATCHES, } from './limits.js';
8
+ function looksBinary(buf) {
9
+ return buf.subarray(0, 8192).includes(0);
10
+ }
11
+ /**
12
+ * Per-file matcher, run inside a vm context so the call deadline can interrupt
13
+ * catastrophic regex backtracking (vm timeout = V8 TerminateExecution, which
14
+ * does stop a spinning RegExp — see GREP_TIMEOUT_MS in limits.ts). The script
15
+ * is fixed; the model supplies only the regex SOURCE string, which the RegExp
16
+ * constructor treats as data, never as code.
17
+ */
18
+ const MATCH_SCRIPT = new vm.Script(`{
19
+ const re = new RegExp(source);
20
+ const hits = [];
21
+ for (let i = 0; i < lines.length; i++) if (re.test(lines[i])) hits.push(i);
22
+ hits;
23
+ }`);
24
+ function fmtTimeout(ms) {
25
+ return ms >= 1000 ? `${Math.round(ms / 1000)}s` : `${ms}ms`;
26
+ }
27
+ export function createGrepTool(root, opts = {}) {
28
+ const timeoutMs = opts.timeoutMs ?? GREP_TIMEOUT_MS;
29
+ return {
30
+ name: 'grep',
31
+ readOnly: true,
32
+ description: 'Search file contents inside the workspace with a regular expression (JavaScript syntax). ' +
33
+ `Returns "file:line: text" matches (first ${MAX_GREP_MATCHES}; larger result sets are truncated ` +
34
+ 'with a note). Binary files and files over 1MB are skipped. ' +
35
+ 'Use include (e.g. "*.md") to restrict which files are searched. ' +
36
+ 'Searches are time-boxed; on timeout you get any partial results plus a note (timeout does NOT mean no matches).',
37
+ parameters: {
38
+ type: 'object',
39
+ properties: {
40
+ pattern: { type: 'string', description: 'Regular expression to search for.' },
41
+ path: { type: 'string', description: 'Directory to search from (default workspace root).' },
42
+ include: { type: 'string', description: 'Glob filter on file names, e.g. "*.md".' },
43
+ },
44
+ required: ['pattern'],
45
+ },
46
+ async execute(args) {
47
+ if (typeof args.pattern !== 'string' || !args.pattern.trim()) {
48
+ return { error: 'Missing required "pattern" argument.' };
49
+ }
50
+ try {
51
+ new RegExp(args.pattern); // validity check only — matching happens in the vm (see MATCH_SCRIPT)
52
+ }
53
+ catch (e) {
54
+ return { error: `Invalid regex: ${e instanceof Error ? e.message : String(e)}` };
55
+ }
56
+ const includeRx = typeof args.include === 'string' && args.include.trim()
57
+ ? globToRegExp(args.include)
58
+ : null;
59
+ const startArg = typeof args.path === 'string' && args.path.trim() ? args.path : '.';
60
+ try {
61
+ const start = await resolveInWorkspace(root, startArg);
62
+ const out = [];
63
+ let truncated = false;
64
+ let timedOut = false;
65
+ const deadline = Date.now() + timeoutMs;
66
+ const ctx = vm.createContext(Object.create(null));
67
+ outer: for await (const f of walkFiles(root, start)) {
68
+ if (includeRx && !includeRx.test(path.basename(f.rel)))
69
+ continue;
70
+ const stat = await fs.stat(f.abs);
71
+ if (stat.size > MAX_FILE_SIZE_BYTES)
72
+ continue;
73
+ const buf = await fs.readFile(f.abs);
74
+ if (looksBinary(buf))
75
+ continue;
76
+ const lines = buf.toString('utf8').split('\n');
77
+ const remaining = deadline - Date.now();
78
+ if (remaining <= 0) {
79
+ timedOut = true;
80
+ break;
81
+ }
82
+ let hits;
83
+ try {
84
+ ctx.source = args.pattern;
85
+ ctx.lines = lines;
86
+ hits = MATCH_SCRIPT.runInContext(ctx, { timeout: Math.max(1, remaining) });
87
+ }
88
+ catch (err) {
89
+ if (err.code === 'ERR_SCRIPT_EXECUTION_TIMEOUT') {
90
+ timedOut = true;
91
+ break;
92
+ }
93
+ throw err;
94
+ }
95
+ for (const i of hits) {
96
+ const text = lines[i].length > MAX_GREP_LINE_CHARS
97
+ ? `${lines[i].slice(0, MAX_GREP_LINE_CHARS)}…`
98
+ : lines[i];
99
+ out.push(`${f.rel}:${i + 1}: ${text.trim()}`);
100
+ // Discovery results are cheap — stop at the cap and report truncation
101
+ // instead of erroring, saving the model a retry round-trip.
102
+ if (out.length >= MAX_GREP_MATCHES) {
103
+ truncated = true;
104
+ break outer;
105
+ }
106
+ }
107
+ }
108
+ if (timedOut) {
109
+ // Claude Code's RipgrepTimeoutError lesson: a timeout must be
110
+ // distinguishable from "no matches", or the model draws wrong conclusions.
111
+ const t = fmtTimeout(timeoutMs);
112
+ if (out.length === 0) {
113
+ return {
114
+ error: `Search timed out after ${t} before completing (the pattern may backtrack ` +
115
+ `catastrophically or the tree is large). This is NOT "no matches" — simplify ` +
116
+ 'the pattern or narrow the path/include filter and retry.',
117
+ };
118
+ }
119
+ const note = `(Search timed out after ${t} before completing — results above are PARTIAL. ` +
120
+ 'Simplify the pattern or narrow the path/include filter to search the rest.)';
121
+ return {
122
+ summary: `${out.length} match${out.length === 1 ? '' : 'es'} (timed out, partial)`,
123
+ content: [...out, note].join('\n'),
124
+ };
125
+ }
126
+ if (out.length === 0)
127
+ return { error: `No matches for: ${args.pattern}` };
128
+ if (truncated) {
129
+ const note = `(Results truncated at ${MAX_GREP_MATCHES} matches. Narrow the pattern, path, or include filter.)`;
130
+ return {
131
+ summary: `${MAX_GREP_MATCHES} matches (truncated)`,
132
+ content: [...out, note].join('\n'),
133
+ };
134
+ }
135
+ return { summary: `${out.length} match${out.length === 1 ? '' : 'es'}`, content: out.join('\n') };
136
+ }
137
+ catch (err) {
138
+ if (err instanceof GuardError)
139
+ return { error: err.message };
140
+ const message = err instanceof Error ? err.message : String(err);
141
+ return { error: `grep failed: ${sanitizeFsError(message, root)}` };
142
+ }
143
+ },
144
+ };
145
+ }
146
+ //# sourceMappingURL=grepTool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grepTool.js","sourceRoot":"","sources":["../../../src/fs/grepTool.ts"],"names":[],"mappings":"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"}