@travisennis/acai 0.0.12 → 0.0.14
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/README.md +5 -2
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +91 -5
- package/dist/commands/history/types.d.ts +3 -2
- package/dist/commands/history/types.d.ts.map +1 -1
- package/dist/commands/init-project/utils.d.ts +0 -1
- package/dist/commands/init-project/utils.d.ts.map +1 -1
- package/dist/commands/init-project/utils.js +1 -1
- package/dist/commands/manager.d.ts.map +1 -1
- package/dist/commands/manager.js +0 -18
- package/dist/commands/share/html-renderer.d.ts.map +1 -1
- package/dist/commands/share/html-renderer.js +54 -48
- package/dist/commands/tools/index.js +39 -38
- package/dist/config/index.d.ts +0 -2
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +3 -7
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +94 -76
- package/dist/models/manager.d.ts +1 -10
- package/dist/models/manager.d.ts.map +1 -1
- package/dist/models/manager.js +2 -2
- package/dist/models/opencode-go-provider.d.ts +6 -4
- package/dist/models/opencode-go-provider.d.ts.map +1 -1
- package/dist/models/opencode-go-provider.js +55 -35
- package/dist/models/providers.d.ts +1 -1
- package/dist/models/providers.d.ts.map +1 -1
- package/dist/prompts/system-prompt.d.ts +0 -2
- package/dist/prompts/system-prompt.d.ts.map +1 -1
- package/dist/prompts/system-prompt.js +6 -10
- package/dist/repl/index.d.ts +27 -11
- package/dist/repl/index.d.ts.map +1 -1
- package/dist/repl/index.js +238 -254
- package/dist/sessions/manager.d.ts +41 -0
- package/dist/sessions/manager.d.ts.map +1 -1
- package/dist/sessions/manager.js +53 -0
- package/dist/sessions/summary.d.ts.map +1 -1
- package/dist/sessions/summary.js +18 -0
- package/dist/skills/index.d.ts +12 -2
- package/dist/skills/index.d.ts.map +1 -1
- package/dist/skills/index.js +123 -73
- package/dist/terminal/control.d.ts +1 -21
- package/dist/terminal/control.d.ts.map +1 -1
- package/dist/terminal/control.js +1 -32
- package/dist/terminal/formatting.d.ts +0 -33
- package/dist/terminal/formatting.d.ts.map +1 -1
- package/dist/terminal/formatting.js +0 -91
- package/dist/terminal/highlight/theme.d.ts +0 -37
- package/dist/terminal/highlight/theme.d.ts.map +1 -1
- package/dist/terminal/highlight/theme.js +1 -79
- package/dist/terminal/keys.d.ts +0 -97
- package/dist/terminal/keys.d.ts.map +1 -1
- package/dist/terminal/keys.js +0 -194
- package/dist/terminal/select-prompt.d.ts.map +1 -1
- package/dist/terminal/select-prompt.js +65 -51
- package/dist/terminal/style.d.ts +1 -1
- package/dist/terminal/style.d.ts.map +1 -1
- package/dist/terminal/supports-color.d.ts.map +1 -1
- package/dist/terminal/supports-color.js +38 -20
- package/dist/terminal/supports-hyperlinks.d.ts +3 -0
- package/dist/terminal/supports-hyperlinks.d.ts.map +1 -1
- package/dist/terminal/supports-hyperlinks.js +72 -31
- package/dist/terminal/table/layout-manager.d.ts +0 -20
- package/dist/terminal/table/layout-manager.d.ts.map +1 -1
- package/dist/terminal/table/layout-manager.js +68 -44
- package/dist/terminal/table/utils.d.ts +0 -1
- package/dist/terminal/table/utils.d.ts.map +1 -1
- package/dist/terminal/table/utils.js +2 -4
- package/dist/tools/apply-patch.d.ts +5 -3
- package/dist/tools/apply-patch.d.ts.map +1 -1
- package/dist/tools/apply-patch.js +154 -123
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +11 -8
- package/dist/tools/dynamic-tool-loader.d.ts +6 -1
- package/dist/tools/dynamic-tool-loader.d.ts.map +1 -1
- package/dist/tools/dynamic-tool-loader.js +154 -135
- package/dist/tools/index.d.ts +3 -133
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +0 -20
- package/dist/tools/read-file.d.ts.map +1 -1
- package/dist/tools/read-file.js +32 -13
- package/dist/tools/skill.d.ts.map +1 -1
- package/dist/tools/skill.js +20 -13
- package/dist/tools/web-fetch.d.ts +2 -6
- package/dist/tools/web-fetch.d.ts.map +1 -1
- package/dist/tools/web-fetch.js +59 -36
- package/dist/tools/web-search.d.ts +0 -4
- package/dist/tools/web-search.d.ts.map +1 -1
- package/dist/tools/web-search.js +40 -32
- package/dist/tui/autocomplete/utils.d.ts +0 -15
- package/dist/tui/autocomplete/utils.d.ts.map +1 -1
- package/dist/tui/autocomplete/utils.js +0 -85
- package/dist/tui/autocomplete.d.ts +1 -1
- package/dist/tui/autocomplete.d.ts.map +1 -1
- package/dist/tui/autocomplete.js +1 -1
- package/dist/tui/components/editor.d.ts +12 -0
- package/dist/tui/components/editor.d.ts.map +1 -1
- package/dist/tui/components/editor.js +181 -195
- package/dist/tui/components/input.d.ts +4 -0
- package/dist/tui/components/input.d.ts.map +1 -1
- package/dist/tui/components/input.js +33 -31
- package/dist/tui/components/markdown.d.ts +30 -0
- package/dist/tui/components/markdown.d.ts.map +1 -1
- package/dist/tui/components/markdown.js +274 -242
- package/dist/tui/components/modal.d.ts.map +1 -1
- package/dist/tui/components/modal.js +11 -11
- package/dist/tui/components/select-list.d.ts +5 -0
- package/dist/tui/components/select-list.d.ts.map +1 -1
- package/dist/tui/components/select-list.js +69 -98
- package/dist/tui/components/table.d.ts +6 -0
- package/dist/tui/components/table.d.ts.map +1 -1
- package/dist/tui/components/table.js +61 -52
- package/dist/tui/index.d.ts +1 -8
- package/dist/tui/index.d.ts.map +1 -1
- package/dist/tui/index.js +0 -8
- package/dist/tui/tui.d.ts +9 -0
- package/dist/tui/tui.d.ts.map +1 -1
- package/dist/tui/tui.js +78 -48
- package/dist/tui/utils.d.ts +7 -0
- package/dist/tui/utils.d.ts.map +1 -1
- package/dist/tui/utils.js +117 -53
- package/dist/utils/bash.d.ts.map +1 -1
- package/dist/utils/bash.js +156 -104
- package/dist/utils/command-protection.d.ts +3 -6
- package/dist/utils/command-protection.d.ts.map +1 -1
- package/dist/utils/command-protection.js +213 -100
- package/dist/utils/filesystem/operations.d.ts +0 -15
- package/dist/utils/filesystem/operations.d.ts.map +1 -1
- package/dist/utils/filesystem/operations.js +1 -38
- package/dist/utils/filesystem/security.d.ts.map +1 -1
- package/dist/utils/filesystem/security.js +22 -15
- package/dist/utils/filetype-detection.d.ts +0 -1
- package/dist/utils/filetype-detection.d.ts.map +1 -1
- package/dist/utils/filetype-detection.js +0 -12
- package/dist/utils/formatting.d.ts +0 -30
- package/dist/utils/formatting.d.ts.map +1 -1
- package/dist/utils/formatting.js +0 -44
- package/dist/utils/git.d.ts +4 -6
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js +36 -46
- package/dist/utils/ignore.d.ts +1 -1
- package/dist/utils/ignore.d.ts.map +1 -1
- package/dist/utils/ignore.js +1 -1
- package/dist/utils/process.d.ts.map +1 -1
- package/dist/utils/process.js +77 -79
- package/dist/utils/yaml.d.ts +0 -1
- package/dist/utils/yaml.d.ts.map +1 -1
- package/dist/utils/yaml.js +80 -89
- package/dist/utils/zod.d.ts +0 -3
- package/dist/utils/zod.d.ts.map +1 -1
- package/dist/utils/zod.js +0 -7
- package/package.json +25 -24
- package/dist/agent/sub-agent.d.ts +0 -23
- package/dist/agent/sub-agent.d.ts.map +0 -1
- package/dist/agent/sub-agent.js +0 -109
- package/dist/commands/add-directory/index.d.ts +0 -3
- package/dist/commands/add-directory/index.d.ts.map +0 -1
- package/dist/commands/add-directory/index.js +0 -50
- package/dist/commands/add-directory/utils.d.ts +0 -3
- package/dist/commands/add-directory/utils.d.ts.map +0 -1
- package/dist/commands/add-directory/utils.js +0 -15
- package/dist/commands/clear/index.d.ts +0 -3
- package/dist/commands/clear/index.d.ts.map +0 -1
- package/dist/commands/clear/index.js +0 -13
- package/dist/commands/generate-rules/index.d.ts +0 -3
- package/dist/commands/generate-rules/index.d.ts.map +0 -1
- package/dist/commands/generate-rules/index.js +0 -206
- package/dist/commands/generate-rules/service.d.ts +0 -22
- package/dist/commands/generate-rules/service.d.ts.map +0 -1
- package/dist/commands/generate-rules/service.js +0 -103
- package/dist/commands/generate-rules/utils.d.ts +0 -5
- package/dist/commands/generate-rules/utils.d.ts.map +0 -1
- package/dist/commands/generate-rules/utils.js +0 -25
- package/dist/commands/handoff/index.d.ts +0 -3
- package/dist/commands/handoff/index.d.ts.map +0 -1
- package/dist/commands/handoff/index.js +0 -97
- package/dist/commands/handoff/utils.d.ts +0 -4
- package/dist/commands/handoff/utils.d.ts.map +0 -1
- package/dist/commands/handoff/utils.js +0 -123
- package/dist/commands/list-directories/index.d.ts +0 -3
- package/dist/commands/list-directories/index.d.ts.map +0 -1
- package/dist/commands/list-directories/index.js +0 -35
- package/dist/commands/pickup/index.d.ts +0 -3
- package/dist/commands/pickup/index.d.ts.map +0 -1
- package/dist/commands/pickup/index.js +0 -141
- package/dist/commands/pickup/types.d.ts +0 -6
- package/dist/commands/pickup/types.d.ts.map +0 -1
- package/dist/commands/pickup/types.js +0 -1
- package/dist/commands/pickup/utils.d.ts +0 -7
- package/dist/commands/pickup/utils.d.ts.map +0 -1
- package/dist/commands/pickup/utils.js +0 -56
- package/dist/commands/remove-directory/index.d.ts +0 -3
- package/dist/commands/remove-directory/index.d.ts.map +0 -1
- package/dist/commands/remove-directory/index.js +0 -55
- package/dist/commands/review/index.d.ts +0 -3
- package/dist/commands/review/index.d.ts.map +0 -1
- package/dist/commands/review/index.js +0 -12
- package/dist/commands/review/review-panel.d.ts +0 -3
- package/dist/commands/review/review-panel.d.ts.map +0 -1
- package/dist/commands/review/review-panel.js +0 -186
- package/dist/commands/review/utils.d.ts +0 -18
- package/dist/commands/review/utils.d.ts.map +0 -1
- package/dist/commands/review/utils.js +0 -146
- package/dist/commands/shell/index.d.ts +0 -3
- package/dist/commands/shell/index.d.ts.map +0 -1
- package/dist/commands/shell/index.js +0 -96
- package/dist/subagents/index.d.ts +0 -16
- package/dist/subagents/index.d.ts.map +0 -1
- package/dist/subagents/index.js +0 -231
- package/dist/terminal/index.d.ts +0 -9
- package/dist/terminal/index.d.ts.map +0 -1
- package/dist/terminal/index.js +0 -8
- package/dist/tools/agent.d.ts +0 -27
- package/dist/tools/agent.d.ts.map +0 -1
- package/dist/tools/agent.js +0 -81
- package/dist/tools/directory-tree.d.ts +0 -28
- package/dist/tools/directory-tree.d.ts.map +0 -1
- package/dist/tools/directory-tree.js +0 -154
- package/dist/tools/glob.d.ts +0 -35
- package/dist/tools/glob.d.ts.map +0 -1
- package/dist/tools/glob.js +0 -167
- package/dist/tools/grep.d.ts +0 -100
- package/dist/tools/grep.d.ts.map +0 -1
- package/dist/tools/grep.js +0 -608
- package/dist/tools/ls.d.ts +0 -26
- package/dist/tools/ls.d.ts.map +0 -1
- package/dist/tools/ls.js +0 -83
- package/dist/tui/components/header.d.ts +0 -21
- package/dist/tui/components/header.d.ts.map +0 -1
- package/dist/tui/components/header.js +0 -63
- package/dist/utils/bash/parse.d.ts +0 -19
- package/dist/utils/bash/parse.d.ts.map +0 -1
- package/dist/utils/bash/parse.js +0 -223
- package/dist/utils/bash/quote.d.ts +0 -6
- package/dist/utils/bash/quote.d.ts.map +0 -1
- package/dist/utils/bash/quote.js +0 -23
- package/dist/utils/generators.d.ts +0 -3
- package/dist/utils/generators.d.ts.map +0 -1
- package/dist/utils/generators.js +0 -25
- package/dist/utils/glob.d.ts +0 -52
- package/dist/utils/glob.d.ts.map +0 -1
- package/dist/utils/glob.js +0 -376
package/dist/tools/grep.js
DELETED
|
@@ -1,608 +0,0 @@
|
|
|
1
|
-
import { execFile, } from "node:child_process";
|
|
2
|
-
import { inspect } from "node:util";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import style from "../terminal/style.js";
|
|
5
|
-
import { toDisplayPath } from "../utils/filesystem/path-display.js";
|
|
6
|
-
import { convertNullString } from "../utils/zod.js";
|
|
7
|
-
// default limit
|
|
8
|
-
const DEFAULT_MAX_RESULTS = 100;
|
|
9
|
-
export const GrepTool = {
|
|
10
|
-
name: "Grep",
|
|
11
|
-
};
|
|
12
|
-
const inputSchema = z.object({
|
|
13
|
-
pattern: z
|
|
14
|
-
.string()
|
|
15
|
-
.describe("The search pattern (regex by default, or fixed-string if literal=true or auto-detected as unbalanced)"),
|
|
16
|
-
path: z.string().describe("The path to search in"),
|
|
17
|
-
recursive: z
|
|
18
|
-
.preprocess((val) => convertNullString(val), z.coerce.boolean().nullable())
|
|
19
|
-
.default(null)
|
|
20
|
-
.describe("Search recursively. (default: true))"),
|
|
21
|
-
ignoreCase: z
|
|
22
|
-
.preprocess((val) => convertNullString(val), z.coerce.boolean().nullable())
|
|
23
|
-
.default(null)
|
|
24
|
-
.describe("Use case-sensitive search. (default: false)"),
|
|
25
|
-
filePattern: z
|
|
26
|
-
.preprocess((val) => convertNullString(val), z.coerce.string().nullable())
|
|
27
|
-
.default(null)
|
|
28
|
-
.describe("Glob pattern to filter files (e.g., '*.ts', '**/*.test.js'). (Default: no filtering)"),
|
|
29
|
-
contextLines: z
|
|
30
|
-
.preprocess((val) => convertNullString(val), z.coerce.number().nullable())
|
|
31
|
-
.default(null)
|
|
32
|
-
.describe("The number of context lines needed in search results. (Default: 0)"),
|
|
33
|
-
searchIgnored: z
|
|
34
|
-
.preprocess((val) => convertNullString(val), z.coerce.boolean().nullable())
|
|
35
|
-
.default(null)
|
|
36
|
-
.describe("Search ignored files. (Default: false)"),
|
|
37
|
-
literal: z
|
|
38
|
-
.preprocess((val) => convertNullString(val), z.coerce.boolean().nullable())
|
|
39
|
-
.default(null)
|
|
40
|
-
.describe("Pass true for fixed-string search (-F), false for regex, (Default: auto-detects unbalanced patterns like mismatched parentheses/brackets.)"),
|
|
41
|
-
maxResults: z
|
|
42
|
-
.preprocess((val) => convertNullString(val), z.coerce.number().nullable())
|
|
43
|
-
.default(null)
|
|
44
|
-
.describe("Maximum number of matches to return. Set to 0 for no limit. (Default: 100)"),
|
|
45
|
-
});
|
|
46
|
-
export const createGrepTool = () => {
|
|
47
|
-
return {
|
|
48
|
-
toolDef: {
|
|
49
|
-
description: "Search file contents using ripgrep.",
|
|
50
|
-
inputSchema,
|
|
51
|
-
},
|
|
52
|
-
display({ pattern, path, filePattern, recursive, ignoreCase, contextLines, }) {
|
|
53
|
-
// grok doesn't follow my instructions
|
|
54
|
-
const safeFilePattern = filePattern === "null" || filePattern === "undefined"
|
|
55
|
-
? null
|
|
56
|
-
: filePattern;
|
|
57
|
-
// Enhanced tool-init with detailed search parameters
|
|
58
|
-
const displayPath = toDisplayPath(path);
|
|
59
|
-
let initMessage = `${style.cyan(inspect(pattern))} in ${style.cyan(displayPath)}`;
|
|
60
|
-
if (safeFilePattern) {
|
|
61
|
-
initMessage += ` ${style.dim(`(filter: ${safeFilePattern})`)}`;
|
|
62
|
-
}
|
|
63
|
-
if (recursive === false) {
|
|
64
|
-
initMessage += ` ${style.dim("(non-recursive)")}`;
|
|
65
|
-
}
|
|
66
|
-
if (ignoreCase) {
|
|
67
|
-
initMessage += ` ${style.dim("(case-insensitive)")}`;
|
|
68
|
-
}
|
|
69
|
-
if (contextLines && contextLines > 0) {
|
|
70
|
-
initMessage += ` ${style.dim(`(with ${contextLines} context line${contextLines === 1 ? "" : "s"})`)}`;
|
|
71
|
-
}
|
|
72
|
-
return initMessage;
|
|
73
|
-
},
|
|
74
|
-
async execute({ pattern, path, recursive, ignoreCase, filePattern, contextLines, searchIgnored, literal, maxResults, }, { abortSignal }) {
|
|
75
|
-
if (abortSignal?.aborted) {
|
|
76
|
-
throw new Error("Grep search aborted");
|
|
77
|
-
}
|
|
78
|
-
// Validate path - default to cwd if not provided
|
|
79
|
-
const effectivePath = typeof path === "string" && path.trim() !== "" ? path : process.cwd();
|
|
80
|
-
try {
|
|
81
|
-
// Compute likelyUnbalancedRegex once and pass through
|
|
82
|
-
const isLikelyUnbalanced = likelyUnbalancedRegex(pattern);
|
|
83
|
-
let effectiveLiteral = null;
|
|
84
|
-
if (literal === true) {
|
|
85
|
-
effectiveLiteral = true;
|
|
86
|
-
}
|
|
87
|
-
else if (literal === false) {
|
|
88
|
-
effectiveLiteral = false;
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
effectiveLiteral = isLikelyUnbalanced;
|
|
92
|
-
}
|
|
93
|
-
const effectiveMaxResults = maxResults ?? DEFAULT_MAX_RESULTS;
|
|
94
|
-
const safeFilePattern = filePattern === "null" || filePattern === "undefined"
|
|
95
|
-
? null
|
|
96
|
-
: filePattern;
|
|
97
|
-
const grepResult = await grepFilesStructured(pattern, effectivePath, {
|
|
98
|
-
recursive,
|
|
99
|
-
ignoreCase,
|
|
100
|
-
filePattern: safeFilePattern,
|
|
101
|
-
contextLines,
|
|
102
|
-
searchIgnored,
|
|
103
|
-
literal: effectiveLiteral,
|
|
104
|
-
maxResults: effectiveMaxResults,
|
|
105
|
-
likelyUnbalanced: isLikelyUnbalanced,
|
|
106
|
-
}, abortSignal);
|
|
107
|
-
return grepResult.rawOutput;
|
|
108
|
-
}
|
|
109
|
-
catch (error) {
|
|
110
|
-
const errorMessage = error.message;
|
|
111
|
-
let userFriendlyError = `Error searching for "${pattern}" in ${effectivePath}: ${errorMessage}`;
|
|
112
|
-
if (errorMessage.includes("No such file or directory")) {
|
|
113
|
-
userFriendlyError = `Path not found: "${effectivePath}"`;
|
|
114
|
-
if (filePattern) {
|
|
115
|
-
userFriendlyError += ` with file pattern "${filePattern}"`;
|
|
116
|
-
}
|
|
117
|
-
userFriendlyError += " - check if the path exists and is accessible";
|
|
118
|
-
}
|
|
119
|
-
else if (errorMessage.includes("permission denied")) {
|
|
120
|
-
userFriendlyError = `Permission denied accessing "${effectivePath}"`;
|
|
121
|
-
}
|
|
122
|
-
else if (errorMessage.includes("Regex parse error")) {
|
|
123
|
-
userFriendlyError = `Invalid search pattern "${pattern}" - try using literal=true for fixed-string search`;
|
|
124
|
-
}
|
|
125
|
-
throw new Error(userFriendlyError);
|
|
126
|
-
}
|
|
127
|
-
},
|
|
128
|
-
};
|
|
129
|
-
};
|
|
130
|
-
/**
|
|
131
|
-
* Skips past a character class [...], handling escaped characters.
|
|
132
|
-
* Returns the new index after the character class.
|
|
133
|
-
*/
|
|
134
|
-
function skipCharacterClass(pattern, startIndex) {
|
|
135
|
-
let i = startIndex + 1;
|
|
136
|
-
while (i < pattern.length && pattern[i] !== "]") {
|
|
137
|
-
if (pattern[i] === "\\")
|
|
138
|
-
i++;
|
|
139
|
-
i++;
|
|
140
|
-
}
|
|
141
|
-
return i;
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Checks if a character is valid inside braces (digits 0-9 or comma).
|
|
145
|
-
*/
|
|
146
|
-
function isValidBraceChar(c) {
|
|
147
|
-
return (c >= "0" && c <= "9") || c === ",";
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Checks if empty braces have a preceding atom.
|
|
151
|
-
* Empty braces with no preceding atom are treated as literal.
|
|
152
|
-
*/
|
|
153
|
-
function hasPrecedingAtom(pattern, startIndex) {
|
|
154
|
-
if (startIndex === 0)
|
|
155
|
-
return false;
|
|
156
|
-
const prev = pattern[startIndex - 1];
|
|
157
|
-
return /\S/.test(prev);
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Parses and validates the content inside braces {..}.
|
|
161
|
-
* Returns true if the brace content is invalid.
|
|
162
|
-
*/
|
|
163
|
-
function isInvalidBraceContent(pattern, startIndex) {
|
|
164
|
-
const j = startIndex + 1;
|
|
165
|
-
// Find closing brace and check for invalid characters
|
|
166
|
-
let hasDigits = false;
|
|
167
|
-
let k = j;
|
|
168
|
-
while (k < pattern.length && pattern[k] !== "}") {
|
|
169
|
-
if (!isValidBraceChar(pattern[k])) {
|
|
170
|
-
return true;
|
|
171
|
-
}
|
|
172
|
-
if (pattern[k] >= "0" && pattern[k] <= "9") {
|
|
173
|
-
hasDigits = true;
|
|
174
|
-
}
|
|
175
|
-
k++;
|
|
176
|
-
}
|
|
177
|
-
// No closing brace found
|
|
178
|
-
if (k >= pattern.length) {
|
|
179
|
-
return true;
|
|
180
|
-
}
|
|
181
|
-
// Empty braces {} with preceding atom are invalid
|
|
182
|
-
if (!hasDigits && hasPrecedingAtom(pattern, startIndex)) {
|
|
183
|
-
return true;
|
|
184
|
-
}
|
|
185
|
-
return false;
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Check for invalid repetition operators (e.g., {n}, {n,}, {n,m}) outside of character classes.
|
|
189
|
-
* Returns true if any invalid repetition operators are found.
|
|
190
|
-
*/
|
|
191
|
-
function hasInvalidRepetition(pattern) {
|
|
192
|
-
for (let i = 0; i < pattern.length; i++) {
|
|
193
|
-
const ch = pattern[i];
|
|
194
|
-
if (ch === "\\") {
|
|
195
|
-
i++; // Skip the next character (escaped)
|
|
196
|
-
continue;
|
|
197
|
-
}
|
|
198
|
-
if (ch === "[") {
|
|
199
|
-
i = skipCharacterClass(pattern, i);
|
|
200
|
-
continue;
|
|
201
|
-
}
|
|
202
|
-
// Unmatched closing brace is invalid
|
|
203
|
-
if (ch === "}") {
|
|
204
|
-
return true;
|
|
205
|
-
}
|
|
206
|
-
// Handle opening brace
|
|
207
|
-
if (ch === "{") {
|
|
208
|
-
if (isInvalidBraceContent(pattern, i)) {
|
|
209
|
-
return true;
|
|
210
|
-
}
|
|
211
|
-
i = findClosingBrace(pattern, i);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
return false;
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* Finds the index of the closing brace matching an opening brace.
|
|
218
|
-
* Returns the index of the closing brace, or the last index of pattern if not found.
|
|
219
|
-
*/
|
|
220
|
-
function findClosingBrace(pattern, startIndex) {
|
|
221
|
-
let j = startIndex + 1;
|
|
222
|
-
while (j < pattern.length && pattern[j] !== "}") {
|
|
223
|
-
j++;
|
|
224
|
-
}
|
|
225
|
-
return j;
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* Count bracket/paren/brace pairs in a regex pattern, excluding character classes.
|
|
229
|
-
*/
|
|
230
|
-
function countBrackets(pattern) {
|
|
231
|
-
const counts = {
|
|
232
|
-
openParen: 0,
|
|
233
|
-
closeParen: 0,
|
|
234
|
-
openBracket: 0,
|
|
235
|
-
closeBracket: 0,
|
|
236
|
-
openBrace: 0,
|
|
237
|
-
closeBrace: 0,
|
|
238
|
-
};
|
|
239
|
-
let escaped = false;
|
|
240
|
-
let inCharacterClass = false;
|
|
241
|
-
for (let i = 0; i < pattern.length; i++) {
|
|
242
|
-
const ch = pattern[i];
|
|
243
|
-
if (escaped) {
|
|
244
|
-
escaped = false;
|
|
245
|
-
continue;
|
|
246
|
-
}
|
|
247
|
-
if (ch === "\\") {
|
|
248
|
-
escaped = true;
|
|
249
|
-
continue;
|
|
250
|
-
}
|
|
251
|
-
// Track character class boundaries
|
|
252
|
-
if (ch === "[" && !inCharacterClass) {
|
|
253
|
-
inCharacterClass = true;
|
|
254
|
-
counts.openBracket++;
|
|
255
|
-
continue;
|
|
256
|
-
}
|
|
257
|
-
if (ch === "]" && inCharacterClass) {
|
|
258
|
-
inCharacterClass = false;
|
|
259
|
-
counts.closeBracket++;
|
|
260
|
-
continue;
|
|
261
|
-
}
|
|
262
|
-
// Only count brackets/parens/braces outside of character classes
|
|
263
|
-
if (!inCharacterClass) {
|
|
264
|
-
switch (ch) {
|
|
265
|
-
case "(":
|
|
266
|
-
counts.openParen++;
|
|
267
|
-
break;
|
|
268
|
-
case ")":
|
|
269
|
-
counts.closeParen++;
|
|
270
|
-
break;
|
|
271
|
-
case "{":
|
|
272
|
-
counts.openBrace++;
|
|
273
|
-
break;
|
|
274
|
-
case "}":
|
|
275
|
-
counts.closeBrace++;
|
|
276
|
-
break;
|
|
277
|
-
default:
|
|
278
|
-
break;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
return counts;
|
|
283
|
-
}
|
|
284
|
-
/**
|
|
285
|
-
* Check if a pattern contains regex metacharacters.
|
|
286
|
-
* Returns true if the pattern likely needs regex mode.
|
|
287
|
-
*/
|
|
288
|
-
function containsRegexMetacharacters(pattern) {
|
|
289
|
-
// Regex metacharacters: ^ $ . * + ? [ ] ( ) { } | \
|
|
290
|
-
const metacharacterPattern = /[\^$.*+?[\](){}|\\]/;
|
|
291
|
-
return metacharacterPattern.test(pattern);
|
|
292
|
-
}
|
|
293
|
-
export function likelyUnbalancedRegex(pattern) {
|
|
294
|
-
// First check: unbalanced brackets/parentheses/braces = likely a typo, use literal
|
|
295
|
-
const counts = countBrackets(pattern);
|
|
296
|
-
// Check for unbalanced brackets, parentheses, and braces
|
|
297
|
-
const hasUnbalancedBrackets = counts.openBracket !== counts.closeBracket;
|
|
298
|
-
const hasUnbalancedParens = counts.openParen !== counts.closeParen;
|
|
299
|
-
const hasUnbalancedBraces = counts.openBrace !== counts.closeBrace;
|
|
300
|
-
// Also check for invalid repetition operators
|
|
301
|
-
const hasInvalidRepetitionFlag = hasInvalidRepetition(pattern);
|
|
302
|
-
// If pattern has unbalanced syntax, treat as literal (user probably meant to type literal)
|
|
303
|
-
if (hasUnbalancedBrackets ||
|
|
304
|
-
hasUnbalancedParens ||
|
|
305
|
-
hasUnbalancedBraces ||
|
|
306
|
-
hasInvalidRepetitionFlag) {
|
|
307
|
-
return true;
|
|
308
|
-
}
|
|
309
|
-
// Second check: if pattern has regex metacharacters, treat as regex (return false)
|
|
310
|
-
// This allows ripgrep to handle the pattern as a proper regex
|
|
311
|
-
if (containsRegexMetacharacters(pattern)) {
|
|
312
|
-
return false;
|
|
313
|
-
}
|
|
314
|
-
// Default: simple alphanumeric strings should use literal mode
|
|
315
|
-
return true;
|
|
316
|
-
}
|
|
317
|
-
/**
|
|
318
|
-
* Build grep command args array directly
|
|
319
|
-
*
|
|
320
|
-
* @param pattern - The regex pattern to search for
|
|
321
|
-
* @param path - The path to search in
|
|
322
|
-
* @param options - Additional options for the grep command
|
|
323
|
-
* @returns The args array for the grep command
|
|
324
|
-
*/
|
|
325
|
-
export function buildGrepArgs(pattern, path, options = {}) {
|
|
326
|
-
const effectiveRecursive = options.recursive === null ? true : options.recursive;
|
|
327
|
-
const effectiveIgnoreCase = options.ignoreCase === null ? false : options.ignoreCase;
|
|
328
|
-
const effectiveSearchIgnored = options.searchIgnored === null ? false : options.searchIgnored;
|
|
329
|
-
const effectiveFilePattern = options.filePattern;
|
|
330
|
-
const effectiveContextLines = options.contextLines;
|
|
331
|
-
// Use the pre-computed likelyUnbalanced result if available
|
|
332
|
-
let effectiveLiteral;
|
|
333
|
-
if (options.literal === true) {
|
|
334
|
-
effectiveLiteral = true;
|
|
335
|
-
}
|
|
336
|
-
else if (options.literal === false) {
|
|
337
|
-
effectiveLiteral = false;
|
|
338
|
-
}
|
|
339
|
-
else if (options.likelyUnbalanced !== undefined) {
|
|
340
|
-
effectiveLiteral = options.likelyUnbalanced;
|
|
341
|
-
}
|
|
342
|
-
else {
|
|
343
|
-
effectiveLiteral = likelyUnbalancedRegex(pattern);
|
|
344
|
-
}
|
|
345
|
-
const args = ["--json"];
|
|
346
|
-
if (effectiveRecursive === false) {
|
|
347
|
-
args.push("--max-depth=0");
|
|
348
|
-
}
|
|
349
|
-
if (effectiveIgnoreCase) {
|
|
350
|
-
args.push("--ignore-case");
|
|
351
|
-
}
|
|
352
|
-
if (effectiveContextLines !== null && effectiveContextLines !== undefined) {
|
|
353
|
-
args.push(`--context=${effectiveContextLines}`);
|
|
354
|
-
}
|
|
355
|
-
if (effectiveFilePattern !== null && effectiveFilePattern !== undefined) {
|
|
356
|
-
args.push(`--glob=${effectiveFilePattern}`);
|
|
357
|
-
}
|
|
358
|
-
if (effectiveSearchIgnored) {
|
|
359
|
-
args.push("--no-ignore");
|
|
360
|
-
}
|
|
361
|
-
if (effectiveLiteral) {
|
|
362
|
-
args.push("-F");
|
|
363
|
-
}
|
|
364
|
-
// Use ripgrep's --max-count flag to limit matches per file for efficiency
|
|
365
|
-
// This helps prevent any single file from dominating results
|
|
366
|
-
if (options.maxResults !== null &&
|
|
367
|
-
options.maxResults !== undefined &&
|
|
368
|
-
options.maxResults > 0) {
|
|
369
|
-
// Use a reasonable per-file limit (max 100 per file) to balance efficiency and completeness
|
|
370
|
-
const perFileLimit = Math.min(options.maxResults, 100);
|
|
371
|
-
args.push(`--max-count=${perFileLimit}`);
|
|
372
|
-
}
|
|
373
|
-
args.push(pattern);
|
|
374
|
-
args.push(path);
|
|
375
|
-
return args;
|
|
376
|
-
}
|
|
377
|
-
/**
|
|
378
|
-
* Parse ripgrep JSON output and extract structured match information
|
|
379
|
-
*/
|
|
380
|
-
export function parseRipgrepJsonOutput(content) {
|
|
381
|
-
if (!content || content.trim() === "") {
|
|
382
|
-
return [];
|
|
383
|
-
}
|
|
384
|
-
const parsed = [];
|
|
385
|
-
const lines = content.trim().split("\n");
|
|
386
|
-
for (const line of lines) {
|
|
387
|
-
if (!line.trim()) {
|
|
388
|
-
continue;
|
|
389
|
-
}
|
|
390
|
-
try {
|
|
391
|
-
const rgResult = JSON.parse(line);
|
|
392
|
-
// Handle different ripgrep message types
|
|
393
|
-
if (rgResult.type === "match") {
|
|
394
|
-
const data = rgResult.data;
|
|
395
|
-
parsed.push({
|
|
396
|
-
file: data.path?.text ?? data.path?.bytes?.toString(),
|
|
397
|
-
line: data.line_number ?? 0,
|
|
398
|
-
content: data.lines?.text ?? data.line ?? "",
|
|
399
|
-
isMatch: true,
|
|
400
|
-
lineNumber: data.line_number,
|
|
401
|
-
absolutePath: data.absolute_path?.text ?? data.absolute_path?.bytes?.toString(),
|
|
402
|
-
submatches: data.submatches?.map((sm) => ({
|
|
403
|
-
start: sm.start,
|
|
404
|
-
end: sm.end,
|
|
405
|
-
text: sm.text,
|
|
406
|
-
})),
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
else if (rgResult.type === "context") {
|
|
410
|
-
const data = rgResult.data;
|
|
411
|
-
parsed.push({
|
|
412
|
-
file: data.path?.text ?? data.path?.bytes?.toString(),
|
|
413
|
-
line: data.line_number ?? 0,
|
|
414
|
-
content: data.lines?.text ?? data.line ?? "",
|
|
415
|
-
isMatch: false,
|
|
416
|
-
isContext: true,
|
|
417
|
-
lineNumber: data.line_number,
|
|
418
|
-
absolutePath: data.absolute_path?.text ?? data.absolute_path?.bytes?.toString(),
|
|
419
|
-
});
|
|
420
|
-
}
|
|
421
|
-
// Ignore other message types like "begin", "end", "summary"
|
|
422
|
-
}
|
|
423
|
-
catch { }
|
|
424
|
-
}
|
|
425
|
-
return parsed;
|
|
426
|
-
}
|
|
427
|
-
/**
|
|
428
|
-
* Convert parsed JSON matches back to legacy line-number format for backwards compatibility
|
|
429
|
-
*/
|
|
430
|
-
function matchesToLegacyFormat(matches) {
|
|
431
|
-
const lines = [];
|
|
432
|
-
for (const match of matches) {
|
|
433
|
-
const lineNum = match.lineNumber ?? match.line;
|
|
434
|
-
const file = match.file ?? match.absolutePath;
|
|
435
|
-
if (file) {
|
|
436
|
-
if (match.isMatch) {
|
|
437
|
-
lines.push(`${file}:${lineNum}:${match.content}`);
|
|
438
|
-
}
|
|
439
|
-
else if (match.isContext) {
|
|
440
|
-
lines.push(`${file}-${lineNum}-${match.content}`);
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
else {
|
|
444
|
-
if (match.isMatch) {
|
|
445
|
-
lines.push(`${lineNum}:${match.content}`);
|
|
446
|
-
}
|
|
447
|
-
else if (match.isContext) {
|
|
448
|
-
lines.push(`${lineNum}-${match.content}`);
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
return lines.join("\n");
|
|
453
|
-
}
|
|
454
|
-
/**
|
|
455
|
-
* Count actual matches (excluding context lines)
|
|
456
|
-
*/
|
|
457
|
-
export function countActualMatches(parsed) {
|
|
458
|
-
return parsed.filter((match) => match.isMatch && !match.isContext).length;
|
|
459
|
-
}
|
|
460
|
-
/**
|
|
461
|
-
* Count context lines
|
|
462
|
-
*/
|
|
463
|
-
export function countContextLines(parsed) {
|
|
464
|
-
return parsed.filter((match) => match.isContext).length;
|
|
465
|
-
}
|
|
466
|
-
function isContextNearKeptMatch(matches, index, indicesToKeep, contextWindow) {
|
|
467
|
-
for (let j = index - 1; j >= Math.max(0, index - contextWindow); j--) {
|
|
468
|
-
if (matches[j].isMatch && !matches[j].isContext && indicesToKeep.has(j)) {
|
|
469
|
-
return true;
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
for (let j = index + 1; j < Math.min(matches.length, index + contextWindow + 1); j++) {
|
|
473
|
-
if (matches[j].isMatch && !matches[j].isContext && indicesToKeep.has(j)) {
|
|
474
|
-
return true;
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
return false;
|
|
478
|
-
}
|
|
479
|
-
/**
|
|
480
|
-
* Truncate matches to a maximum number of results, preserving context lines for kept matches
|
|
481
|
-
*/
|
|
482
|
-
export function truncateMatches(matches, maxResults) {
|
|
483
|
-
if (!maxResults || maxResults <= 0) {
|
|
484
|
-
return { truncated: matches, isTruncated: false };
|
|
485
|
-
}
|
|
486
|
-
// Find indices of actual matches (excluding context lines)
|
|
487
|
-
const matchIndices = [];
|
|
488
|
-
for (let i = 0; i < matches.length; i++) {
|
|
489
|
-
if (matches[i].isMatch && !matches[i].isContext) {
|
|
490
|
-
matchIndices.push(i);
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
if (matchIndices.length <= maxResults) {
|
|
494
|
-
return { truncated: matches, isTruncated: false };
|
|
495
|
-
}
|
|
496
|
-
// Get the indices of matches we want to keep
|
|
497
|
-
const indicesToKeep = new Set();
|
|
498
|
-
for (let i = 0; i < maxResults; i++) {
|
|
499
|
-
indicesToKeep.add(matchIndices[i]);
|
|
500
|
-
}
|
|
501
|
-
// Build truncated result: include all kept matches AND their associated context lines
|
|
502
|
-
const truncated = [];
|
|
503
|
-
let matchesKept = 0;
|
|
504
|
-
const contextWindow = 3; // Include up to 3 context lines around each match
|
|
505
|
-
for (let i = 0; i < matches.length; i++) {
|
|
506
|
-
const match = matches[i];
|
|
507
|
-
if (match.isMatch && !match.isContext) {
|
|
508
|
-
// This is an actual match
|
|
509
|
-
if (matchesKept < maxResults) {
|
|
510
|
-
truncated.push(match);
|
|
511
|
-
matchesKept++;
|
|
512
|
-
}
|
|
513
|
-
else {
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
else if (match.isContext) {
|
|
517
|
-
if (isContextNearKeptMatch(matches, i, indicesToKeep, contextWindow)) {
|
|
518
|
-
truncated.push(match);
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
else {
|
|
522
|
-
// Other types, include them
|
|
523
|
-
truncated.push(match);
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
return {
|
|
527
|
-
truncated,
|
|
528
|
-
isTruncated: true,
|
|
529
|
-
};
|
|
530
|
-
}
|
|
531
|
-
export async function grepFilesStructured(pattern, path, options = {}, abortSignal) {
|
|
532
|
-
try {
|
|
533
|
-
const args = buildGrepArgs(pattern, path, options);
|
|
534
|
-
// Use execFile for async execution with proper abort signal handling
|
|
535
|
-
const rawOutput = await new Promise((resolve, reject) => {
|
|
536
|
-
const child = execFile("rg", args, {
|
|
537
|
-
encoding: "utf-8",
|
|
538
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
539
|
-
});
|
|
540
|
-
let stdout = "";
|
|
541
|
-
let stderr = "";
|
|
542
|
-
child.stdout?.on("data", (data) => {
|
|
543
|
-
stdout += data;
|
|
544
|
-
});
|
|
545
|
-
child.stderr?.on("data", (data) => {
|
|
546
|
-
stderr += data;
|
|
547
|
-
});
|
|
548
|
-
child.on("close", (code) => {
|
|
549
|
-
if (code === 0 || code === 1) {
|
|
550
|
-
resolve(stdout);
|
|
551
|
-
}
|
|
552
|
-
else {
|
|
553
|
-
reject(new Error(stderr || `ripgrep exited with code ${code}`));
|
|
554
|
-
}
|
|
555
|
-
});
|
|
556
|
-
child.on("error", (err) => {
|
|
557
|
-
reject(err);
|
|
558
|
-
});
|
|
559
|
-
if (abortSignal) {
|
|
560
|
-
abortSignal.addEventListener("abort", () => {
|
|
561
|
-
child.kill("SIGTERM");
|
|
562
|
-
reject(new Error("Grep search aborted"));
|
|
563
|
-
});
|
|
564
|
-
}
|
|
565
|
-
});
|
|
566
|
-
// Parse JSON output from ripgrep
|
|
567
|
-
const parsedMatches = parseRipgrepJsonOutput(rawOutput);
|
|
568
|
-
// If JSON parsing resulted in no matches, check if it's a "no matches" case
|
|
569
|
-
// (ripgrep --json returns empty for no matches)
|
|
570
|
-
const hasMatches = parsedMatches.length > 0;
|
|
571
|
-
const matchCount = countActualMatches(parsedMatches);
|
|
572
|
-
// Use the maxResults from options (which will be set by the execute function)
|
|
573
|
-
const maxResults = options.maxResults;
|
|
574
|
-
const { truncated, isTruncated } = truncateMatches(parsedMatches, maxResults);
|
|
575
|
-
const displayedCount = countActualMatches(truncated);
|
|
576
|
-
const displayedContextCount = countContextLines(truncated);
|
|
577
|
-
// Convert to legacy format for backwards compatibility
|
|
578
|
-
const legacyOutput = matchesToLegacyFormat(truncated);
|
|
579
|
-
const finalOutput = legacyOutput || "No matches found.";
|
|
580
|
-
return {
|
|
581
|
-
rawOutput: finalOutput,
|
|
582
|
-
parsedMatches: truncated,
|
|
583
|
-
matchCount,
|
|
584
|
-
displayedCount,
|
|
585
|
-
contextCount: displayedContextCount,
|
|
586
|
-
hasMatches,
|
|
587
|
-
isTruncated,
|
|
588
|
-
};
|
|
589
|
-
}
|
|
590
|
-
catch (error) {
|
|
591
|
-
const execError = error;
|
|
592
|
-
const exitCode = execError.code;
|
|
593
|
-
if (exitCode === "1") {
|
|
594
|
-
return {
|
|
595
|
-
rawOutput: "No matches found.",
|
|
596
|
-
parsedMatches: [],
|
|
597
|
-
matchCount: 0,
|
|
598
|
-
contextCount: 0,
|
|
599
|
-
hasMatches: false,
|
|
600
|
-
};
|
|
601
|
-
}
|
|
602
|
-
if (exitCode === "2") {
|
|
603
|
-
const stderrStr = execError.message;
|
|
604
|
-
throw new Error(`Regex parse error in pattern "${pattern}": ${stderrStr}`);
|
|
605
|
-
}
|
|
606
|
-
throw new Error(`Error executing ripgrep: ${execError.message}`);
|
|
607
|
-
}
|
|
608
|
-
}
|
package/dist/tools/ls.d.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import type { WorkspaceContext } from "../index.ts";
|
|
3
|
-
import type { ToolExecutionOptions } from "./types.ts";
|
|
4
|
-
export declare const LsTool: {
|
|
5
|
-
name: "LS";
|
|
6
|
-
};
|
|
7
|
-
declare const inputSchema: z.ZodObject<{
|
|
8
|
-
path: z.ZodOptional<z.ZodString>;
|
|
9
|
-
limit: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedNumber<unknown>>>>;
|
|
10
|
-
}, z.core.$strip>;
|
|
11
|
-
type LsInputSchema = z.infer<typeof inputSchema>;
|
|
12
|
-
export declare const createLsTool: (options: {
|
|
13
|
-
workspace: WorkspaceContext;
|
|
14
|
-
}) => Promise<{
|
|
15
|
-
toolDef: {
|
|
16
|
-
description: string;
|
|
17
|
-
inputSchema: z.ZodObject<{
|
|
18
|
-
path: z.ZodOptional<z.ZodString>;
|
|
19
|
-
limit: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedNumber<unknown>>>>;
|
|
20
|
-
}, z.core.$strip>;
|
|
21
|
-
};
|
|
22
|
-
display({ path: providedPath, limit }: LsInputSchema): string;
|
|
23
|
-
execute({ path: providedPath, limit }: LsInputSchema, { abortSignal }: ToolExecutionOptions): Promise<string>;
|
|
24
|
-
}>;
|
|
25
|
-
export {};
|
|
26
|
-
//# sourceMappingURL=ls.d.ts.map
|
package/dist/tools/ls.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ls.d.ts","sourceRoot":"","sources":["../../source/tools/ls.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAMpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,eAAO,MAAM,MAAM;;CAElB,CAAC;AAEF,QAAA,MAAM,WAAW;;;iBASf,CAAC;AAEH,KAAK,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAIjD,eAAO,MAAM,YAAY,GAAU,SAAS;IAC1C,SAAS,EAAE,gBAAgB,CAAC;CAC7B;;;;;;;;2CAU0C,aAAa;2CAOnB,aAAa,mBAC3B,oBAAoB,GACpC,OAAO,CAAC,MAAM,CAAC;EA6DrB,CAAC"}
|