openmagic 0.31.9 → 0.33.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/dist/cli.js CHANGED
@@ -204,6 +204,79 @@ function listFiles(rootPath, roots, maxDepth = 4) {
204
204
  walk(rootPath, 0);
205
205
  return entries;
206
206
  }
207
+ var GREP_EXTENSIONS = /* @__PURE__ */ new Set([
208
+ ".js",
209
+ ".jsx",
210
+ ".ts",
211
+ ".tsx",
212
+ ".mjs",
213
+ ".cjs",
214
+ ".vue",
215
+ ".svelte",
216
+ ".astro",
217
+ ".html",
218
+ ".htm",
219
+ ".css",
220
+ ".scss",
221
+ ".less",
222
+ ".json",
223
+ ".md",
224
+ ".yaml",
225
+ ".yml",
226
+ ".php",
227
+ ".py",
228
+ ".rb"
229
+ ]);
230
+ function grepFiles(pattern, searchRoot, roots, maxResults = 30) {
231
+ if (!isPathSafe(searchRoot, roots)) return [];
232
+ const results = [];
233
+ const lowerPattern = pattern.toLowerCase();
234
+ function walk(dir, depth) {
235
+ if (depth > 5 || results.length >= maxResults) return;
236
+ let items;
237
+ try {
238
+ items = readdirSync(dir);
239
+ } catch {
240
+ return;
241
+ }
242
+ for (const item of items) {
243
+ if (results.length >= maxResults) return;
244
+ if (IGNORED_DIRS.has(item) || item.startsWith(".") && item !== ".env.example") continue;
245
+ const fullPath = join2(dir, item);
246
+ let stat;
247
+ try {
248
+ stat = lstatSync(fullPath);
249
+ } catch {
250
+ continue;
251
+ }
252
+ if (stat.isSymbolicLink()) continue;
253
+ if (stat.isDirectory()) {
254
+ walk(fullPath, depth + 1);
255
+ } else if (stat.isFile()) {
256
+ const ext = extname(item).toLowerCase();
257
+ if (!GREP_EXTENSIONS.has(ext)) continue;
258
+ try {
259
+ const content = readFileSync2(fullPath, "utf-8");
260
+ const lines = content.split("\n");
261
+ let fileMatches = 0;
262
+ for (let i = 0; i < lines.length && fileMatches < 5; i++) {
263
+ if (lines[i].toLowerCase().includes(lowerPattern)) {
264
+ results.push({
265
+ file: relative(searchRoot, fullPath),
266
+ lineNum: i + 1,
267
+ line: lines[i].trim().slice(0, 200)
268
+ });
269
+ fileMatches++;
270
+ }
271
+ }
272
+ } catch {
273
+ }
274
+ }
275
+ }
276
+ }
277
+ walk(searchRoot, 0);
278
+ return results;
279
+ }
207
280
  function getProjectTree(roots) {
208
281
  const lines = [];
209
282
  for (const root of roots) {
@@ -909,6 +982,7 @@ You MUST respond with valid JSON in this exact format:
909
982
  4. Include 3-5 lines of surrounding context in the search field to ensure uniqueness
910
983
  5. Keep modifications minimal \u2014 change only what's needed. Do NOT rewrite entire files.
911
984
  6. If the grounded files don't contain the code you need to modify, return: {"modifications":[],"explanation":"NEED_FILE: exact/relative/path/to/file.ext"}
985
+ 7. To search for a pattern across the codebase, return: {"modifications":[],"explanation":"SEARCH_FILES: \\"pattern\\" in optional/path/"}
912
986
  7. For style changes: check the dependencies (package.json) to know if the project uses Tailwind, MUI, etc. Use the project's styling approach, not raw CSS
913
987
  8. Use the selected element's cssSelector, className, parentContainerStyles, and siblings to understand the full layout context
914
988
  9. Use the page URL route and componentHint to identify the correct source file to modify
@@ -949,6 +1023,12 @@ function buildContextParts(context) {
949
1023
  if (el.reactProps) {
950
1024
  elementData.reactProps = el.reactProps;
951
1025
  }
1026
+ if (el.childrenLayout?.length) {
1027
+ elementData.childrenLayout = el.childrenLayout;
1028
+ }
1029
+ if (el.resolvedClasses?.length) {
1030
+ elementData.resolvedClasses = el.resolvedClasses;
1031
+ }
952
1032
  parts.selectedElement = JSON.stringify(elementData, null, 2);
953
1033
  }
954
1034
  if (context.files?.length) {
@@ -959,6 +1039,12 @@ function buildContextParts(context) {
959
1039
  if (context.pageTitle) parts.pageTitle = context.pageTitle;
960
1040
  if (context.networkLogs) parts.networkLogs = context.networkLogs.map((l) => `${l.method} ${l.url} \u2192 ${l.status || "pending"}`).join("\n");
961
1041
  if (context.consoleLogs) parts.consoleLogs = context.consoleLogs.map((l) => `[${l.level}] ${l.args.join(" ")}`).join("\n");
1042
+ if (context.searchResults?.length) {
1043
+ parts.searchResults = context.searchResults.map(
1044
+ (s) => `Search: "${s.query}"
1045
+ ${s.matches.map((m) => ` ${m.file}:${m.lineNum}: ${m.line}`).join("\n")}`
1046
+ ).join("\n\n");
1047
+ }
962
1048
  return parts;
963
1049
  }
964
1050
  function buildUserMessage(userPrompt, context) {
@@ -1002,6 +1088,12 @@ ${context.networkLogs}
1002
1088
  parts.push(`## Console Output
1003
1089
  \`\`\`
1004
1090
  ${context.consoleLogs}
1091
+ \`\`\``);
1092
+ }
1093
+ if (context.searchResults) {
1094
+ parts.push(`## Search Results
1095
+ \`\`\`
1096
+ ${context.searchResults}
1005
1097
  \`\`\``);
1006
1098
  }
1007
1099
  parts.push(`## User Request
@@ -1694,6 +1786,17 @@ async function handleMessage(ws, msg, state, roots) {
1694
1786
  }
1695
1787
  break;
1696
1788
  }
1789
+ case "fs.grep": {
1790
+ const payload = msg.payload;
1791
+ if (!payload?.pattern) {
1792
+ sendError(ws, "invalid_payload", "Missing pattern", msg.id);
1793
+ break;
1794
+ }
1795
+ const searchRoot = payload.path ? join3(roots[0] || process.cwd(), payload.path) : roots[0] || process.cwd();
1796
+ const results = grepFiles(payload.pattern, searchRoot, roots);
1797
+ send(ws, { id: msg.id, type: "fs.grep.result", payload: { results } });
1798
+ break;
1799
+ }
1697
1800
  case "debug.logs": {
1698
1801
  send(ws, {
1699
1802
  id: msg.id,