beth-copilot 1.0.18 → 1.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.
- package/CHANGELOG.md +41 -28
- package/README.md +87 -247
- package/bin/cli.js +158 -358
- package/dist/__tests__/smoke.test.d.ts +8 -0
- package/dist/__tests__/smoke.test.d.ts.map +1 -0
- package/dist/__tests__/smoke.test.js +49 -0
- package/dist/__tests__/smoke.test.js.map +1 -0
- package/dist/cli/commands/beads.e2e.test.d.ts +13 -0
- package/dist/cli/commands/beads.e2e.test.d.ts.map +1 -0
- package/dist/cli/commands/beads.e2e.test.js +526 -0
- package/dist/cli/commands/beads.e2e.test.js.map +1 -0
- package/dist/cli/commands/cli-edge-cases.e2e.test.d.ts +32 -0
- package/dist/cli/commands/cli-edge-cases.e2e.test.d.ts.map +1 -0
- package/dist/cli/commands/cli-edge-cases.e2e.test.js +162 -0
- package/dist/cli/commands/cli-edge-cases.e2e.test.js.map +1 -0
- package/dist/cli/commands/close.d.ts +89 -0
- package/dist/cli/commands/close.d.ts.map +1 -0
- package/dist/cli/commands/close.e2e.test.d.ts +27 -0
- package/dist/cli/commands/close.e2e.test.d.ts.map +1 -0
- package/dist/cli/commands/close.e2e.test.js +252 -0
- package/dist/cli/commands/close.e2e.test.js.map +1 -0
- package/dist/cli/commands/close.js +309 -0
- package/dist/cli/commands/close.js.map +1 -0
- package/dist/cli/commands/close.test.d.ts +15 -0
- package/dist/cli/commands/close.test.d.ts.map +1 -0
- package/dist/cli/commands/close.test.js +634 -0
- package/dist/cli/commands/close.test.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +23 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/doctor.js +93 -0
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/doctor.test.js +209 -0
- package/dist/cli/commands/doctor.test.js.map +1 -1
- package/dist/cli/commands/framework-isolation.test.d.ts +30 -0
- package/dist/cli/commands/framework-isolation.test.d.ts.map +1 -0
- package/dist/cli/commands/framework-isolation.test.js +119 -0
- package/dist/cli/commands/framework-isolation.test.js.map +1 -0
- package/dist/cli/commands/help.e2e.test.js +4 -4
- package/dist/cli/commands/help.e2e.test.js.map +1 -1
- package/dist/cli/commands/init-logic.e2e.test.d.ts +37 -0
- package/dist/cli/commands/init-logic.e2e.test.d.ts.map +1 -0
- package/dist/cli/commands/init-logic.e2e.test.js +305 -0
- package/dist/cli/commands/init-logic.e2e.test.js.map +1 -0
- package/dist/cli/commands/land.d.ts +142 -0
- package/dist/cli/commands/land.d.ts.map +1 -0
- package/dist/cli/commands/land.js +647 -0
- package/dist/cli/commands/land.js.map +1 -0
- package/dist/cli/commands/land.test.d.ts +20 -0
- package/dist/cli/commands/land.test.d.ts.map +1 -0
- package/dist/cli/commands/land.test.js +622 -0
- package/dist/cli/commands/land.test.js.map +1 -0
- package/dist/cli/commands/mcp.e2e.test.js +22 -29
- package/dist/cli/commands/mcp.e2e.test.js.map +1 -1
- package/dist/cli/commands/pipeline.e2e.test.js +20 -20
- package/dist/cli/commands/pipeline.e2e.test.js.map +1 -1
- package/dist/cli/commands/pre-push-guard.d.ts +84 -0
- package/dist/cli/commands/pre-push-guard.d.ts.map +1 -0
- package/dist/cli/commands/pre-push-guard.e2e.test.d.ts +24 -0
- package/dist/cli/commands/pre-push-guard.e2e.test.d.ts.map +1 -0
- package/dist/cli/commands/pre-push-guard.e2e.test.js +171 -0
- package/dist/cli/commands/pre-push-guard.e2e.test.js.map +1 -0
- package/dist/cli/commands/pre-push-guard.js +257 -0
- package/dist/cli/commands/pre-push-guard.js.map +1 -0
- package/dist/cli/commands/pre-push-guard.test.d.ts +15 -0
- package/dist/cli/commands/pre-push-guard.test.d.ts.map +1 -0
- package/dist/cli/commands/pre-push-guard.test.js +397 -0
- package/dist/cli/commands/pre-push-guard.test.js.map +1 -0
- package/dist/cli/commands/quickstart-expanded.e2e.test.d.ts +23 -0
- package/dist/cli/commands/quickstart-expanded.e2e.test.d.ts.map +1 -0
- package/dist/cli/commands/quickstart-expanded.e2e.test.js +179 -0
- package/dist/cli/commands/quickstart-expanded.e2e.test.js.map +1 -0
- package/dist/cli/commands/quickstart.d.ts.map +1 -1
- package/dist/cli/commands/quickstart.js +7 -23
- package/dist/cli/commands/quickstart.js.map +1 -1
- package/dist/cli/commands/quickstart.test.js +40 -67
- package/dist/cli/commands/quickstart.test.js.map +1 -1
- package/dist/core/agents/suite.test.js +4 -2
- package/dist/core/agents/suite.test.js.map +1 -1
- package/dist/core/agents/tools.test.js +5 -1
- package/dist/core/agents/tools.test.js.map +1 -1
- package/dist/index.d.ts +3 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -10
- package/dist/index.js.map +1 -1
- package/package.json +15 -9
- package/sbom.json +2011 -819
- package/templates/.github/agents/beth.agent.md +220 -66
- package/templates/.github/agents/developer.agent.md +53 -90
- package/templates/.github/agents/product-manager.agent.md +15 -68
- package/templates/.github/agents/researcher.agent.md +20 -71
- package/templates/.github/agents/security-reviewer.agent.md +29 -81
- package/templates/.github/agents/tester.agent.md +40 -69
- package/templates/.github/agents/ux-designer.agent.md +20 -74
- package/templates/.github/copilot-instructions.md +217 -225
- package/templates/AGENTS.md +108 -20
- package/templates/mcp.json.example +0 -3
- package/dist/cli/commands/client-config.d.ts +0 -31
- package/dist/cli/commands/client-config.d.ts.map +0 -1
- package/dist/cli/commands/client-config.e2e.test.d.ts +0 -15
- package/dist/cli/commands/client-config.e2e.test.d.ts.map +0 -1
- package/dist/cli/commands/client-config.e2e.test.js +0 -556
- package/dist/cli/commands/client-config.e2e.test.js.map +0 -1
- package/dist/cli/commands/client-config.js +0 -73
- package/dist/cli/commands/client-config.js.map +0 -1
- package/dist/cli/commands/client-config.test.d.ts +0 -6
- package/dist/cli/commands/client-config.test.d.ts.map +0 -1
- package/dist/cli/commands/client-config.test.js +0 -133
- package/dist/cli/commands/client-config.test.js.map +0 -1
- package/dist/cli/commands/init-quickstart.e2e.test.d.ts +0 -11
- package/dist/cli/commands/init-quickstart.e2e.test.d.ts.map +0 -1
- package/dist/cli/commands/init-quickstart.e2e.test.js +0 -221
- package/dist/cli/commands/init-quickstart.e2e.test.js.map +0 -1
- package/dist/core/context.d.ts +0 -171
- package/dist/core/context.d.ts.map +0 -1
- package/dist/core/context.js +0 -353
- package/dist/core/context.js.map +0 -1
- package/dist/core/context.test.d.ts +0 -8
- package/dist/core/context.test.d.ts.map +0 -1
- package/dist/core/context.test.js +0 -253
- package/dist/core/context.test.js.map +0 -1
- package/dist/core/handoffs.d.ts +0 -151
- package/dist/core/handoffs.d.ts.map +0 -1
- package/dist/core/handoffs.js +0 -220
- package/dist/core/handoffs.js.map +0 -1
- package/dist/core/handoffs.test.d.ts +0 -8
- package/dist/core/handoffs.test.d.ts.map +0 -1
- package/dist/core/handoffs.test.js +0 -231
- package/dist/core/handoffs.test.js.map +0 -1
- package/dist/core/orchestrator.d.ts +0 -246
- package/dist/core/orchestrator.d.ts.map +0 -1
- package/dist/core/orchestrator.js +0 -514
- package/dist/core/orchestrator.js.map +0 -1
- package/dist/core/orchestrator.test.d.ts +0 -8
- package/dist/core/orchestrator.test.d.ts.map +0 -1
- package/dist/core/orchestrator.test.js +0 -517
- package/dist/core/orchestrator.test.js.map +0 -1
- package/dist/core/router.d.ts +0 -102
- package/dist/core/router.d.ts.map +0 -1
- package/dist/core/router.js +0 -178
- package/dist/core/router.js.map +0 -1
- package/dist/core/router.test.d.ts +0 -8
- package/dist/core/router.test.d.ts.map +0 -1
- package/dist/core/router.test.js +0 -215
- package/dist/core/router.test.js.map +0 -1
- package/dist/init.test.js +0 -288
- package/dist/providers/azure.d.ts +0 -147
- package/dist/providers/azure.d.ts.map +0 -1
- package/dist/providers/azure.js +0 -491
- package/dist/providers/azure.js.map +0 -1
- package/dist/providers/azure.test.d.ts +0 -11
- package/dist/providers/azure.test.d.ts.map +0 -1
- package/dist/providers/azure.test.js +0 -330
- package/dist/providers/azure.test.js.map +0 -1
- package/dist/providers/config.d.ts +0 -87
- package/dist/providers/config.d.ts.map +0 -1
- package/dist/providers/config.js +0 -193
- package/dist/providers/config.js.map +0 -1
- package/dist/providers/config.test.d.ts +0 -7
- package/dist/providers/config.test.d.ts.map +0 -1
- package/dist/providers/config.test.js +0 -370
- package/dist/providers/config.test.js.map +0 -1
- package/dist/providers/index.d.ts +0 -18
- package/dist/providers/index.d.ts.map +0 -1
- package/dist/providers/index.js +0 -14
- package/dist/providers/index.js.map +0 -1
- package/dist/providers/interface.d.ts +0 -191
- package/dist/providers/interface.d.ts.map +0 -1
- package/dist/providers/interface.js +0 -94
- package/dist/providers/interface.js.map +0 -1
- package/dist/providers/retry.d.ts +0 -128
- package/dist/providers/retry.d.ts.map +0 -1
- package/dist/providers/retry.js +0 -205
- package/dist/providers/retry.js.map +0 -1
- package/dist/providers/retry.test.d.ts +0 -7
- package/dist/providers/retry.test.d.ts.map +0 -1
- package/dist/providers/retry.test.js +0 -439
- package/dist/providers/retry.test.js.map +0 -1
- package/dist/providers/streaming.d.ts +0 -157
- package/dist/providers/streaming.d.ts.map +0 -1
- package/dist/providers/streaming.js +0 -233
- package/dist/providers/streaming.js.map +0 -1
- package/dist/providers/streaming.test.d.ts +0 -7
- package/dist/providers/streaming.test.d.ts.map +0 -1
- package/dist/providers/streaming.test.js +0 -372
- package/dist/providers/streaming.test.js.map +0 -1
- package/dist/providers/types.d.ts +0 -209
- package/dist/providers/types.d.ts.map +0 -1
- package/dist/providers/types.js +0 -53
- package/dist/providers/types.js.map +0 -1
- package/dist/providers/types.test.d.ts +0 -7
- package/dist/providers/types.test.d.ts.map +0 -1
- package/dist/providers/types.test.js +0 -141
- package/dist/providers/types.test.js.map +0 -1
- package/dist/tools/cli/beads.d.ts +0 -27
- package/dist/tools/cli/beads.d.ts.map +0 -1
- package/dist/tools/cli/beads.js +0 -172
- package/dist/tools/cli/beads.js.map +0 -1
- package/dist/tools/cli/beads.test.d.ts +0 -8
- package/dist/tools/cli/beads.test.d.ts.map +0 -1
- package/dist/tools/cli/beads.test.js +0 -264
- package/dist/tools/cli/beads.test.js.map +0 -1
- package/dist/tools/cli/editFile.d.ts +0 -17
- package/dist/tools/cli/editFile.d.ts.map +0 -1
- package/dist/tools/cli/editFile.js +0 -125
- package/dist/tools/cli/editFile.js.map +0 -1
- package/dist/tools/cli/editFile.test.d.ts +0 -8
- package/dist/tools/cli/editFile.test.d.ts.map +0 -1
- package/dist/tools/cli/editFile.test.js +0 -177
- package/dist/tools/cli/editFile.test.js.map +0 -1
- package/dist/tools/cli/readFile.d.ts +0 -25
- package/dist/tools/cli/readFile.d.ts.map +0 -1
- package/dist/tools/cli/readFile.js +0 -118
- package/dist/tools/cli/readFile.js.map +0 -1
- package/dist/tools/cli/readFile.test.d.ts +0 -8
- package/dist/tools/cli/readFile.test.d.ts.map +0 -1
- package/dist/tools/cli/readFile.test.js +0 -194
- package/dist/tools/cli/readFile.test.js.map +0 -1
- package/dist/tools/cli/search.d.ts +0 -16
- package/dist/tools/cli/search.d.ts.map +0 -1
- package/dist/tools/cli/search.js +0 -261
- package/dist/tools/cli/search.js.map +0 -1
- package/dist/tools/cli/search.test.d.ts +0 -8
- package/dist/tools/cli/search.test.d.ts.map +0 -1
- package/dist/tools/cli/search.test.js +0 -172
- package/dist/tools/cli/search.test.js.map +0 -1
- package/dist/tools/cli/subagent.d.ts +0 -43
- package/dist/tools/cli/subagent.d.ts.map +0 -1
- package/dist/tools/cli/subagent.js +0 -99
- package/dist/tools/cli/subagent.js.map +0 -1
- package/dist/tools/cli/subagent.test.d.ts +0 -8
- package/dist/tools/cli/subagent.test.d.ts.map +0 -1
- package/dist/tools/cli/subagent.test.js +0 -190
- package/dist/tools/cli/subagent.test.js.map +0 -1
- package/dist/tools/cli/terminal.d.ts +0 -19
- package/dist/tools/cli/terminal.d.ts.map +0 -1
- package/dist/tools/cli/terminal.js +0 -164
- package/dist/tools/cli/terminal.js.map +0 -1
- package/dist/tools/cli/terminal.test.d.ts +0 -8
- package/dist/tools/cli/terminal.test.d.ts.map +0 -1
- package/dist/tools/cli/terminal.test.js +0 -161
- package/dist/tools/cli/terminal.test.js.map +0 -1
- package/dist/tools/index.d.ts +0 -25
- package/dist/tools/index.d.ts.map +0 -1
- package/dist/tools/index.js +0 -41
- package/dist/tools/index.js.map +0 -1
- package/dist/tools/interface.d.ts +0 -64
- package/dist/tools/interface.d.ts.map +0 -1
- package/dist/tools/interface.js +0 -37
- package/dist/tools/interface.js.map +0 -1
- package/dist/tools/interface.test.d.ts +0 -7
- package/dist/tools/interface.test.d.ts.map +0 -1
- package/dist/tools/interface.test.js +0 -179
- package/dist/tools/interface.test.js.map +0 -1
- package/dist/tools/mcp/bridge.d.ts +0 -48
- package/dist/tools/mcp/bridge.d.ts.map +0 -1
- package/dist/tools/mcp/bridge.js +0 -128
- package/dist/tools/mcp/bridge.js.map +0 -1
- package/dist/tools/mcp/bridge.test.d.ts +0 -8
- package/dist/tools/mcp/bridge.test.d.ts.map +0 -1
- package/dist/tools/mcp/bridge.test.js +0 -300
- package/dist/tools/mcp/bridge.test.js.map +0 -1
- package/dist/tools/mcp/client.d.ts +0 -135
- package/dist/tools/mcp/client.d.ts.map +0 -1
- package/dist/tools/mcp/client.js +0 -263
- package/dist/tools/mcp/client.js.map +0 -1
- package/dist/tools/mcp/client.test.d.ts +0 -8
- package/dist/tools/mcp/client.test.d.ts.map +0 -1
- package/dist/tools/mcp/client.test.js +0 -390
- package/dist/tools/mcp/client.test.js.map +0 -1
- package/dist/tools/registry.d.ts +0 -82
- package/dist/tools/registry.d.ts.map +0 -1
- package/dist/tools/registry.js +0 -99
- package/dist/tools/registry.js.map +0 -1
- package/dist/tools/registry.test.d.ts +0 -7
- package/dist/tools/registry.test.d.ts.map +0 -1
- package/dist/tools/registry.test.js +0 -199
- package/dist/tools/registry.test.js.map +0 -1
- package/dist/tools/suite.test.d.ts +0 -11
- package/dist/tools/suite.test.d.ts.map +0 -1
- package/dist/tools/suite.test.js +0 -119
- package/dist/tools/suite.test.js.map +0 -1
- package/dist/tools/types.d.ts +0 -75
- package/dist/tools/types.d.ts.map +0 -1
- package/dist/tools/types.js +0 -30
- package/dist/tools/types.js.map +0 -1
- package/dist/tools/types.test.d.ts +0 -7
- package/dist/tools/types.test.d.ts.map +0 -1
- package/dist/tools/types.test.js +0 -178
- package/dist/tools/types.test.js.map +0 -1
- package/templates/.vscode/mcp.json +0 -20
- package/templates/CLAUDE.md +0 -129
package/dist/tools/cli/search.js
DELETED
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Search Tool
|
|
3
|
-
*
|
|
4
|
-
* Searches file contents using ripgrep (preferred) or a Node.js fallback.
|
|
5
|
-
* Validates search paths against traversal attacks and checks permissions
|
|
6
|
-
* before reading any files.
|
|
7
|
-
*/
|
|
8
|
-
import { execFile } from 'node:child_process';
|
|
9
|
-
import { readdir, readFile } from 'node:fs/promises';
|
|
10
|
-
import { join, resolve, relative } from 'node:path';
|
|
11
|
-
import { containsTraversal } from '../../lib/pathValidation.js';
|
|
12
|
-
import { ToolError } from '../types.js';
|
|
13
|
-
/** Default maximum number of results to return */
|
|
14
|
-
const DEFAULT_MAX_RESULTS = 50;
|
|
15
|
-
/** Input schema for the search tool */
|
|
16
|
-
const inputSchema = {
|
|
17
|
-
type: 'object',
|
|
18
|
-
properties: {
|
|
19
|
-
query: {
|
|
20
|
-
type: 'string',
|
|
21
|
-
description: 'Search pattern (plain text or regex)',
|
|
22
|
-
},
|
|
23
|
-
isRegexp: {
|
|
24
|
-
type: 'boolean',
|
|
25
|
-
description: 'Whether query is a regular expression (default: false)',
|
|
26
|
-
},
|
|
27
|
-
includePattern: {
|
|
28
|
-
type: 'string',
|
|
29
|
-
description: 'Glob pattern to filter files (e.g., "**/*.ts")',
|
|
30
|
-
},
|
|
31
|
-
path: {
|
|
32
|
-
type: 'string',
|
|
33
|
-
description: 'Directory to search in (defaults to working directory)',
|
|
34
|
-
},
|
|
35
|
-
maxResults: {
|
|
36
|
-
type: 'number',
|
|
37
|
-
description: 'Maximum number of results to return (default: 50)',
|
|
38
|
-
},
|
|
39
|
-
},
|
|
40
|
-
required: ['query'],
|
|
41
|
-
};
|
|
42
|
-
/**
|
|
43
|
-
* Validate and resolve the search path, ensuring it stays within workingDir.
|
|
44
|
-
*
|
|
45
|
-
* @param searchPath - User-provided search path (may be relative or absolute)
|
|
46
|
-
* @param workingDir - The allowed root directory
|
|
47
|
-
* @param toolName - Name of the calling tool (for error reporting)
|
|
48
|
-
* @returns The resolved absolute path
|
|
49
|
-
* @throws {ToolError} If the path escapes workingDir or contains traversal
|
|
50
|
-
*/
|
|
51
|
-
function validateSearchPath(searchPath, workingDir, toolName) {
|
|
52
|
-
if (containsTraversal(searchPath)) {
|
|
53
|
-
throw new ToolError('Search path contains directory traversal sequences', 'INVALID_INPUT', toolName);
|
|
54
|
-
}
|
|
55
|
-
const resolved = resolve(workingDir, searchPath);
|
|
56
|
-
const normalizedWorking = resolve(workingDir);
|
|
57
|
-
if (!resolved.startsWith(normalizedWorking)) {
|
|
58
|
-
throw new ToolError('Search path is outside the working directory', 'INVALID_INPUT', toolName);
|
|
59
|
-
}
|
|
60
|
-
return resolved;
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Attempt to search using ripgrep (rg).
|
|
64
|
-
*
|
|
65
|
-
* @returns Search output string, or null if rg is not available
|
|
66
|
-
*/
|
|
67
|
-
function searchWithRipgrep(query, isRegexp, includePattern, searchPath, maxResults, signal) {
|
|
68
|
-
return new Promise((resolvePromise) => {
|
|
69
|
-
const args = [
|
|
70
|
-
'--no-heading',
|
|
71
|
-
'--line-number',
|
|
72
|
-
'--max-count', String(maxResults),
|
|
73
|
-
];
|
|
74
|
-
if (isRegexp) {
|
|
75
|
-
args.push('-e', query);
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
args.push('--fixed-strings', query);
|
|
79
|
-
}
|
|
80
|
-
if (includePattern) {
|
|
81
|
-
args.push('--glob', includePattern);
|
|
82
|
-
}
|
|
83
|
-
args.push(searchPath);
|
|
84
|
-
const child = execFile('rg', args, { maxBuffer: 1024 * 1024, signal }, (error, stdout) => {
|
|
85
|
-
if (error) {
|
|
86
|
-
const nodeErr = error;
|
|
87
|
-
// Exit code 1 means no matches — that's fine
|
|
88
|
-
if (nodeErr.code === 'ENOENT' || nodeErr.message?.includes('ENOENT')) {
|
|
89
|
-
// rg not found
|
|
90
|
-
resolvePromise(null);
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
if ('code' in error && error.code === 1) {
|
|
94
|
-
// rg exit code 1 = no matches
|
|
95
|
-
resolvePromise('');
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
// Other errors: treat as rg not available
|
|
99
|
-
resolvePromise(null);
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
resolvePromise(stdout);
|
|
103
|
-
});
|
|
104
|
-
// Handle abort to clean up child process
|
|
105
|
-
if (signal) {
|
|
106
|
-
signal.addEventListener('abort', () => {
|
|
107
|
-
child.kill();
|
|
108
|
-
}, { once: true });
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Check whether a filename matches a simple glob pattern.
|
|
114
|
-
* Supports patterns like "*.ts", "**\/*.ts", "*.{ts,js}".
|
|
115
|
-
*/
|
|
116
|
-
function matchesGlob(filePath, pattern) {
|
|
117
|
-
// Convert glob to regex
|
|
118
|
-
let regexStr = pattern
|
|
119
|
-
.replace(/\*\*\//g, '(.+/)?') // **/ matches any directory depth
|
|
120
|
-
.replace(/\*/g, '[^/]*') // * matches anything except /
|
|
121
|
-
.replace(/\?/g, '[^/]') // ? matches single char
|
|
122
|
-
.replace(/\{([^}]+)\}/g, (_match, group) => {
|
|
123
|
-
// {ts,js} => (ts|js)
|
|
124
|
-
return '(' + group.split(',').join('|') + ')';
|
|
125
|
-
});
|
|
126
|
-
regexStr = '^' + regexStr + '$';
|
|
127
|
-
try {
|
|
128
|
-
const regex = new RegExp(regexStr);
|
|
129
|
-
return regex.test(filePath);
|
|
130
|
-
}
|
|
131
|
-
catch {
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Recursively collect all files under a directory.
|
|
137
|
-
*/
|
|
138
|
-
async function collectFiles(dir) {
|
|
139
|
-
const files = [];
|
|
140
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
141
|
-
for (const entry of entries) {
|
|
142
|
-
const fullPath = join(dir, entry.name);
|
|
143
|
-
if (entry.isDirectory()) {
|
|
144
|
-
// Skip hidden directories and node_modules
|
|
145
|
-
if (entry.name.startsWith('.') || entry.name === 'node_modules') {
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
const subFiles = await collectFiles(fullPath);
|
|
149
|
-
files.push(...subFiles);
|
|
150
|
-
}
|
|
151
|
-
else if (entry.isFile()) {
|
|
152
|
-
files.push(fullPath);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
return files;
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Fallback Node.js search when ripgrep is not available.
|
|
159
|
-
*/
|
|
160
|
-
async function searchWithNode(query, isRegexp, includePattern, searchPath, maxResults) {
|
|
161
|
-
const files = await collectFiles(searchPath);
|
|
162
|
-
const results = [];
|
|
163
|
-
let pattern;
|
|
164
|
-
if (isRegexp) {
|
|
165
|
-
pattern = new RegExp(query);
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
// Escape special regex characters for literal search
|
|
169
|
-
const escaped = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
170
|
-
pattern = new RegExp(escaped);
|
|
171
|
-
}
|
|
172
|
-
for (const filePath of files) {
|
|
173
|
-
if (results.length >= maxResults)
|
|
174
|
-
break;
|
|
175
|
-
// Apply include pattern filter
|
|
176
|
-
if (includePattern) {
|
|
177
|
-
const relPath = relative(searchPath, filePath);
|
|
178
|
-
if (!matchesGlob(relPath, includePattern)) {
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
let contents;
|
|
183
|
-
try {
|
|
184
|
-
contents = await readFile(filePath, 'utf-8');
|
|
185
|
-
}
|
|
186
|
-
catch {
|
|
187
|
-
// Skip files we can't read (binary, permission denied, etc.)
|
|
188
|
-
continue;
|
|
189
|
-
}
|
|
190
|
-
const lines = contents.split('\n');
|
|
191
|
-
for (let i = 0; i < lines.length; i++) {
|
|
192
|
-
if (results.length >= maxResults)
|
|
193
|
-
break;
|
|
194
|
-
if (pattern.test(lines[i])) {
|
|
195
|
-
results.push(`${filePath}:${i + 1}:${lines[i]}`);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
return results.join('\n');
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Search tool implementation.
|
|
203
|
-
*
|
|
204
|
-
* Searches file contents using ripgrep when available, falling back to
|
|
205
|
-
* a recursive Node.js search. Results are returned in `filepath:line:content` format.
|
|
206
|
-
*/
|
|
207
|
-
export const searchTool = {
|
|
208
|
-
name: 'search',
|
|
209
|
-
description: 'Search file contents using grep/ripgrep. Returns matches in filepath:line:content format.',
|
|
210
|
-
inputSchema,
|
|
211
|
-
async execute(input, context) {
|
|
212
|
-
// Check permission
|
|
213
|
-
if (!context.permissions.allowFileRead) {
|
|
214
|
-
throw new ToolError('File read permission denied', 'PERMISSION_DENIED', 'search');
|
|
215
|
-
}
|
|
216
|
-
// Validate query
|
|
217
|
-
const query = input.query;
|
|
218
|
-
if (typeof query !== 'string' || query.trim().length === 0) {
|
|
219
|
-
throw new ToolError('query is required and must be a non-empty string', 'INVALID_INPUT', 'search');
|
|
220
|
-
}
|
|
221
|
-
const isRegexp = typeof input.isRegexp === 'boolean' ? input.isRegexp : false;
|
|
222
|
-
const includePattern = typeof input.includePattern === 'string' ? input.includePattern : undefined;
|
|
223
|
-
const maxResults = typeof input.maxResults === 'number' && input.maxResults > 0
|
|
224
|
-
? input.maxResults
|
|
225
|
-
: DEFAULT_MAX_RESULTS;
|
|
226
|
-
// Validate and resolve search path
|
|
227
|
-
const rawPath = typeof input.path === 'string' ? input.path : context.workingDir;
|
|
228
|
-
const searchPath = validateSearchPath(rawPath, context.workingDir, 'search');
|
|
229
|
-
// Try ripgrep first, fall back to Node.js search
|
|
230
|
-
let output = await searchWithRipgrep(query, isRegexp, includePattern, searchPath, maxResults, context.signal);
|
|
231
|
-
let usedRipgrep = true;
|
|
232
|
-
if (output === null) {
|
|
233
|
-
// rg not available, use Node.js fallback
|
|
234
|
-
usedRipgrep = false;
|
|
235
|
-
try {
|
|
236
|
-
output = await searchWithNode(query, isRegexp, includePattern, searchPath, maxResults);
|
|
237
|
-
}
|
|
238
|
-
catch (err) {
|
|
239
|
-
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
240
|
-
throw new ToolError(`Search failed: ${message}`, 'EXECUTION_FAILED', 'search', {
|
|
241
|
-
cause: err instanceof Error ? err : undefined,
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
// Trim trailing newline
|
|
246
|
-
const trimmed = output.trim();
|
|
247
|
-
// Count matches
|
|
248
|
-
const matchCount = trimmed.length === 0 ? 0 : trimmed.split('\n').length;
|
|
249
|
-
return {
|
|
250
|
-
success: true,
|
|
251
|
-
output: trimmed,
|
|
252
|
-
metadata: {
|
|
253
|
-
matchCount,
|
|
254
|
-
maxResults,
|
|
255
|
-
searchPath,
|
|
256
|
-
engine: usedRipgrep ? 'ripgrep' : 'node',
|
|
257
|
-
},
|
|
258
|
-
};
|
|
259
|
-
},
|
|
260
|
-
};
|
|
261
|
-
//# sourceMappingURL=search.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../../src/tools/cli/search.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAGhE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,kDAAkD;AAClD,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAE/B,uCAAuC;AACvC,MAAM,WAAW,GAAoB;IACnC,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE;QACV,KAAK,EAAE;YACL,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,sCAAsC;SACpD;QACD,QAAQ,EAAE;YACR,IAAI,EAAE,SAAS;YACf,WAAW,EAAE,wDAAwD;SACtE;QACD,cAAc,EAAE;YACd,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,gDAAgD;SAC9D;QACD,IAAI,EAAE;YACJ,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,wDAAwD;SACtE;QACD,UAAU,EAAE;YACV,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,mDAAmD;SACjE;KACF;IACD,QAAQ,EAAE,CAAC,OAAO,CAAC;CACpB,CAAC;AAEF;;;;;;;;GAQG;AACH,SAAS,kBAAkB,CAAC,UAAkB,EAAE,UAAkB,EAAE,QAAgB;IAClF,IAAI,iBAAiB,CAAC,UAAU,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,SAAS,CAAC,oDAAoD,EAAE,eAAe,EAAE,QAAQ,CAAC,CAAC;IACvG,CAAC;IAED,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IACjD,MAAM,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAE9C,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,SAAS,CAAC,8CAA8C,EAAE,eAAe,EAAE,QAAQ,CAAC,CAAC;IACjG,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;GAIG;AACH,SAAS,iBAAiB,CACxB,KAAa,EACb,QAAiB,EACjB,cAAkC,EAClC,UAAkB,EAClB,UAAkB,EAClB,MAAoB;IAEpB,OAAO,IAAI,OAAO,CAAC,CAAC,cAAc,EAAE,EAAE;QACpC,MAAM,IAAI,GAAa;YACrB,cAAc;YACd,eAAe;YACf,aAAa,EAAE,MAAM,CAAC,UAAU,CAAC;SAClC,CAAC;QAEF,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC;QACtC,CAAC;QAED,IAAI,cAAc,EAAE,CAAC;YACnB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;QACtC,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAEtB,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,GAAG,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;YACvF,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,OAAO,GAAG,KAA8B,CAAC;gBAC/C,6CAA6C;gBAC7C,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;oBACrE,eAAe;oBACf,cAAc,CAAC,IAAI,CAAC,CAAC;oBACrB,OAAO;gBACT,CAAC;gBACD,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;oBACxC,8BAA8B;oBAC9B,cAAc,CAAC,EAAE,CAAC,CAAC;oBACnB,OAAO;gBACT,CAAC;gBACD,0CAA0C;gBAC1C,cAAc,CAAC,IAAI,CAAC,CAAC;gBACrB,OAAO;YACT,CAAC;YACD,cAAc,CAAC,MAAM,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;QAEH,yCAAyC;QACzC,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;gBACpC,KAAK,CAAC,IAAI,EAAE,CAAC;YACf,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACrB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,SAAS,WAAW,CAAC,QAAgB,EAAE,OAAe;IACpD,wBAAwB;IACxB,IAAI,QAAQ,GAAG,OAAO;SACnB,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAK,kCAAkC;SACnE,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAW,8BAA8B;SAChE,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAY,wBAAwB;SAC1D,OAAO,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE,KAAa,EAAE,EAAE;QACjD,qBAAqB;QACrB,OAAO,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IAChD,CAAC,CAAC,CAAC;IACL,QAAQ,GAAG,GAAG,GAAG,QAAQ,GAAG,GAAG,CAAC;IAEhC,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC;QACnC,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,YAAY,CAAC,GAAW;IACrC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAE5D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,2CAA2C;YAC3C,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;gBAChE,SAAS;YACX,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAC;YAC9C,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC;QAC1B,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1B,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,cAAc,CAC3B,KAAa,EACb,QAAiB,EACjB,cAAkC,EAClC,UAAkB,EAClB,UAAkB;IAElB,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,UAAU,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,IAAI,OAAe,CAAC;IACpB,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;SAAM,CAAC;QACN,qDAAqD;QACrD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;QAC7D,OAAO,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IAED,KAAK,MAAM,QAAQ,IAAI,KAAK,EAAE,CAAC;QAC7B,IAAI,OAAO,CAAC,MAAM,IAAI,UAAU;YAAE,MAAM;QAExC,+BAA+B;QAC/B,IAAI,cAAc,EAAE,CAAC;YACnB,MAAM,OAAO,GAAG,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;YAC/C,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,cAAc,CAAC,EAAE,CAAC;gBAC1C,SAAS;YACX,CAAC;QACH,CAAC;QAED,IAAI,QAAgB,CAAC;QACrB,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,6DAA6D;YAC7D,SAAS;QACX,CAAC;QAED,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACnC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,IAAI,OAAO,CAAC,MAAM,IAAI,UAAU;gBAAE,MAAM;YACxC,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC3B,OAAO,CAAC,IAAI,CAAC,GAAG,QAAQ,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACnD,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,UAAU,GAAS;IAC9B,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,2FAA2F;IACxG,WAAW;IAEX,KAAK,CAAC,OAAO,CAAC,KAA8B,EAAE,OAAoB;QAChE,mBAAmB;QACnB,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,aAAa,EAAE,CAAC;YACvC,MAAM,IAAI,SAAS,CAAC,6BAA6B,EAAE,mBAAmB,EAAE,QAAQ,CAAC,CAAC;QACpF,CAAC;QAED,iBAAiB;QACjB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3D,MAAM,IAAI,SAAS,CAAC,kDAAkD,EAAE,eAAe,EAAE,QAAQ,CAAC,CAAC;QACrG,CAAC;QAED,MAAM,QAAQ,GAAG,OAAO,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC;QAC9E,MAAM,cAAc,GAAG,OAAO,KAAK,CAAC,cAAc,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC;QACnG,MAAM,UAAU,GAAG,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ,IAAI,KAAK,CAAC,UAAU,GAAG,CAAC;YAC7E,CAAC,CAAC,KAAK,CAAC,UAAU;YAClB,CAAC,CAAC,mBAAmB,CAAC;QAExB,mCAAmC;QACnC,MAAM,OAAO,GAAG,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC;QACjF,MAAM,UAAU,GAAG,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAE7E,iDAAiD;QACjD,IAAI,MAAM,GAAG,MAAM,iBAAiB,CAAC,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAC9G,IAAI,WAAW,GAAG,IAAI,CAAC;QAEvB,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,yCAAyC;YACzC,WAAW,GAAG,KAAK,CAAC;YACpB,IAAI,CAAC;gBACH,MAAM,GAAG,MAAM,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;YACzF,CAAC;YAAC,OAAO,GAAY,EAAE,CAAC;gBACtB,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;gBACrE,MAAM,IAAI,SAAS,CAAC,kBAAkB,OAAO,EAAE,EAAE,kBAAkB,EAAE,QAAQ,EAAE;oBAC7E,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS;iBAC9C,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,wBAAwB;QACxB,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;QAE9B,gBAAgB;QAChB,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;QAEzE,OAAO;YACL,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,OAAO;YACf,QAAQ,EAAE;gBACR,UAAU;gBACV,UAAU;gBACV,UAAU;gBACV,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM;aACzC;SACF,CAAC;IACJ,CAAC;CACF,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"search.test.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/search.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Search Tool Tests
|
|
3
|
-
*
|
|
4
|
-
* Tests for the search tool implementation.
|
|
5
|
-
* Uses node:test and node:assert with real file I/O via tmpdir.
|
|
6
|
-
*/
|
|
7
|
-
import { describe, it, beforeEach, afterEach } from 'node:test';
|
|
8
|
-
import assert from 'node:assert';
|
|
9
|
-
import { mkdtempSync, writeFileSync, mkdirSync, rmSync } from 'node:fs';
|
|
10
|
-
import { join } from 'node:path';
|
|
11
|
-
import { tmpdir } from 'node:os';
|
|
12
|
-
import { searchTool } from './search.js';
|
|
13
|
-
import { ToolError } from '../types.js';
|
|
14
|
-
/** Create a ToolContext with sensible defaults */
|
|
15
|
-
function createContext(overrides) {
|
|
16
|
-
return {
|
|
17
|
-
workingDir: overrides?.workingDir ?? '/tmp',
|
|
18
|
-
permissions: overrides?.permissions ?? {
|
|
19
|
-
allowFileRead: true,
|
|
20
|
-
allowFileWrite: false,
|
|
21
|
-
allowTerminal: false,
|
|
22
|
-
allowNetwork: false,
|
|
23
|
-
},
|
|
24
|
-
signal: overrides?.signal,
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
describe('search tool', () => {
|
|
28
|
-
let tempDir;
|
|
29
|
-
beforeEach(() => {
|
|
30
|
-
tempDir = mkdtempSync(join(tmpdir(), 'beth-search-'));
|
|
31
|
-
});
|
|
32
|
-
afterEach(() => {
|
|
33
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
34
|
-
});
|
|
35
|
-
describe('metadata', () => {
|
|
36
|
-
it('should have name "search"', () => {
|
|
37
|
-
assert.strictEqual(searchTool.name, 'search');
|
|
38
|
-
});
|
|
39
|
-
it('should have a description', () => {
|
|
40
|
-
assert.ok(searchTool.description.length > 0);
|
|
41
|
-
});
|
|
42
|
-
it('should have an inputSchema with query required', () => {
|
|
43
|
-
assert.strictEqual(searchTool.inputSchema.type, 'object');
|
|
44
|
-
assert.ok(Array.isArray(searchTool.inputSchema.required));
|
|
45
|
-
assert.ok(searchTool.inputSchema.required.includes('query'));
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
describe('finding matches', () => {
|
|
49
|
-
it('should find matches in files', async () => {
|
|
50
|
-
writeFileSync(join(tempDir, 'file1.txt'), 'hello world\ngoodbye world');
|
|
51
|
-
writeFileSync(join(tempDir, 'file2.txt'), 'hello there');
|
|
52
|
-
const result = await searchTool.execute({ query: 'hello' }, createContext({ workingDir: tempDir }));
|
|
53
|
-
assert.strictEqual(result.success, true);
|
|
54
|
-
assert.ok(result.output.includes('hello'));
|
|
55
|
-
assert.ok(result.metadata?.matchCount >= 2);
|
|
56
|
-
});
|
|
57
|
-
it('should return empty output for no matches (success=true)', async () => {
|
|
58
|
-
writeFileSync(join(tempDir, 'file.txt'), 'nothing relevant here');
|
|
59
|
-
const result = await searchTool.execute({ query: 'zzz_no_match_zzz' }, createContext({ workingDir: tempDir }));
|
|
60
|
-
assert.strictEqual(result.success, true);
|
|
61
|
-
assert.strictEqual(result.output, '');
|
|
62
|
-
assert.strictEqual(result.metadata?.matchCount, 0);
|
|
63
|
-
});
|
|
64
|
-
it('should return results in filepath:line:content format', async () => {
|
|
65
|
-
writeFileSync(join(tempDir, 'formatted.txt'), 'line one\nfind me here\nline three');
|
|
66
|
-
const result = await searchTool.execute({ query: 'find me' }, createContext({ workingDir: tempDir }));
|
|
67
|
-
assert.strictEqual(result.success, true);
|
|
68
|
-
// Output should contain the file path, line number, and content
|
|
69
|
-
assert.ok(result.output.includes('formatted.txt'));
|
|
70
|
-
assert.ok(result.output.includes('find me here'));
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
describe('maxResults', () => {
|
|
74
|
-
it('should respect maxResults limit', async () => {
|
|
75
|
-
// Create a file with many matching lines
|
|
76
|
-
const lines = Array.from({ length: 20 }, (_, i) => `match line ${i}`);
|
|
77
|
-
writeFileSync(join(tempDir, 'many.txt'), lines.join('\n'));
|
|
78
|
-
const result = await searchTool.execute({ query: 'match line', maxResults: 5 }, createContext({ workingDir: tempDir }));
|
|
79
|
-
assert.strictEqual(result.success, true);
|
|
80
|
-
const matchCount = result.metadata?.matchCount;
|
|
81
|
-
assert.ok(matchCount <= 5, `Expected at most 5 matches, got ${matchCount}`);
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
describe('regex patterns', () => {
|
|
85
|
-
it('should handle regex patterns', async () => {
|
|
86
|
-
writeFileSync(join(tempDir, 'regex.txt'), 'foo123bar\nfoo456bar\nhello world');
|
|
87
|
-
const result = await searchTool.execute({ query: 'foo\\d+bar', isRegexp: true }, createContext({ workingDir: tempDir }));
|
|
88
|
-
assert.strictEqual(result.success, true);
|
|
89
|
-
assert.ok(result.output.includes('foo123bar'));
|
|
90
|
-
assert.ok(result.output.includes('foo456bar'));
|
|
91
|
-
assert.ok(!result.output.includes('hello world'));
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
describe('include pattern filtering', () => {
|
|
95
|
-
it('should filter by include pattern', async () => {
|
|
96
|
-
writeFileSync(join(tempDir, 'code.ts'), 'const x = matchword;');
|
|
97
|
-
writeFileSync(join(tempDir, 'notes.md'), 'matchword in docs');
|
|
98
|
-
const result = await searchTool.execute({ query: 'matchword', includePattern: '*.ts' }, createContext({ workingDir: tempDir }));
|
|
99
|
-
assert.strictEqual(result.success, true);
|
|
100
|
-
assert.ok(result.output.includes('code.ts'));
|
|
101
|
-
// The md file may or may not appear depending on rg vs node
|
|
102
|
-
// Just verify the ts file was found
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
describe('permissions', () => {
|
|
106
|
-
it('should return PERMISSION_DENIED when allowFileRead is false', async () => {
|
|
107
|
-
const ctx = createContext({
|
|
108
|
-
workingDir: tempDir,
|
|
109
|
-
permissions: {
|
|
110
|
-
allowFileRead: false,
|
|
111
|
-
allowFileWrite: false,
|
|
112
|
-
allowTerminal: false,
|
|
113
|
-
allowNetwork: false,
|
|
114
|
-
},
|
|
115
|
-
});
|
|
116
|
-
await assert.rejects(() => searchTool.execute({ query: 'test' }, ctx), (err) => {
|
|
117
|
-
assert.ok(err instanceof ToolError);
|
|
118
|
-
assert.strictEqual(err.code, 'PERMISSION_DENIED');
|
|
119
|
-
return true;
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
describe('path validation', () => {
|
|
124
|
-
it('should reject search paths with traversal', async () => {
|
|
125
|
-
await assert.rejects(() => searchTool.execute({ query: 'test', path: '../../../etc' }, createContext({ workingDir: tempDir })), (err) => {
|
|
126
|
-
assert.ok(err instanceof ToolError);
|
|
127
|
-
assert.strictEqual(err.code, 'INVALID_INPUT');
|
|
128
|
-
assert.ok(err.message.includes('traversal'));
|
|
129
|
-
return true;
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
it('should default path to workingDir', async () => {
|
|
133
|
-
writeFileSync(join(tempDir, 'default.txt'), 'find_default_marker');
|
|
134
|
-
const result = await searchTool.execute({ query: 'find_default_marker' }, createContext({ workingDir: tempDir }));
|
|
135
|
-
assert.strictEqual(result.success, true);
|
|
136
|
-
assert.ok(result.output.includes('find_default_marker'));
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
describe('input validation', () => {
|
|
140
|
-
it('should reject empty query', async () => {
|
|
141
|
-
await assert.rejects(() => searchTool.execute({ query: '' }, createContext({ workingDir: tempDir })), (err) => {
|
|
142
|
-
assert.ok(err instanceof ToolError);
|
|
143
|
-
assert.strictEqual(err.code, 'INVALID_INPUT');
|
|
144
|
-
return true;
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
it('should reject non-string query', async () => {
|
|
148
|
-
await assert.rejects(() => searchTool.execute({ query: 123 }, createContext({ workingDir: tempDir })), (err) => {
|
|
149
|
-
assert.ok(err instanceof ToolError);
|
|
150
|
-
assert.strictEqual(err.code, 'INVALID_INPUT');
|
|
151
|
-
return true;
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
describe('node fallback', () => {
|
|
156
|
-
it('should search subdirectories', async () => {
|
|
157
|
-
const subDir = join(tempDir, 'subdir');
|
|
158
|
-
mkdirSync(subDir);
|
|
159
|
-
writeFileSync(join(subDir, 'deep.txt'), 'deep_search_marker');
|
|
160
|
-
const result = await searchTool.execute({ query: 'deep_search_marker' }, createContext({ workingDir: tempDir }));
|
|
161
|
-
assert.strictEqual(result.success, true);
|
|
162
|
-
assert.ok(result.output.includes('deep_search_marker'));
|
|
163
|
-
});
|
|
164
|
-
it('should report which engine was used', async () => {
|
|
165
|
-
writeFileSync(join(tempDir, 'engine.txt'), 'engine_test_marker');
|
|
166
|
-
const result = await searchTool.execute({ query: 'engine_test_marker' }, createContext({ workingDir: tempDir }));
|
|
167
|
-
assert.strictEqual(result.success, true);
|
|
168
|
-
assert.ok(result.metadata?.engine === 'ripgrep' || result.metadata?.engine === 'node', `Expected engine to be 'ripgrep' or 'node', got '${result.metadata?.engine}'`);
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
//# sourceMappingURL=search.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"search.test.js","sourceRoot":"","sources":["../../../src/tools/cli/search.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Subagent Tool
|
|
3
|
-
*
|
|
4
|
-
* Spawns a new agent conversation by preparing a subagent invocation request.
|
|
5
|
-
*
|
|
6
|
-
* **This is intentionally a stub/placeholder.** The actual agent conversation
|
|
7
|
-
* loop requires the orchestrator (Phase 4). For now, `execute` validates input
|
|
8
|
-
* and returns a ToolResult with `status: 'pending'` metadata. The orchestrator
|
|
9
|
-
* will inspect tool results using `isSubagentRequest()` and handle the real
|
|
10
|
-
* invocation.
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```typescript
|
|
14
|
-
* const result = await subagentTool.execute(
|
|
15
|
-
* { agentName: 'developer', prompt: 'Implement the login page', description: 'Login UI' },
|
|
16
|
-
* context,
|
|
17
|
-
* );
|
|
18
|
-
* if (isSubagentRequest(result)) {
|
|
19
|
-
* // Phase 4 orchestrator handles this
|
|
20
|
-
* }
|
|
21
|
-
* ```
|
|
22
|
-
*/
|
|
23
|
-
import type { Tool } from '../interface.js';
|
|
24
|
-
import type { ToolResult } from '../types.js';
|
|
25
|
-
/**
|
|
26
|
-
* Check whether a ToolResult represents a pending subagent invocation.
|
|
27
|
-
*
|
|
28
|
-
* Used by the orchestrator (Phase 4) to detect subagent requests in
|
|
29
|
-
* tool execution results and route them to the appropriate agent.
|
|
30
|
-
*
|
|
31
|
-
* @param result - A ToolResult to inspect
|
|
32
|
-
* @returns true if the result contains a pending subagent request
|
|
33
|
-
*/
|
|
34
|
-
export declare function isSubagentRequest(result: ToolResult): boolean;
|
|
35
|
-
/**
|
|
36
|
-
* Subagent tool implementation.
|
|
37
|
-
*
|
|
38
|
-
* **Stub for Phase 3** — validates input and returns a pending subagent request.
|
|
39
|
-
* The Phase 4 orchestrator will call this tool, detect the pending result via
|
|
40
|
-
* `isSubagentRequest()`, and execute the actual agent conversation.
|
|
41
|
-
*/
|
|
42
|
-
export declare const subagentTool: Tool;
|
|
43
|
-
//# sourceMappingURL=subagent.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"subagent.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/subagent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAAgC,UAAU,EAAE,MAAM,aAAa,CAAC;AA+B5E;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAQ7D;AAED;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,EAAE,IAsC1B,CAAC"}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Subagent Tool
|
|
3
|
-
*
|
|
4
|
-
* Spawns a new agent conversation by preparing a subagent invocation request.
|
|
5
|
-
*
|
|
6
|
-
* **This is intentionally a stub/placeholder.** The actual agent conversation
|
|
7
|
-
* loop requires the orchestrator (Phase 4). For now, `execute` validates input
|
|
8
|
-
* and returns a ToolResult with `status: 'pending'` metadata. The orchestrator
|
|
9
|
-
* will inspect tool results using `isSubagentRequest()` and handle the real
|
|
10
|
-
* invocation.
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```typescript
|
|
14
|
-
* const result = await subagentTool.execute(
|
|
15
|
-
* { agentName: 'developer', prompt: 'Implement the login page', description: 'Login UI' },
|
|
16
|
-
* context,
|
|
17
|
-
* );
|
|
18
|
-
* if (isSubagentRequest(result)) {
|
|
19
|
-
* // Phase 4 orchestrator handles this
|
|
20
|
-
* }
|
|
21
|
-
* ```
|
|
22
|
-
*/
|
|
23
|
-
import { ToolError } from '../types.js';
|
|
24
|
-
/** Input schema for the subagent tool */
|
|
25
|
-
const inputSchema = {
|
|
26
|
-
type: 'object',
|
|
27
|
-
properties: {
|
|
28
|
-
agentName: {
|
|
29
|
-
type: 'string',
|
|
30
|
-
description: 'Name of the agent to invoke (e.g., "developer", "tester")',
|
|
31
|
-
},
|
|
32
|
-
prompt: {
|
|
33
|
-
type: 'string',
|
|
34
|
-
description: 'The task prompt to send to the subagent',
|
|
35
|
-
},
|
|
36
|
-
description: {
|
|
37
|
-
type: 'string',
|
|
38
|
-
description: 'Short description of the task (optional)',
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
required: ['agentName', 'prompt'],
|
|
42
|
-
};
|
|
43
|
-
/**
|
|
44
|
-
* Check whether a ToolResult represents a pending subagent invocation.
|
|
45
|
-
*
|
|
46
|
-
* Used by the orchestrator (Phase 4) to detect subagent requests in
|
|
47
|
-
* tool execution results and route them to the appropriate agent.
|
|
48
|
-
*
|
|
49
|
-
* @param result - A ToolResult to inspect
|
|
50
|
-
* @returns true if the result contains a pending subagent request
|
|
51
|
-
*/
|
|
52
|
-
export function isSubagentRequest(result) {
|
|
53
|
-
return (result.success === true &&
|
|
54
|
-
result.metadata !== undefined &&
|
|
55
|
-
result.metadata.status === 'pending' &&
|
|
56
|
-
typeof result.metadata.agentName === 'string' &&
|
|
57
|
-
typeof result.metadata.prompt === 'string');
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Subagent tool implementation.
|
|
61
|
-
*
|
|
62
|
-
* **Stub for Phase 3** — validates input and returns a pending subagent request.
|
|
63
|
-
* The Phase 4 orchestrator will call this tool, detect the pending result via
|
|
64
|
-
* `isSubagentRequest()`, and execute the actual agent conversation.
|
|
65
|
-
*/
|
|
66
|
-
export const subagentTool = {
|
|
67
|
-
name: 'subagent',
|
|
68
|
-
description: 'Spawn a subagent to perform a task. Returns a pending request for the orchestrator.',
|
|
69
|
-
inputSchema,
|
|
70
|
-
async execute(input, context) {
|
|
71
|
-
// Check permission — subagent calls use LLM API (network)
|
|
72
|
-
if (!context.permissions.allowNetwork) {
|
|
73
|
-
throw new ToolError('Network permission denied', 'PERMISSION_DENIED', 'subagent');
|
|
74
|
-
}
|
|
75
|
-
// Validate agentName
|
|
76
|
-
const agentName = input.agentName;
|
|
77
|
-
if (typeof agentName !== 'string' || agentName.trim().length === 0) {
|
|
78
|
-
throw new ToolError('agentName is required and must be a non-empty string', 'INVALID_INPUT', 'subagent');
|
|
79
|
-
}
|
|
80
|
-
// Validate prompt
|
|
81
|
-
const prompt = input.prompt;
|
|
82
|
-
if (typeof prompt !== 'string' || prompt.trim().length === 0) {
|
|
83
|
-
throw new ToolError('prompt is required and must be a non-empty string', 'INVALID_INPUT', 'subagent');
|
|
84
|
-
}
|
|
85
|
-
const description = typeof input.description === 'string' ? input.description : undefined;
|
|
86
|
-
const metadata = {
|
|
87
|
-
agentName: agentName.trim(),
|
|
88
|
-
prompt: prompt.trim(),
|
|
89
|
-
description,
|
|
90
|
-
status: 'pending',
|
|
91
|
-
};
|
|
92
|
-
return {
|
|
93
|
-
success: true,
|
|
94
|
-
output: `Subagent request prepared: ${agentName} — ${description ?? 'no description'}`,
|
|
95
|
-
metadata,
|
|
96
|
-
};
|
|
97
|
-
},
|
|
98
|
-
};
|
|
99
|
-
//# sourceMappingURL=subagent.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"subagent.js","sourceRoot":"","sources":["../../../src/tools/cli/subagent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAUxC,yCAAyC;AACzC,MAAM,WAAW,GAAoB;IACnC,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE;QACV,SAAS,EAAE;YACT,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,2DAA2D;SACzE;QACD,MAAM,EAAE;YACN,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,yCAAyC;SACvD;QACD,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,0CAA0C;SACxD;KACF;IACD,QAAQ,EAAE,CAAC,WAAW,EAAE,QAAQ,CAAC;CAClC,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAkB;IAClD,OAAO,CACL,MAAM,CAAC,OAAO,KAAK,IAAI;QACvB,MAAM,CAAC,QAAQ,KAAK,SAAS;QAC7B,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,SAAS;QACpC,OAAO,MAAM,CAAC,QAAQ,CAAC,SAAS,KAAK,QAAQ;QAC7C,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAC3C,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,YAAY,GAAS;IAChC,IAAI,EAAE,UAAU;IAChB,WAAW,EAAE,qFAAqF;IAClG,WAAW;IAEX,KAAK,CAAC,OAAO,CAAC,KAA8B,EAAE,OAAoB;QAChE,0DAA0D;QAC1D,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,YAAY,EAAE,CAAC;YACtC,MAAM,IAAI,SAAS,CAAC,2BAA2B,EAAE,mBAAmB,EAAE,UAAU,CAAC,CAAC;QACpF,CAAC;QAED,qBAAqB;QACrB,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;QAClC,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACnE,MAAM,IAAI,SAAS,CAAC,sDAAsD,EAAE,eAAe,EAAE,UAAU,CAAC,CAAC;QAC3G,CAAC;QAED,kBAAkB;QAClB,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC5B,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7D,MAAM,IAAI,SAAS,CAAC,mDAAmD,EAAE,eAAe,EAAE,UAAU,CAAC,CAAC;QACxG,CAAC;QAED,MAAM,WAAW,GAAG,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;QAE1F,MAAM,QAAQ,GAA4B;YACxC,SAAS,EAAE,SAAS,CAAC,IAAI,EAAE;YAC3B,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE;YACrB,WAAW;YACX,MAAM,EAAE,SAAS;SACS,CAAC;QAE7B,OAAO;YACL,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,8BAA8B,SAAS,MAAM,WAAW,IAAI,gBAAgB,EAAE;YACtF,QAAQ;SACT,CAAC;IACJ,CAAC;CACF,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"subagent.test.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/subagent.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|