@wrongstack/tools 0.286.0 → 0.289.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/README.md +2 -2
- package/dist/_edit-match.d.ts.map +1 -1
- package/dist/_fetch-guard.d.ts.map +1 -1
- package/dist/_redact-command.d.ts.map +1 -1
- package/dist/_spawn-stream.d.ts.map +1 -1
- package/dist/audit.js +61 -14
- package/dist/audit.js.map +3 -3
- package/dist/bash.d.ts +1 -1
- package/dist/bash.d.ts.map +1 -1
- package/dist/bash.js +97 -19
- package/dist/bash.js.map +3 -3
- package/dist/batch-tool-use.d.ts +1 -1
- package/dist/batch-tool-use.d.ts.map +1 -1
- package/dist/batch-tool-use.js +26 -8
- package/dist/batch-tool-use.js.map +2 -2
- package/dist/browser/index.js +1 -1
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/types.d.ts +1 -1
- package/dist/browser/types.d.ts.map +1 -1
- package/dist/builtin.d.ts +12 -10
- package/dist/builtin.d.ts.map +1 -1
- package/dist/builtin.js +2107 -864
- package/dist/builtin.js.map +4 -4
- package/dist/codebase-index/background-indexer.d.ts.map +1 -1
- package/dist/codebase-index/bm25.d.ts +1 -2
- package/dist/codebase-index/bm25.d.ts.map +1 -1
- package/dist/codebase-index/codebase-index-tool.d.ts.map +1 -1
- package/dist/codebase-index/codebase-search-tool.d.ts.map +1 -1
- package/dist/codebase-index/codebase-stats-tool.d.ts +7 -0
- package/dist/codebase-index/codebase-stats-tool.d.ts.map +1 -1
- package/dist/codebase-index/go-parser.d.ts.map +1 -1
- package/dist/codebase-index/index-service.d.ts +11 -1
- package/dist/codebase-index/index-service.d.ts.map +1 -1
- package/dist/codebase-index/index.d.ts +2 -1
- package/dist/codebase-index/index.d.ts.map +1 -1
- package/dist/codebase-index/index.js +934 -283
- package/dist/codebase-index/index.js.map +4 -4
- package/dist/codebase-index/indexer.d.ts.map +1 -1
- package/dist/codebase-index/py-parser.d.ts.map +1 -1
- package/dist/codebase-index/rs-parser.d.ts.map +1 -1
- package/dist/codebase-index/schema.d.ts +43 -1
- package/dist/codebase-index/schema.d.ts.map +1 -1
- package/dist/codebase-index/ts-parser.d.ts.map +1 -1
- package/dist/codebase-index/worker-protocol.d.ts +1 -0
- package/dist/codebase-index/worker-protocol.d.ts.map +1 -1
- package/dist/codebase-index/worker.js +793 -241
- package/dist/codebase-index/worker.js.map +4 -4
- package/dist/codebase-index/writer.d.ts +27 -1
- package/dist/codebase-index/writer.d.ts.map +1 -1
- package/dist/e2e.js +477 -0
- package/dist/e2e.js.map +7 -0
- package/dist/edit.d.ts.map +1 -1
- package/dist/edit.js +36 -12
- package/dist/edit.js.map +3 -3
- package/dist/exec.d.ts +1 -1
- package/dist/exec.d.ts.map +1 -1
- package/dist/exec.js +60 -5
- package/dist/exec.js.map +3 -3
- package/dist/fetch.js +2 -2
- package/dist/fetch.js.map +2 -2
- package/dist/format.js +61 -14
- package/dist/format.js.map +3 -3
- package/dist/glob.js +2 -2
- package/dist/glob.js.map +1 -1
- package/dist/grep.d.ts.map +1 -1
- package/dist/grep.js +2 -2
- package/dist/grep.js.map +2 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2535 -865
- package/dist/index.js.map +4 -4
- package/dist/install.js +61 -14
- package/dist/install.js.map +3 -3
- package/dist/kanban.d.ts +16 -2
- package/dist/kanban.d.ts.map +1 -1
- package/dist/kanban.js +795 -532
- package/dist/kanban.js.map +4 -4
- package/dist/languages/detect.d.ts.map +1 -1
- package/dist/languages/index.js +61 -14
- package/dist/languages/index.js.map +3 -3
- package/dist/languages/types.d.ts +7 -0
- package/dist/languages/types.d.ts.map +1 -1
- package/dist/lint.js +61 -14
- package/dist/lint.js.map +3 -3
- package/dist/next-steps.d.ts +6 -2
- package/dist/next-steps.d.ts.map +1 -1
- package/dist/next-steps.js +3 -3
- package/dist/next-steps.js.map +2 -2
- package/dist/outdated.js +61 -14
- package/dist/outdated.js.map +3 -3
- package/dist/pack.js +2104 -861
- package/dist/pack.js.map +4 -4
- package/dist/plan.d.ts.map +1 -1
- package/dist/plan.js +158 -4
- package/dist/plan.js.map +4 -4
- package/dist/process-registry.js +12 -5
- package/dist/process-registry.js.map +2 -2
- package/dist/search.js +2 -2
- package/dist/search.js.map +2 -2
- package/dist/session-kanban.d.ts +47 -0
- package/dist/session-kanban.d.ts.map +1 -0
- package/dist/session-kanban.js +626 -0
- package/dist/session-kanban.js.map +7 -0
- package/dist/task.d.ts.map +1 -1
- package/dist/task.js +167 -4
- package/dist/task.js.map +4 -4
- package/dist/test.js +61 -14
- package/dist/test.js.map +3 -3
- package/dist/todo.d.ts.map +1 -1
- package/dist/todo.js +159 -7
- package/dist/todo.js.map +4 -4
- package/dist/tool-icon-map.d.ts.map +1 -1
- package/dist/tool-icons.d.ts.map +1 -1
- package/dist/tool-icons.js +7 -1
- package/dist/tool-icons.js.map +2 -2
- package/dist/tree.js +2 -2
- package/dist/tree.js.map +1 -1
- package/dist/typecheck.js +61 -14
- package/dist/typecheck.js.map +3 -3
- package/package.json +11 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FileMeta, IndexStats, Ref, SearchResult, Symbol as IndexSymbol, SymbolKind, SymbolLang } from './schema.js';
|
|
1
|
+
import type { FileMeta, IndexStats, Ref, SearchResult, Symbol as IndexSymbol, SymbolKind, SymbolLang, CodeMapGraph } from './schema.js';
|
|
2
2
|
/**
|
|
3
3
|
* Resolve the per-project index directory. By default it lives under the
|
|
4
4
|
* global project dir (`~/.wrongstack/projects/<hash>/codebase-index`),
|
|
@@ -115,6 +115,8 @@ export declare class IndexStore {
|
|
|
115
115
|
getMaxSymbolId(): number;
|
|
116
116
|
getStats(): IndexStats;
|
|
117
117
|
setLastIndexed(ts: number): void;
|
|
118
|
+
getMetadata(key: string): string | undefined;
|
|
119
|
+
setMetadata(key: string, value: string): void;
|
|
118
120
|
clearAll(): void;
|
|
119
121
|
/**
|
|
120
122
|
* Insert cross-references for a given source symbol id.
|
|
@@ -184,6 +186,30 @@ export declare class IndexStore {
|
|
|
184
186
|
*/
|
|
185
187
|
findRefsFrom(symbolId: number): Ref[];
|
|
186
188
|
private sizeBytes;
|
|
189
|
+
/**
|
|
190
|
+
* Derive a monorepo package name from an absolute file path.
|
|
191
|
+
* Handles both `packages/<name>/...` (workspace) and `src/<name>/...` layouts.
|
|
192
|
+
* Returns `undefined` for files outside any recognisable package structure.
|
|
193
|
+
*/
|
|
194
|
+
private static derivePackage;
|
|
195
|
+
private static packageFromImport;
|
|
196
|
+
private static resolveRelativeImport;
|
|
197
|
+
/**
|
|
198
|
+
* Package-level graph: each workspace package is a node; edges are derived
|
|
199
|
+
* from cross-package symbol references (a symbol in package A references a
|
|
200
|
+
* symbol resolved in package B). Node metadata includes symbol/file counts.
|
|
201
|
+
*/
|
|
202
|
+
getPackageGraph(): CodeMapGraph;
|
|
203
|
+
/**
|
|
204
|
+
* File-level graph for a single package: each file is a node; edges are
|
|
205
|
+
* derived from cross-file symbol references within the package.
|
|
206
|
+
*/
|
|
207
|
+
getFileGraph(packageFilter: string): CodeMapGraph;
|
|
208
|
+
/**
|
|
209
|
+
* Symbol-level graph for a single file: each symbol is a node; edges are
|
|
210
|
+
* derived from intra-file and cross-file symbol references (who calls whom).
|
|
211
|
+
*/
|
|
212
|
+
getSymbolGraph(fileFilter: string): CodeMapGraph;
|
|
187
213
|
close(): void;
|
|
188
214
|
}
|
|
189
215
|
//# sourceMappingURL=writer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"writer.d.ts","sourceRoot":"","sources":["../../src/codebase-index/writer.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"writer.d.ts","sourceRoot":"","sources":["../../src/codebase-index/writer.ts"],"names":[],"mappings":"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"}
|
package/dist/e2e.js
ADDED
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
// src/e2e.ts
|
|
2
|
+
import { open, readdir } from "node:fs/promises";
|
|
3
|
+
import * as path2 from "node:path";
|
|
4
|
+
|
|
5
|
+
// src/_util.ts
|
|
6
|
+
import * as fsp from "node:fs/promises";
|
|
7
|
+
import * as path from "node:path";
|
|
8
|
+
import * as Core from "@wrongstack/core";
|
|
9
|
+
function resolvePath(input, ctx) {
|
|
10
|
+
return path.isAbsolute(input) ? path.normalize(input) : path.resolve(ctx.workingDir ?? ctx.cwd, input);
|
|
11
|
+
}
|
|
12
|
+
function allowedRoots(ctx) {
|
|
13
|
+
return [path.resolve(ctx.projectRoot), path.resolve(Core.wstackGlobalRoot())];
|
|
14
|
+
}
|
|
15
|
+
function isInsideAny(target, roots) {
|
|
16
|
+
return roots.some((root) => {
|
|
17
|
+
const rel = path.relative(root, target);
|
|
18
|
+
return rel === "" || !rel.startsWith("..") && !path.isAbsolute(rel);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function ensureInsideRoot(absPath, ctx) {
|
|
22
|
+
const target = path.resolve(absPath);
|
|
23
|
+
if (ctx.allowOutsideProjectRoot) return target;
|
|
24
|
+
if (isInsideAny(target, allowedRoots(ctx))) return target;
|
|
25
|
+
throw new Error(`Path "${absPath}" is outside project root "${path.resolve(ctx.projectRoot)}"`);
|
|
26
|
+
}
|
|
27
|
+
function safeResolve(input, ctx) {
|
|
28
|
+
return ensureInsideRoot(resolvePath(input, ctx), ctx);
|
|
29
|
+
}
|
|
30
|
+
async function assertRealInsideRoot(absPath, ctx) {
|
|
31
|
+
if (ctx.allowOutsideProjectRoot) return;
|
|
32
|
+
const realRoots = await Promise.all(
|
|
33
|
+
allowedRoots(ctx).map((r) => fsp.realpath(r).catch(() => path.resolve(r)))
|
|
34
|
+
);
|
|
35
|
+
let probe = absPath;
|
|
36
|
+
for (; ; ) {
|
|
37
|
+
let real;
|
|
38
|
+
try {
|
|
39
|
+
real = await fsp.realpath(probe);
|
|
40
|
+
} catch (err) {
|
|
41
|
+
if (err.code === "ENOENT") {
|
|
42
|
+
const parent = path.dirname(probe);
|
|
43
|
+
if (parent === probe) return;
|
|
44
|
+
probe = parent;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
throw err;
|
|
48
|
+
}
|
|
49
|
+
if (isInsideAny(real, realRoots)) return;
|
|
50
|
+
throw new Error(
|
|
51
|
+
`Path "${absPath}" resolves through a symlink outside project root "${realRoots[0]}"`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async function safeResolveReal(input, ctx) {
|
|
56
|
+
const abs = safeResolve(input, ctx);
|
|
57
|
+
await assertRealInsideRoot(abs, ctx);
|
|
58
|
+
return abs;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/e2e.ts
|
|
62
|
+
async function readBoundedText(filePath, maxBytes) {
|
|
63
|
+
let handle;
|
|
64
|
+
try {
|
|
65
|
+
handle = await open(filePath, "r");
|
|
66
|
+
const info = await handle.stat();
|
|
67
|
+
if (!info.isFile() || info.size > maxBytes) return void 0;
|
|
68
|
+
const buffer = Buffer.alloc(info.size);
|
|
69
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
|
70
|
+
return buffer.subarray(0, bytesRead).toString("utf8");
|
|
71
|
+
} catch {
|
|
72
|
+
return void 0;
|
|
73
|
+
} finally {
|
|
74
|
+
await handle?.close().catch(() => void 0);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
var CONFIG_NAMES = /* @__PURE__ */ new Map([
|
|
78
|
+
["playwright.config.ts", "playwright"],
|
|
79
|
+
["playwright.config.js", "playwright"],
|
|
80
|
+
["playwright.config.mts", "playwright"],
|
|
81
|
+
["playwright.config.mjs", "playwright"],
|
|
82
|
+
["playwright.config.cts", "playwright"],
|
|
83
|
+
["playwright.config.cjs", "playwright"],
|
|
84
|
+
["cypress.config.ts", "cypress"],
|
|
85
|
+
["cypress.config.js", "cypress"],
|
|
86
|
+
["cypress.config.mts", "cypress"],
|
|
87
|
+
["cypress.config.mjs", "cypress"],
|
|
88
|
+
["cypress.config.cts", "cypress"],
|
|
89
|
+
["cypress.config.cjs", "cypress"],
|
|
90
|
+
["cypress.json", "cypress"]
|
|
91
|
+
]);
|
|
92
|
+
var SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
93
|
+
".git",
|
|
94
|
+
".hg",
|
|
95
|
+
".svn",
|
|
96
|
+
".wrongstack",
|
|
97
|
+
"node_modules",
|
|
98
|
+
"dist",
|
|
99
|
+
"build",
|
|
100
|
+
"coverage",
|
|
101
|
+
"coverage-html",
|
|
102
|
+
"playwright-report",
|
|
103
|
+
"test-results",
|
|
104
|
+
".next"
|
|
105
|
+
]);
|
|
106
|
+
var MAX_SCAN_DIRECTORIES = 4e3;
|
|
107
|
+
var MAX_PACKAGE_BYTES = 512 * 1024;
|
|
108
|
+
var MAX_CONFIG_BYTES = 512 * 1024;
|
|
109
|
+
var MAX_SPEC_SAMPLES = 20;
|
|
110
|
+
function relativePath(root, target) {
|
|
111
|
+
const value = path2.relative(root, target) || ".";
|
|
112
|
+
return value.split(path2.sep).join("/");
|
|
113
|
+
}
|
|
114
|
+
function escapeRegExp(value) {
|
|
115
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
116
|
+
}
|
|
117
|
+
function staticString(source, property) {
|
|
118
|
+
const key = escapeRegExp(property);
|
|
119
|
+
const direct = source.match(
|
|
120
|
+
new RegExp(`(?:['"]?${key}['"]?)\\s*:\\s*(['"\\x60])([^'"\\x60\\r\\n]*)\\1`)
|
|
121
|
+
);
|
|
122
|
+
if (direct?.[2] && !direct[2].includes("${")) return direct[2];
|
|
123
|
+
const sameLineFallback = source.match(
|
|
124
|
+
new RegExp(`(?:['"]?${key}['"]?)\\s*:[^\\r\\n]{0,300}?(['"\\x60])([^'"\\x60\\r\\n]*)\\1`)
|
|
125
|
+
);
|
|
126
|
+
return sameLineFallback?.[2] && !sameLineFallback[2].includes("${") ? sameLineFallback[2] : void 0;
|
|
127
|
+
}
|
|
128
|
+
function staticNumber(source, property) {
|
|
129
|
+
const key = escapeRegExp(property);
|
|
130
|
+
const match = source.match(new RegExp(`(?:['"]?${key}['"]?)\\s*:\\s*(\\d{2,5})\\b`));
|
|
131
|
+
return match?.[1] ? Number.parseInt(match[1], 10) : void 0;
|
|
132
|
+
}
|
|
133
|
+
function serverHints(framework, source) {
|
|
134
|
+
if (framework === "playwright" && /\bwebServer\s*:/.test(source)) {
|
|
135
|
+
const command = staticString(source, "command");
|
|
136
|
+
const url = staticString(source, "url");
|
|
137
|
+
const port = staticNumber(source, "port");
|
|
138
|
+
return [
|
|
139
|
+
{
|
|
140
|
+
kind: "managed",
|
|
141
|
+
...command ? { command } : {},
|
|
142
|
+
...url ? { url } : {},
|
|
143
|
+
...port ? { port } : {},
|
|
144
|
+
source: "static-config",
|
|
145
|
+
incomplete: !command || !url && !port
|
|
146
|
+
}
|
|
147
|
+
];
|
|
148
|
+
}
|
|
149
|
+
const baseUrl = staticString(source, "baseURL") ?? staticString(source, "baseUrl");
|
|
150
|
+
return baseUrl ? [{ kind: "attach", url: baseUrl, source: "static-config", incomplete: false }] : [];
|
|
151
|
+
}
|
|
152
|
+
function configuredSpecPattern(framework, source) {
|
|
153
|
+
if (framework === "playwright") {
|
|
154
|
+
const testDir = staticString(source, "testDir") ?? "./tests";
|
|
155
|
+
return `${testDir.replace(/\/$/, "")}/**/*.{spec,test}.{js,jsx,ts,tsx,mjs,cjs}`;
|
|
156
|
+
}
|
|
157
|
+
return staticString(source, "specPattern") ?? "cypress/{e2e,integration}/**/*.cy.{js,jsx,ts,tsx,mjs,cjs}";
|
|
158
|
+
}
|
|
159
|
+
async function scanWorkspace(root, maxDepth, signal) {
|
|
160
|
+
const result = {
|
|
161
|
+
configs: [],
|
|
162
|
+
packageFiles: [],
|
|
163
|
+
scannedDirectories: 0,
|
|
164
|
+
truncated: false
|
|
165
|
+
};
|
|
166
|
+
const queue = [{ directory: root, depth: 0 }];
|
|
167
|
+
while (queue.length > 0) {
|
|
168
|
+
signal.throwIfAborted();
|
|
169
|
+
const current = queue.shift();
|
|
170
|
+
if (!current) break;
|
|
171
|
+
result.scannedDirectories += 1;
|
|
172
|
+
if (result.scannedDirectories > MAX_SCAN_DIRECTORIES) {
|
|
173
|
+
result.truncated = true;
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
let entries;
|
|
177
|
+
try {
|
|
178
|
+
entries = await readdir(current.directory, { withFileTypes: true });
|
|
179
|
+
} catch {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
for (const entry of entries) {
|
|
183
|
+
signal.throwIfAborted();
|
|
184
|
+
const absolutePath = path2.join(current.directory, entry.name);
|
|
185
|
+
if (entry.isFile()) {
|
|
186
|
+
if (entry.name === "package.json") result.packageFiles.push(absolutePath);
|
|
187
|
+
const framework = CONFIG_NAMES.get(entry.name);
|
|
188
|
+
if (framework) result.configs.push({ framework, absolutePath });
|
|
189
|
+
} else if (entry.isDirectory() && current.depth < maxDepth && !SKIP_DIRECTORIES.has(entry.name) && !entry.name.startsWith("coverage-") && !entry.name.startsWith("cov-")) {
|
|
190
|
+
queue.push({ directory: absolutePath, depth: current.depth + 1 });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
async function readPackageInfo(packagePath) {
|
|
197
|
+
try {
|
|
198
|
+
const raw = await readBoundedText(packagePath, MAX_PACKAGE_BYTES);
|
|
199
|
+
if (raw === void 0) return void 0;
|
|
200
|
+
const parsed = JSON.parse(raw);
|
|
201
|
+
const scripts = parsed.scripts && typeof parsed.scripts === "object" ? Object.fromEntries(
|
|
202
|
+
Object.entries(parsed.scripts).filter(
|
|
203
|
+
(entry) => typeof entry[1] === "string"
|
|
204
|
+
)
|
|
205
|
+
) : {};
|
|
206
|
+
const dependencies = /* @__PURE__ */ new Set();
|
|
207
|
+
for (const field of ["dependencies", "devDependencies", "optionalDependencies"]) {
|
|
208
|
+
const value = parsed[field];
|
|
209
|
+
if (!value || typeof value !== "object") continue;
|
|
210
|
+
for (const name of Object.keys(value)) dependencies.add(name);
|
|
211
|
+
}
|
|
212
|
+
const declared = typeof parsed.packageManager === "string" ? parsed.packageManager.split("@")[0] ?? "" : "";
|
|
213
|
+
const packageManager = ["pnpm", "npm", "yarn", "bun"].includes(declared) ? declared : void 0;
|
|
214
|
+
return { path: packagePath, scripts, dependencies, packageManager };
|
|
215
|
+
} catch {
|
|
216
|
+
return void 0;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function frameworkFromPackage(info) {
|
|
220
|
+
const found = /* @__PURE__ */ new Set();
|
|
221
|
+
if (info.dependencies.has("@playwright/test") || info.dependencies.has("playwright")) {
|
|
222
|
+
found.add("playwright");
|
|
223
|
+
}
|
|
224
|
+
if (info.dependencies.has("cypress")) found.add("cypress");
|
|
225
|
+
for (const command of Object.values(info.scripts)) {
|
|
226
|
+
if (/\bplaywright(?:\.cmd)?\b/i.test(command)) found.add("playwright");
|
|
227
|
+
if (/\bcypress(?:\.cmd)?\b/i.test(command)) found.add("cypress");
|
|
228
|
+
}
|
|
229
|
+
return [...found];
|
|
230
|
+
}
|
|
231
|
+
async function detectPackageManager(projectRoot, scanRoot, declared) {
|
|
232
|
+
if (declared) return declared;
|
|
233
|
+
let directory = projectRoot;
|
|
234
|
+
while (true) {
|
|
235
|
+
const names = /* @__PURE__ */ new Set();
|
|
236
|
+
try {
|
|
237
|
+
for (const entry of await readdir(directory)) names.add(entry);
|
|
238
|
+
} catch {
|
|
239
|
+
}
|
|
240
|
+
if (names.has("pnpm-lock.yaml")) return "pnpm";
|
|
241
|
+
if (names.has("yarn.lock")) return "yarn";
|
|
242
|
+
if (names.has("bun.lock") || names.has("bun.lockb")) return "bun";
|
|
243
|
+
if (names.has("package-lock.json") || names.has("npm-shrinkwrap.json")) return "npm";
|
|
244
|
+
if (directory === scanRoot) break;
|
|
245
|
+
const parent = path2.dirname(directory);
|
|
246
|
+
const relativeParent = path2.relative(scanRoot, parent);
|
|
247
|
+
if (parent === directory || relativeParent.startsWith("..") || path2.isAbsolute(relativeParent)) {
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
directory = parent;
|
|
251
|
+
}
|
|
252
|
+
return "npm";
|
|
253
|
+
}
|
|
254
|
+
function matchingScripts(info, framework) {
|
|
255
|
+
if (!info) return [];
|
|
256
|
+
const matcher = framework === "playwright" ? /\bplaywright(?:\.cmd)?\b/i : /\bcypress(?:\.cmd)?\b/i;
|
|
257
|
+
return Object.entries(info.scripts).filter(([, command]) => matcher.test(command)).map(([name]) => name).sort((a, b) => {
|
|
258
|
+
const preferred = ["test:e2e", "e2e", "e2e:test"];
|
|
259
|
+
const ai = preferred.indexOf(a);
|
|
260
|
+
const bi = preferred.indexOf(b);
|
|
261
|
+
if (ai !== -1 || bi !== -1) return (ai === -1 ? 99 : ai) - (bi === -1 ? 99 : bi);
|
|
262
|
+
return a.localeCompare(b);
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
function executionPlan(framework, manager, root, configPath, scripts) {
|
|
266
|
+
const script = scripts[0];
|
|
267
|
+
if (script) {
|
|
268
|
+
return {
|
|
269
|
+
command: manager,
|
|
270
|
+
args: ["run", script],
|
|
271
|
+
cwd: root,
|
|
272
|
+
source: "package-script",
|
|
273
|
+
script
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
const runnerArgs = framework === "playwright" ? ["playwright", "test"] : ["cypress", "run"];
|
|
277
|
+
if (configPath) {
|
|
278
|
+
runnerArgs.push(framework === "playwright" ? "--config" : "--config-file", configPath);
|
|
279
|
+
}
|
|
280
|
+
return {
|
|
281
|
+
command: manager === "bun" ? "bunx" : manager,
|
|
282
|
+
args: manager === "bun" ? runnerArgs : manager === "npm" ? ["exec", "--", ...runnerArgs] : ["exec", ...runnerArgs],
|
|
283
|
+
cwd: root,
|
|
284
|
+
source: "framework-default"
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
function isSpec(framework, filename) {
|
|
288
|
+
if (framework === "cypress") return /\.cy\.(?:[cm]?[jt]sx?)$/i.test(filename);
|
|
289
|
+
return /\.(?:spec|test)\.(?:[cm]?[jt]sx?)$/i.test(filename);
|
|
290
|
+
}
|
|
291
|
+
async function collectSpecs(root, framework, testDirectory, signal) {
|
|
292
|
+
const roots = testDirectory ? [path2.resolve(root, testDirectory)] : framework === "cypress" ? [path2.join(root, "cypress", "e2e"), path2.join(root, "cypress", "integration")] : [path2.join(root, "tests")];
|
|
293
|
+
const samples = [];
|
|
294
|
+
let count = 0;
|
|
295
|
+
let scanned = 0;
|
|
296
|
+
const queue = [...roots];
|
|
297
|
+
while (queue.length > 0) {
|
|
298
|
+
signal.throwIfAborted();
|
|
299
|
+
const directory = queue.shift();
|
|
300
|
+
if (!directory) break;
|
|
301
|
+
scanned += 1;
|
|
302
|
+
if (scanned > MAX_SCAN_DIRECTORIES) return { count, samples, truncated: true };
|
|
303
|
+
let entries;
|
|
304
|
+
try {
|
|
305
|
+
entries = await readdir(directory, { withFileTypes: true });
|
|
306
|
+
} catch {
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
for (const entry of entries) {
|
|
310
|
+
signal.throwIfAborted();
|
|
311
|
+
const target = path2.join(directory, entry.name);
|
|
312
|
+
if (entry.isDirectory() && !SKIP_DIRECTORIES.has(entry.name)) queue.push(target);
|
|
313
|
+
else if (entry.isFile() && isSpec(framework, entry.name)) {
|
|
314
|
+
count += 1;
|
|
315
|
+
if (samples.length < MAX_SPEC_SAMPLES) samples.push(relativePath(root, target));
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return { count, samples, truncated: count > samples.length };
|
|
320
|
+
}
|
|
321
|
+
async function readConfig(configPath) {
|
|
322
|
+
if (!configPath) return "";
|
|
323
|
+
return await readBoundedText(configPath, MAX_CONFIG_BYTES) ?? "";
|
|
324
|
+
}
|
|
325
|
+
function nearestPackage(projectRoot, packagesByDirectory, scanRoot) {
|
|
326
|
+
let directory = projectRoot;
|
|
327
|
+
while (true) {
|
|
328
|
+
const found = packagesByDirectory.get(directory);
|
|
329
|
+
if (found) return found;
|
|
330
|
+
if (directory === scanRoot) return void 0;
|
|
331
|
+
const parent = path2.dirname(directory);
|
|
332
|
+
if (parent === directory || relativePath(scanRoot, parent).startsWith("..")) return void 0;
|
|
333
|
+
directory = parent;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
async function discoverE2EProjects(root, options) {
|
|
337
|
+
const maxDepth = Math.max(0, Math.min(options.maxDepth ?? 5, 8));
|
|
338
|
+
const scan = await scanWorkspace(root, maxDepth, options.signal);
|
|
339
|
+
const packages = (await Promise.all(scan.packageFiles.map(readPackageInfo))).filter(
|
|
340
|
+
(info) => Boolean(info)
|
|
341
|
+
);
|
|
342
|
+
const packagesByDirectory = new Map(packages.map((info) => [path2.dirname(info.path), info]));
|
|
343
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
344
|
+
for (const config of scan.configs) {
|
|
345
|
+
if (options.framework && options.framework !== "all" && config.framework !== options.framework) {
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
const projectRoot = path2.dirname(config.absolutePath);
|
|
349
|
+
candidates.set(`${config.framework}:${projectRoot}`, {
|
|
350
|
+
framework: config.framework,
|
|
351
|
+
root: projectRoot,
|
|
352
|
+
configPath: config.absolutePath
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
for (const info of packages) {
|
|
356
|
+
const projectRoot = path2.dirname(info.path);
|
|
357
|
+
for (const framework of frameworkFromPackage(info)) {
|
|
358
|
+
if (options.framework && options.framework !== "all" && framework !== options.framework)
|
|
359
|
+
continue;
|
|
360
|
+
const key = `${framework}:${projectRoot}`;
|
|
361
|
+
if (!candidates.has(key)) candidates.set(key, { framework, root: projectRoot });
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
const projects = [];
|
|
365
|
+
for (const candidate of candidates.values()) {
|
|
366
|
+
options.signal.throwIfAborted();
|
|
367
|
+
const info = nearestPackage(candidate.root, packagesByDirectory, root);
|
|
368
|
+
const source = await readConfig(candidate.configPath);
|
|
369
|
+
const scripts = matchingScripts(info, candidate.framework);
|
|
370
|
+
const manager = await detectPackageManager(candidate.root, root, info?.packageManager);
|
|
371
|
+
const testDirectory = candidate.framework === "playwright" ? staticString(source, "testDir") : void 0;
|
|
372
|
+
const resolvedTestDirectory = testDirectory ? path2.resolve(candidate.root, testDirectory) : void 0;
|
|
373
|
+
const relativeTestDirectory = resolvedTestDirectory ? path2.relative(root, resolvedTestDirectory) : void 0;
|
|
374
|
+
const unsafeTestDirectory = Boolean(
|
|
375
|
+
relativeTestDirectory && (relativeTestDirectory.startsWith("..") || path2.isAbsolute(relativeTestDirectory))
|
|
376
|
+
);
|
|
377
|
+
const specs = options.includeSpecs === false || unsafeTestDirectory ? { count: 0, samples: [], truncated: false } : await collectSpecs(candidate.root, candidate.framework, testDirectory, options.signal);
|
|
378
|
+
const warnings = [];
|
|
379
|
+
if (!candidate.configPath)
|
|
380
|
+
warnings.push("Framework dependency or script found without a config file.");
|
|
381
|
+
if (candidate.configPath && !source) {
|
|
382
|
+
warnings.push("Config could not be read or exceeded the static inspection limit.");
|
|
383
|
+
}
|
|
384
|
+
if (unsafeTestDirectory) {
|
|
385
|
+
warnings.push(
|
|
386
|
+
"Configured testDir resolves outside the requested discovery root and was not scanned."
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
const hints = serverHints(candidate.framework, source);
|
|
390
|
+
if (hints.some((hint) => hint.incomplete)) {
|
|
391
|
+
warnings.push("Configured server contains dynamic values; only static hints are shown.");
|
|
392
|
+
}
|
|
393
|
+
projects.push({
|
|
394
|
+
framework: candidate.framework,
|
|
395
|
+
root: relativePath(root, candidate.root),
|
|
396
|
+
...candidate.configPath ? { configPath: relativePath(root, candidate.configPath) } : {},
|
|
397
|
+
...info ? { packagePath: relativePath(root, info.path) } : {},
|
|
398
|
+
packageManager: manager,
|
|
399
|
+
evidence: [
|
|
400
|
+
...candidate.configPath ? [`config:${relativePath(root, candidate.configPath)}`] : [],
|
|
401
|
+
...info?.dependencies.has(
|
|
402
|
+
candidate.framework === "playwright" ? "@playwright/test" : "cypress"
|
|
403
|
+
) ? [`dependency:${candidate.framework === "playwright" ? "@playwright/test" : "cypress"}`] : [],
|
|
404
|
+
...scripts.map((script) => `script:${script}`)
|
|
405
|
+
],
|
|
406
|
+
scripts,
|
|
407
|
+
serverHints: hints,
|
|
408
|
+
specPattern: configuredSpecPattern(candidate.framework, source),
|
|
409
|
+
specCount: specs.count,
|
|
410
|
+
specSamples: specs.samples,
|
|
411
|
+
specsTruncated: specs.truncated,
|
|
412
|
+
execution: executionPlan(
|
|
413
|
+
candidate.framework,
|
|
414
|
+
manager,
|
|
415
|
+
relativePath(root, candidate.root),
|
|
416
|
+
candidate.configPath ? relativePath(candidate.root, candidate.configPath) : void 0,
|
|
417
|
+
scripts
|
|
418
|
+
),
|
|
419
|
+
warnings
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
projects.sort((a, b) => a.root.localeCompare(b.root) || a.framework.localeCompare(b.framework));
|
|
423
|
+
return {
|
|
424
|
+
root,
|
|
425
|
+
projects,
|
|
426
|
+
scannedDirectories: scan.scannedDirectories,
|
|
427
|
+
scanTruncated: scan.truncated,
|
|
428
|
+
warnings: scan.truncated ? [`Discovery stopped after ${MAX_SCAN_DIRECTORIES} directories; narrow cwd or maxDepth.`] : []
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
var e2ePlanTool = {
|
|
432
|
+
name: "e2e_plan",
|
|
433
|
+
category: "Code Quality",
|
|
434
|
+
description: "Discover Playwright and Cypress projects and preview bounded E2E execution plans without loading configs or starting processes.",
|
|
435
|
+
usageHint: "Use before running browser E2E tests. The result identifies authoritative configs, package scripts, static server hints, specs, and exact argv. This tool never imports project config or executes commands.",
|
|
436
|
+
permission: "auto",
|
|
437
|
+
mutating: false,
|
|
438
|
+
riskTier: "safe",
|
|
439
|
+
capabilities: ["fs.read"],
|
|
440
|
+
icon: "test",
|
|
441
|
+
timeoutMs: 3e4,
|
|
442
|
+
inputSchema: {
|
|
443
|
+
type: "object",
|
|
444
|
+
properties: {
|
|
445
|
+
cwd: { type: "string", description: "Directory inside the project to inspect." },
|
|
446
|
+
framework: {
|
|
447
|
+
type: "string",
|
|
448
|
+
enum: ["all", "playwright", "cypress"],
|
|
449
|
+
description: "Optional framework filter; defaults to all."
|
|
450
|
+
},
|
|
451
|
+
maxDepth: {
|
|
452
|
+
type: "integer",
|
|
453
|
+
minimum: 0,
|
|
454
|
+
maximum: 8,
|
|
455
|
+
description: "Maximum workspace discovery depth; defaults to 5."
|
|
456
|
+
},
|
|
457
|
+
includeSpecs: {
|
|
458
|
+
type: "boolean",
|
|
459
|
+
description: "Count and sample specs; defaults to true."
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
async execute(input, ctx, opts) {
|
|
464
|
+
const root = await safeResolveReal(input.cwd ?? ".", ctx);
|
|
465
|
+
return discoverE2EProjects(root, {
|
|
466
|
+
framework: input.framework,
|
|
467
|
+
maxDepth: input.maxDepth,
|
|
468
|
+
includeSpecs: input.includeSpecs,
|
|
469
|
+
signal: opts.signal
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
export {
|
|
474
|
+
discoverE2EProjects,
|
|
475
|
+
e2ePlanTool
|
|
476
|
+
};
|
|
477
|
+
//# sourceMappingURL=e2e.js.map
|
package/dist/e2e.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/e2e.ts", "../src/_util.ts"],
|
|
4
|
+
"sourcesContent": ["import { open, readdir } from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { Tool } from '@wrongstack/core';\nimport { safeResolveReal } from './_util.js';\n\nexport type E2EFramework = 'playwright' | 'cypress';\nexport type E2EPackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun';\n\ninterface E2EPlanInput {\n cwd?: string | undefined;\n framework?: E2EFramework | 'all' | undefined;\n maxDepth?: number | undefined;\n includeSpecs?: boolean | undefined;\n}\n\nexport interface E2EServerHint {\n kind: 'managed' | 'attach';\n command?: string | undefined;\n url?: string | undefined;\n port?: number | undefined;\n source: 'static-config';\n incomplete: boolean;\n}\n\nexport interface E2EExecutionPlan {\n command: string;\n args: string[];\n cwd: string;\n source: 'package-script' | 'framework-default';\n script?: string | undefined;\n}\n\nexport interface E2EProjectPlan {\n framework: E2EFramework;\n root: string;\n configPath?: string | undefined;\n packagePath?: string | undefined;\n packageManager: E2EPackageManager;\n evidence: string[];\n scripts: string[];\n serverHints: E2EServerHint[];\n specPattern: string;\n specCount: number;\n specSamples: string[];\n specsTruncated: boolean;\n execution: E2EExecutionPlan;\n warnings: string[];\n}\n\nexport interface E2EPlanOutput {\n root: string;\n projects: E2EProjectPlan[];\n scannedDirectories: number;\n scanTruncated: boolean;\n warnings: string[];\n}\n\ninterface ScanResult {\n configs: Array<{ framework: E2EFramework; absolutePath: string }>;\n packageFiles: string[];\n scannedDirectories: number;\n truncated: boolean;\n}\n\ninterface PackageInfo {\n path: string;\n scripts: Record<string, string>;\n dependencies: Set<string>;\n packageManager?: E2EPackageManager | undefined;\n}\n\nasync function readBoundedText(filePath: string, maxBytes: number): Promise<string | undefined> {\n let handle;\n try {\n handle = await open(filePath, 'r');\n const info = await handle.stat();\n if (!info.isFile() || info.size > maxBytes) return undefined;\n const buffer = Buffer.alloc(info.size);\n const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);\n return buffer.subarray(0, bytesRead).toString('utf8');\n } catch {\n return undefined;\n } finally {\n await handle?.close().catch(() => undefined);\n }\n}\n\nconst CONFIG_NAMES = new Map<string, E2EFramework>([\n ['playwright.config.ts', 'playwright'],\n ['playwright.config.js', 'playwright'],\n ['playwright.config.mts', 'playwright'],\n ['playwright.config.mjs', 'playwright'],\n ['playwright.config.cts', 'playwright'],\n ['playwright.config.cjs', 'playwright'],\n ['cypress.config.ts', 'cypress'],\n ['cypress.config.js', 'cypress'],\n ['cypress.config.mts', 'cypress'],\n ['cypress.config.mjs', 'cypress'],\n ['cypress.config.cts', 'cypress'],\n ['cypress.config.cjs', 'cypress'],\n ['cypress.json', 'cypress'],\n]);\n\nconst SKIP_DIRECTORIES = new Set([\n '.git',\n '.hg',\n '.svn',\n '.wrongstack',\n 'node_modules',\n 'dist',\n 'build',\n 'coverage',\n 'coverage-html',\n 'playwright-report',\n 'test-results',\n '.next',\n]);\n\nconst MAX_SCAN_DIRECTORIES = 4_000;\nconst MAX_PACKAGE_BYTES = 512 * 1024;\nconst MAX_CONFIG_BYTES = 512 * 1024;\nconst MAX_SPEC_SAMPLES = 20;\n\nfunction relativePath(root: string, target: string): string {\n const value = path.relative(root, target) || '.';\n return value.split(path.sep).join('/');\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction staticString(source: string, property: string): string | undefined {\n const key = escapeRegExp(property);\n const direct = source.match(\n new RegExp(`(?:['\"]?${key}['\"]?)\\\\s*:\\\\s*(['\"\\\\x60])([^'\"\\\\x60\\\\r\\\\n]*)\\\\1`),\n );\n if (direct?.[2] && !direct[2].includes('${')) return direct[2];\n\n const sameLineFallback = source.match(\n new RegExp(`(?:['\"]?${key}['\"]?)\\\\s*:[^\\\\r\\\\n]{0,300}?(['\"\\\\x60])([^'\"\\\\x60\\\\r\\\\n]*)\\\\1`),\n );\n return sameLineFallback?.[2] && !sameLineFallback[2].includes('${')\n ? sameLineFallback[2]\n : undefined;\n}\n\nfunction staticNumber(source: string, property: string): number | undefined {\n const key = escapeRegExp(property);\n const match = source.match(new RegExp(`(?:['\"]?${key}['\"]?)\\\\s*:\\\\s*(\\\\d{2,5})\\\\b`));\n return match?.[1] ? Number.parseInt(match[1], 10) : undefined;\n}\n\nfunction serverHints(framework: E2EFramework, source: string): E2EServerHint[] {\n if (framework === 'playwright' && /\\bwebServer\\s*:/.test(source)) {\n const command = staticString(source, 'command');\n const url = staticString(source, 'url');\n const port = staticNumber(source, 'port');\n return [\n {\n kind: 'managed',\n ...(command ? { command } : {}),\n ...(url ? { url } : {}),\n ...(port ? { port } : {}),\n source: 'static-config',\n incomplete: !command || (!url && !port),\n },\n ];\n }\n\n const baseUrl = staticString(source, 'baseURL') ?? staticString(source, 'baseUrl');\n return baseUrl\n ? [{ kind: 'attach', url: baseUrl, source: 'static-config', incomplete: false }]\n : [];\n}\n\nfunction configuredSpecPattern(framework: E2EFramework, source: string): string {\n if (framework === 'playwright') {\n const testDir = staticString(source, 'testDir') ?? './tests';\n return `${testDir.replace(/\\/$/, '')}/**/*.{spec,test}.{js,jsx,ts,tsx,mjs,cjs}`;\n }\n return (\n staticString(source, 'specPattern') ??\n 'cypress/{e2e,integration}/**/*.cy.{js,jsx,ts,tsx,mjs,cjs}'\n );\n}\n\nasync function scanWorkspace(\n root: string,\n maxDepth: number,\n signal: AbortSignal,\n): Promise<ScanResult> {\n const result: ScanResult = {\n configs: [],\n packageFiles: [],\n scannedDirectories: 0,\n truncated: false,\n };\n const queue: Array<{ directory: string; depth: number }> = [{ directory: root, depth: 0 }];\n\n while (queue.length > 0) {\n signal.throwIfAborted();\n const current = queue.shift();\n if (!current) break;\n result.scannedDirectories += 1;\n if (result.scannedDirectories > MAX_SCAN_DIRECTORIES) {\n result.truncated = true;\n break;\n }\n\n let entries;\n try {\n entries = await readdir(current.directory, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n signal.throwIfAborted();\n const absolutePath = path.join(current.directory, entry.name);\n if (entry.isFile()) {\n if (entry.name === 'package.json') result.packageFiles.push(absolutePath);\n const framework = CONFIG_NAMES.get(entry.name);\n if (framework) result.configs.push({ framework, absolutePath });\n } else if (\n entry.isDirectory() &&\n current.depth < maxDepth &&\n !SKIP_DIRECTORIES.has(entry.name) &&\n !entry.name.startsWith('coverage-') &&\n !entry.name.startsWith('cov-')\n ) {\n queue.push({ directory: absolutePath, depth: current.depth + 1 });\n }\n }\n }\n\n return result;\n}\n\nasync function readPackageInfo(packagePath: string): Promise<PackageInfo | undefined> {\n try {\n const raw = await readBoundedText(packagePath, MAX_PACKAGE_BYTES);\n if (raw === undefined) return undefined;\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n const scripts =\n parsed.scripts && typeof parsed.scripts === 'object'\n ? Object.fromEntries(\n Object.entries(parsed.scripts as Record<string, unknown>).filter(\n (entry): entry is [string, string] => typeof entry[1] === 'string',\n ),\n )\n : {};\n const dependencies = new Set<string>();\n for (const field of ['dependencies', 'devDependencies', 'optionalDependencies']) {\n const value = parsed[field];\n if (!value || typeof value !== 'object') continue;\n for (const name of Object.keys(value as Record<string, unknown>)) dependencies.add(name);\n }\n const declared =\n typeof parsed.packageManager === 'string' ? (parsed.packageManager.split('@')[0] ?? '') : '';\n const packageManager = ['pnpm', 'npm', 'yarn', 'bun'].includes(declared)\n ? (declared as E2EPackageManager)\n : undefined;\n return { path: packagePath, scripts, dependencies, packageManager };\n } catch {\n return undefined;\n }\n}\n\nfunction frameworkFromPackage(info: PackageInfo): E2EFramework[] {\n const found = new Set<E2EFramework>();\n if (info.dependencies.has('@playwright/test') || info.dependencies.has('playwright')) {\n found.add('playwright');\n }\n if (info.dependencies.has('cypress')) found.add('cypress');\n for (const command of Object.values(info.scripts)) {\n if (/\\bplaywright(?:\\.cmd)?\\b/i.test(command)) found.add('playwright');\n if (/\\bcypress(?:\\.cmd)?\\b/i.test(command)) found.add('cypress');\n }\n return [...found];\n}\n\nasync function detectPackageManager(\n projectRoot: string,\n scanRoot: string,\n declared?: E2EPackageManager,\n): Promise<E2EPackageManager> {\n if (declared) return declared;\n let directory = projectRoot;\n while (true) {\n const names = new Set<string>();\n try {\n for (const entry of await readdir(directory)) names.add(entry);\n } catch {\n // Continue toward the scan root.\n }\n if (names.has('pnpm-lock.yaml')) return 'pnpm';\n if (names.has('yarn.lock')) return 'yarn';\n if (names.has('bun.lock') || names.has('bun.lockb')) return 'bun';\n if (names.has('package-lock.json') || names.has('npm-shrinkwrap.json')) return 'npm';\n if (directory === scanRoot) break;\n const parent = path.dirname(directory);\n const relativeParent = path.relative(scanRoot, parent);\n if (\n parent === directory ||\n relativeParent.startsWith('..') ||\n path.isAbsolute(relativeParent)\n ) {\n break;\n }\n directory = parent;\n }\n return 'npm';\n}\n\nfunction matchingScripts(info: PackageInfo | undefined, framework: E2EFramework): string[] {\n if (!info) return [];\n const matcher =\n framework === 'playwright' ? /\\bplaywright(?:\\.cmd)?\\b/i : /\\bcypress(?:\\.cmd)?\\b/i;\n return Object.entries(info.scripts)\n .filter(([, command]) => matcher.test(command))\n .map(([name]) => name)\n .sort((a, b) => {\n const preferred = ['test:e2e', 'e2e', 'e2e:test'];\n const ai = preferred.indexOf(a);\n const bi = preferred.indexOf(b);\n if (ai !== -1 || bi !== -1) return (ai === -1 ? 99 : ai) - (bi === -1 ? 99 : bi);\n return a.localeCompare(b);\n });\n}\n\nfunction executionPlan(\n framework: E2EFramework,\n manager: E2EPackageManager,\n root: string,\n configPath: string | undefined,\n scripts: string[],\n): E2EExecutionPlan {\n const script = scripts[0];\n if (script) {\n return {\n command: manager,\n args: ['run', script],\n cwd: root,\n source: 'package-script',\n script,\n };\n }\n\n const runnerArgs = framework === 'playwright' ? ['playwright', 'test'] : ['cypress', 'run'];\n if (configPath) {\n runnerArgs.push(framework === 'playwright' ? '--config' : '--config-file', configPath);\n }\n return {\n command: manager === 'bun' ? 'bunx' : manager,\n args:\n manager === 'bun'\n ? runnerArgs\n : manager === 'npm'\n ? ['exec', '--', ...runnerArgs]\n : ['exec', ...runnerArgs],\n cwd: root,\n source: 'framework-default',\n };\n}\n\nfunction isSpec(framework: E2EFramework, filename: string): boolean {\n if (framework === 'cypress') return /\\.cy\\.(?:[cm]?[jt]sx?)$/i.test(filename);\n return /\\.(?:spec|test)\\.(?:[cm]?[jt]sx?)$/i.test(filename);\n}\n\nasync function collectSpecs(\n root: string,\n framework: E2EFramework,\n testDirectory: string | undefined,\n signal: AbortSignal,\n): Promise<{ count: number; samples: string[]; truncated: boolean }> {\n const roots = testDirectory\n ? [path.resolve(root, testDirectory)]\n : framework === 'cypress'\n ? [path.join(root, 'cypress', 'e2e'), path.join(root, 'cypress', 'integration')]\n : [path.join(root, 'tests')];\n const samples: string[] = [];\n let count = 0;\n let scanned = 0;\n const queue = [...roots];\n\n while (queue.length > 0) {\n signal.throwIfAborted();\n const directory = queue.shift();\n if (!directory) break;\n scanned += 1;\n if (scanned > MAX_SCAN_DIRECTORIES) return { count, samples, truncated: true };\n let entries;\n try {\n entries = await readdir(directory, { withFileTypes: true });\n } catch {\n continue;\n }\n for (const entry of entries) {\n signal.throwIfAborted();\n const target = path.join(directory, entry.name);\n if (entry.isDirectory() && !SKIP_DIRECTORIES.has(entry.name)) queue.push(target);\n else if (entry.isFile() && isSpec(framework, entry.name)) {\n count += 1;\n if (samples.length < MAX_SPEC_SAMPLES) samples.push(relativePath(root, target));\n }\n }\n }\n return { count, samples, truncated: count > samples.length };\n}\n\nasync function readConfig(configPath: string | undefined): Promise<string> {\n if (!configPath) return '';\n return (await readBoundedText(configPath, MAX_CONFIG_BYTES)) ?? '';\n}\n\nfunction nearestPackage(\n projectRoot: string,\n packagesByDirectory: Map<string, PackageInfo>,\n scanRoot: string,\n): PackageInfo | undefined {\n let directory = projectRoot;\n while (true) {\n const found = packagesByDirectory.get(directory);\n if (found) return found;\n if (directory === scanRoot) return undefined;\n const parent = path.dirname(directory);\n if (parent === directory || relativePath(scanRoot, parent).startsWith('..')) return undefined;\n directory = parent;\n }\n}\n\nexport async function discoverE2EProjects(\n root: string,\n options: {\n framework?: E2EFramework | 'all' | undefined;\n maxDepth?: number | undefined;\n includeSpecs?: boolean | undefined;\n signal: AbortSignal;\n },\n): Promise<E2EPlanOutput> {\n const maxDepth = Math.max(0, Math.min(options.maxDepth ?? 5, 8));\n const scan = await scanWorkspace(root, maxDepth, options.signal);\n const packages = (await Promise.all(scan.packageFiles.map(readPackageInfo))).filter(\n (info): info is PackageInfo => Boolean(info),\n );\n const packagesByDirectory = new Map(packages.map((info) => [path.dirname(info.path), info]));\n const candidates = new Map<\n string,\n { framework: E2EFramework; root: string; configPath?: string }\n >();\n\n for (const config of scan.configs) {\n if (\n options.framework &&\n options.framework !== 'all' &&\n config.framework !== options.framework\n ) {\n continue;\n }\n const projectRoot = path.dirname(config.absolutePath);\n candidates.set(`${config.framework}:${projectRoot}`, {\n framework: config.framework,\n root: projectRoot,\n configPath: config.absolutePath,\n });\n }\n\n for (const info of packages) {\n const projectRoot = path.dirname(info.path);\n for (const framework of frameworkFromPackage(info)) {\n if (options.framework && options.framework !== 'all' && framework !== options.framework)\n continue;\n const key = `${framework}:${projectRoot}`;\n if (!candidates.has(key)) candidates.set(key, { framework, root: projectRoot });\n }\n }\n\n const projects: E2EProjectPlan[] = [];\n for (const candidate of candidates.values()) {\n options.signal.throwIfAborted();\n const info = nearestPackage(candidate.root, packagesByDirectory, root);\n const source = await readConfig(candidate.configPath);\n const scripts = matchingScripts(info, candidate.framework);\n const manager = await detectPackageManager(candidate.root, root, info?.packageManager);\n const testDirectory =\n candidate.framework === 'playwright' ? staticString(source, 'testDir') : undefined;\n const resolvedTestDirectory = testDirectory\n ? path.resolve(candidate.root, testDirectory)\n : undefined;\n const relativeTestDirectory = resolvedTestDirectory\n ? path.relative(root, resolvedTestDirectory)\n : undefined;\n const unsafeTestDirectory = Boolean(\n relativeTestDirectory &&\n (relativeTestDirectory.startsWith('..') || path.isAbsolute(relativeTestDirectory)),\n );\n const specs =\n options.includeSpecs === false || unsafeTestDirectory\n ? { count: 0, samples: [], truncated: false }\n : await collectSpecs(candidate.root, candidate.framework, testDirectory, options.signal);\n const warnings: string[] = [];\n if (!candidate.configPath)\n warnings.push('Framework dependency or script found without a config file.');\n if (candidate.configPath && !source) {\n warnings.push('Config could not be read or exceeded the static inspection limit.');\n }\n if (unsafeTestDirectory) {\n warnings.push(\n 'Configured testDir resolves outside the requested discovery root and was not scanned.',\n );\n }\n const hints = serverHints(candidate.framework, source);\n if (hints.some((hint) => hint.incomplete)) {\n warnings.push('Configured server contains dynamic values; only static hints are shown.');\n }\n\n projects.push({\n framework: candidate.framework,\n root: relativePath(root, candidate.root),\n ...(candidate.configPath ? { configPath: relativePath(root, candidate.configPath) } : {}),\n ...(info ? { packagePath: relativePath(root, info.path) } : {}),\n packageManager: manager,\n evidence: [\n ...(candidate.configPath ? [`config:${relativePath(root, candidate.configPath)}`] : []),\n ...(info?.dependencies.has(\n candidate.framework === 'playwright' ? '@playwright/test' : 'cypress',\n )\n ? [`dependency:${candidate.framework === 'playwright' ? '@playwright/test' : 'cypress'}`]\n : []),\n ...scripts.map((script) => `script:${script}`),\n ],\n scripts,\n serverHints: hints,\n specPattern: configuredSpecPattern(candidate.framework, source),\n specCount: specs.count,\n specSamples: specs.samples,\n specsTruncated: specs.truncated,\n execution: executionPlan(\n candidate.framework,\n manager,\n relativePath(root, candidate.root),\n candidate.configPath ? relativePath(candidate.root, candidate.configPath) : undefined,\n scripts,\n ),\n warnings,\n });\n }\n\n projects.sort((a, b) => a.root.localeCompare(b.root) || a.framework.localeCompare(b.framework));\n return {\n root,\n projects,\n scannedDirectories: scan.scannedDirectories,\n scanTruncated: scan.truncated,\n warnings: scan.truncated\n ? [`Discovery stopped after ${MAX_SCAN_DIRECTORIES} directories; narrow cwd or maxDepth.`]\n : [],\n };\n}\n\nexport const e2ePlanTool: Tool<E2EPlanInput, E2EPlanOutput> = {\n name: 'e2e_plan',\n category: 'Code Quality',\n description:\n 'Discover Playwright and Cypress projects and preview bounded E2E execution plans without loading configs or starting processes.',\n usageHint:\n 'Use before running browser E2E tests. The result identifies authoritative configs, package scripts, ' +\n 'static server hints, specs, and exact argv. This tool never imports project config or executes commands.',\n permission: 'auto',\n mutating: false,\n riskTier: 'safe',\n capabilities: ['fs.read'],\n icon: 'test',\n timeoutMs: 30_000,\n inputSchema: {\n type: 'object',\n properties: {\n cwd: { type: 'string', description: 'Directory inside the project to inspect.' },\n framework: {\n type: 'string',\n enum: ['all', 'playwright', 'cypress'],\n description: 'Optional framework filter; defaults to all.',\n },\n maxDepth: {\n type: 'integer',\n minimum: 0,\n maximum: 8,\n description: 'Maximum workspace discovery depth; defaults to 5.',\n },\n includeSpecs: {\n type: 'boolean',\n description: 'Count and sample specs; defaults to true.',\n },\n },\n },\n async execute(input, ctx, opts) {\n const root = await safeResolveReal(input.cwd ?? '.', ctx);\n return discoverE2EProjects(root, {\n framework: input.framework,\n maxDepth: input.maxDepth,\n includeSpecs: input.includeSpecs,\n signal: opts.signal,\n });\n },\n};\n", "import { createHash } from 'node:crypto';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport * as Core from '@wrongstack/core';\nimport type { Context } from '@wrongstack/core';\n\n/**\n * sha-256 hex of a UTF-8 string. Used by the file tools to record a content\n * hash alongside the mtime in `ctx.recordRead` \u2014 the hash is the authoritative\n * staleness arbiter for `edit` (mtime has a 2 s tolerance window on Windows).\n */\nexport function sha256hex(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n/** Detected package manager for a project directory. */\nexport type PackageManager = 'pnpm' | 'yarn' | 'npm';\n\n/**\n * Detect the project's package manager by inspecting lockfiles in `cwd`.\n * Order: pnpm \u2192 yarn \u2192 npm (default). Missing or unreadable directories fall\n * back to `npm` rather than throwing, so a `safeResolve`-checked cwd that\n * happens to be empty never aborts the tool.\n */\nexport async function detectPackageManager(cwd: string): Promise<PackageManager> {\n const { stat } = await import('node:fs/promises');\n try {\n await stat(`${cwd}/pnpm-lock.yaml`);\n return 'pnpm';\n } catch {\n /* not pnpm */\n }\n try {\n await stat(`${cwd}/yarn.lock`);\n return 'yarn';\n } catch {\n /* not yarn */\n }\n return 'npm';\n}\n\nexport function resolvePath(input: string, ctx: Context): string {\n return path.isAbsolute(input) ? path.normalize(input) : path.resolve(ctx.workingDir ?? ctx.cwd, input);\n}\n\n/**\n * Roots every file tool may always reach, even in restricted mode: the\n * project root and the user-global `~/.wrongstack` directory (config, memory,\n * sessions, skills). `~/.wrongstack` honors the `WRONGSTACK_HOME` override.\n */\nfunction allowedRoots(ctx: Context): string[] {\n return [path.resolve(ctx.projectRoot), path.resolve(Core.wstackGlobalRoot())];\n}\n\n/** True if `target` is `root` itself or nested inside any of `roots`. */\nfunction isInsideAny(target: string, roots: string[]): boolean {\n return roots.some((root) => {\n const rel = path.relative(root, target);\n return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));\n });\n}\n\nexport function ensureInsideRoot(absPath: string, ctx: Context): string {\n const target = path.resolve(absPath);\n // Unrestricted filesystem access: skip the project-root containment check.\n if (ctx.allowOutsideProjectRoot) return target;\n if (isInsideAny(target, allowedRoots(ctx))) return target;\n throw new Error(`Path \"${absPath}\" is outside project root \"${path.resolve(ctx.projectRoot)}\"`);\n}\n\nexport function safeResolve(input: string, ctx: Context): string {\n return ensureInsideRoot(resolvePath(input, ctx), ctx);\n}\n\n/**\n * Defense against in-root\u2192out-of-root symlink escape (CWE-59). `safeResolve`\n * only does a syntactic `../` check, so a symlink that lives *inside* the\n * project root but points outside still passes it. This resolves the path\n * through `fs.realpath` and re-verifies containment against the realpath of\n * the project root (comparing like-for-like, since the root itself may be a\n * symlink \u2014 macOS `/var`\u2192`/private/var`, Windows 8.3 short names). For a path\n * that does not exist yet (e.g. a `write` to a new file) the nearest existing\n * ancestor directory is checked instead. Throws if the real target escapes.\n *\n * Mirrors the per-file guard already used in `replace.ts`/`grep.ts`; applied\n * to single-file `read`/`edit`/`write` it throws (rather than skips) because\n * the caller named exactly one file.\n */\nexport async function assertRealInsideRoot(absPath: string, ctx: Context): Promise<void> {\n // Unrestricted filesystem access: no symlink-escape check to perform.\n if (ctx.allowOutsideProjectRoot) return;\n // Compare like-for-like against the realpath of each always-allowed root\n // (project root + ~/.wrongstack), since a root may itself be a symlink.\n const realRoots = await Promise.all(\n allowedRoots(ctx).map((r) => fsp.realpath(r).catch(() => path.resolve(r))),\n );\n let probe = absPath;\n for (;;) {\n let real: string;\n try {\n real = await fsp.realpath(probe);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n const parent = path.dirname(probe);\n if (parent === probe) return; // reached fs root without escaping\n probe = parent;\n continue;\n }\n throw err;\n }\n if (isInsideAny(real, realRoots)) return;\n throw new Error(\n `Path \"${absPath}\" resolves through a symlink outside project root \"${realRoots[0]}\"`,\n );\n }\n}\n\n/** `safeResolve` + symlink realpath containment check. Async. */\nexport async function safeResolveReal(input: string, ctx: Context): Promise<string> {\n const abs = safeResolve(input, ctx);\n await assertRealInsideRoot(abs, ctx);\n return abs;\n}\n\nexport function truncateMiddle(s: string, max: number): string {\n if (Buffer.byteLength(s, 'utf8') <= max) return s;\n const half = Math.floor(max / 2);\n return (\n s.slice(0, half) +\n `\\n\u2026[truncated ${Buffer.byteLength(s, 'utf8') - max} bytes from middle]\u2026\\n` +\n s.slice(-half)\n );\n}\n\nexport function isBinaryBuffer(buf: Buffer): boolean {\n const len = Math.min(buf.length, 8192);\n for (let i = 0; i < len; i++) {\n if (buf[i] === 0) return true;\n }\n return false;\n}\n\n// \u2500\u2500\u2500 Command-output normalization (token-saving) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Raw process output is full of tokens the model gains nothing from: ANSI\n// escapes, carriage-return progress spam, runs of identical warning lines, and\n// huge tails of build noise. These helpers strip that noise before the output\n// reaches the LLM. They are scoped to COMMAND tools (bash/git/exec and the\n// _spawn-stream consumers) \u2014 never applied to structured/code outputs.\n\n/** Unified byte cap for all command tool output fed to the model. */\nexport const COMMAND_OUTPUT_MAX_BYTES = 32_768;\n\n/** Runs of >= this many identical consecutive lines are collapsed. */\nconst REPEAT_RUN_THRESHOLD = 3;\n\n/**\n * Collapse carriage-return overwrites the way a terminal would: `\\r\\n` becomes\n * `\\n`, and a bare `\\r` (progress redraw) keeps only the text after the LAST\n * `\\r` on its physical line. Without this, a single progress bar that redraws\n * 200 times explodes into 200 lines.\n */\nexport function collapseCarriageReturns(text: string): string {\n const lf = text.replace(/\\r\\n/g, '\\n');\n if (!lf.includes('\\r')) return lf;\n return lf\n .split('\\n')\n .map((line) => (line.includes('\\r') ? line.slice(line.lastIndexOf('\\r') + 1) : line))\n .join('\\n');\n}\n\n/**\n * Collapse a run of `minRun`+ identical consecutive lines into the line once\n * plus a marker. Consecutive-only \u2014 it never reorders or dedups non-adjacent\n * lines, so diffs/source stay intact.\n */\nexport function collapseConsecutiveDuplicates(text: string, minRun = REPEAT_RUN_THRESHOLD): string {\n const lines = text.split('\\n');\n const out: string[] = [];\n let i = 0;\n while (i < lines.length) {\n let j = i + 1;\n while (j < lines.length && lines[j] === lines[i]) j++;\n const run = j - i;\n if (run >= minRun) {\n out.push(lines[i]!, `\u2026 \u27E8repeated ${run}\u00D7\u27E9`);\n } else {\n for (let k = i; k < j; k++) out.push(lines[k]!);\n }\n i = j;\n }\n return out.join('\\n');\n}\n\n/** Largest prefix of `s` whose UTF-8 byte length is <= `maxBytes`. */\nfunction takeHeadBytes(s: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n /* v8 ignore next -- only caller (truncateHeadTail) passes a budget smaller than s; defensive. */\n if (Buffer.byteLength(s, 'utf8') <= maxBytes) return s;\n let lo = 0;\n let hi = s.length;\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (Buffer.byteLength(s.slice(0, mid), 'utf8') <= maxBytes) lo = mid;\n else hi = mid - 1;\n }\n return s.slice(0, lo);\n}\n\n/** Largest suffix of `s` whose UTF-8 byte length is <= `maxBytes`. */\nfunction takeTailBytes(s: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n /* v8 ignore next -- only caller (truncateHeadTail) passes a budget smaller than s; defensive. */\n if (Buffer.byteLength(s, 'utf8') <= maxBytes) return s;\n let lo = 0;\n let hi = s.length;\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (Buffer.byteLength(s.slice(s.length - mid), 'utf8') <= maxBytes) lo = mid;\n else hi = mid - 1;\n }\n return s.slice(s.length - lo);\n}\n\n/**\n * Truncate to `maxBytes` keeping BOTH ends \u2014 the head (what ran / early context)\n * and the tail (errors and summaries usually land last), biased ~45/55 toward\n * the tail. The result never exceeds `maxBytes`.\n */\nexport function truncateHeadTail(s: string, maxBytes: number): string {\n const total = Buffer.byteLength(s, 'utf8');\n if (total <= maxBytes) return s;\n // Reserve a fixed allowance for the marker so the final string can't exceed\n // the cap even though the dropped-byte count's digit width varies.\n const MARKER_RESERVE = 64;\n const avail = Math.max(0, maxBytes - MARKER_RESERVE);\n const headBudget = Math.floor(avail * 0.45);\n const head = takeHeadBytes(s, headBudget);\n const tail = takeTailBytes(s, avail - Buffer.byteLength(head, 'utf8'));\n const kept = Buffer.byteLength(head, 'utf8') + Buffer.byteLength(tail, 'utf8');\n return `${head}\\n\u2026[truncated ${total - kept} bytes]\u2026\\n${tail}`;\n}\n\n/**\n * Full token-saving pipeline for command tool output: strip ANSI \u2192 collapse\n * carriage-return progress \u2192 trim trailing whitespace \u2192 collapse identical\n * consecutive lines \u2192 squeeze blank-line runs \u2192 head+tail truncate to the cap.\n */\nexport function normalizeCommandOutput(\n raw: string,\n opts: { maxBytes?: number | undefined } = {},\n): string {\n if (!raw) return raw;\n let text = Core.stripAnsi(raw);\n text = collapseCarriageReturns(text);\n text = text.replace(/[ \\t]+$/gm, ''); // trailing whitespace per line\n text = collapseConsecutiveDuplicates(text);\n text = text.replace(/\\n{3,}/g, '\\n\\n'); // >=2 blank lines \u2192 1\n return truncateHeadTail(text, opts.maxBytes ?? COMMAND_OUTPUT_MAX_BYTES);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["path"]
|
|
7
|
+
}
|
package/dist/edit.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../src/edit.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAS7C,OAAO,EAIL,KAAK,SAAS,EAIf,MAAM,kBAAkB,CAAC;AAI1B,UAAU,SAAS;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACnC;AAED,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IACrC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AAED,eAAO,MAAM,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../src/edit.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAS7C,OAAO,EAIL,KAAK,SAAS,EAIf,MAAM,kBAAkB,CAAC;AAI1B,UAAU,SAAS;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACnC;AAED,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IACrC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AAED,eAAO,MAAM,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE,UAAU,CAsRhD,CAAC"}
|