@justmpm/ai-tool 1.0.0 → 1.0.1
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.
|
@@ -297,6 +297,29 @@ function recoveryHint(errorType, ctx, _extra) {
|
|
|
297
297
|
\u2192 ${hint("map", ctx)} - ver estrutura do projeto
|
|
298
298
|
\u2192 ${hint("find", ctx)} - buscar simbolos no codigo
|
|
299
299
|
\u2192 ${hint("areas", ctx)} - listar areas funcionais
|
|
300
|
+
`;
|
|
301
|
+
case "no_areas_configured":
|
|
302
|
+
return `
|
|
303
|
+
\u{1F4A1} Nenhuma area configurada neste projeto.
|
|
304
|
+
\u2192 ${hint("areas_init", ctx)} - gerar arquivo de configuracao
|
|
305
|
+
\u2192 Depois edite .analyze/areas.config.json com as areas do projeto
|
|
306
|
+
\u2192 ${hint("map", ctx)} - ver estrutura do projeto sem areas
|
|
307
|
+
`;
|
|
308
|
+
case "symbol_not_found":
|
|
309
|
+
return `
|
|
310
|
+
\u{1F4A1} Dicas:
|
|
311
|
+
\u2192 Verifique a ortografia do simbolo
|
|
312
|
+
\u2192 Tente buscar parte do nome
|
|
313
|
+
\u2192 ${hint("find", ctx)} - buscar com outro termo
|
|
314
|
+
\u2192 ${hint("describe", ctx)} - buscar areas por descricao
|
|
315
|
+
\u2192 ${hint("map", ctx)} - ver estrutura do projeto
|
|
316
|
+
`;
|
|
317
|
+
case "index_failed":
|
|
318
|
+
return `
|
|
319
|
+
\u{1F4A1} Falha ao indexar o projeto:
|
|
320
|
+
\u2192 Verifique se tsconfig.json existe e esta valido
|
|
321
|
+
\u2192 Verifique se o projeto tem arquivos .ts ou .tsx
|
|
322
|
+
\u2192 ${hint("map", ctx)} - tente ver a estrutura basica primeiro
|
|
300
323
|
`;
|
|
301
324
|
case "generic":
|
|
302
325
|
return `
|
|
@@ -373,6 +396,10 @@ function extractFileName(filePath) {
|
|
|
373
396
|
// src/formatters/text.ts
|
|
374
397
|
function formatMapSummary(result, areasInfo, ctx = "cli") {
|
|
375
398
|
let out = "";
|
|
399
|
+
if (result.framework) {
|
|
400
|
+
out += `\u{1F3D7}\uFE0F Framework: ${result.framework}
|
|
401
|
+
`;
|
|
402
|
+
}
|
|
376
403
|
out += `\u{1F4CA} ${result.summary.totalFiles} arquivos | ${result.summary.totalFolders} pastas
|
|
377
404
|
`;
|
|
378
405
|
const catOrder = [
|
|
@@ -399,11 +426,23 @@ function formatMapSummary(result, areasInfo, ctx = "cli") {
|
|
|
399
426
|
}
|
|
400
427
|
out += ` ${catParts.join(", ")}
|
|
401
428
|
`;
|
|
402
|
-
const
|
|
403
|
-
if (
|
|
404
|
-
|
|
405
|
-
|
|
429
|
+
const SMALL_PROJECT_THRESHOLD = 25;
|
|
430
|
+
if (result.summary.totalFiles <= SMALL_PROJECT_THRESHOLD && result.files.length > 0) {
|
|
431
|
+
out += `
|
|
432
|
+
\u{1F4C1} Arquivos:
|
|
406
433
|
`;
|
|
434
|
+
for (const file of result.files) {
|
|
435
|
+
const icon = categoryIcons[file.category];
|
|
436
|
+
out += ` ${icon} ${file.path}
|
|
437
|
+
`;
|
|
438
|
+
}
|
|
439
|
+
} else {
|
|
440
|
+
const topFolders = result.folders.sort((a, b) => b.fileCount - a.fileCount).slice(0, 5);
|
|
441
|
+
if (topFolders.length > 0) {
|
|
442
|
+
const folderParts = topFolders.map((f) => `${f.path}/ (${f.fileCount})`);
|
|
443
|
+
out += `\u{1F4C1} ${folderParts.join(", ")}
|
|
444
|
+
`;
|
|
445
|
+
}
|
|
407
446
|
}
|
|
408
447
|
if (areasInfo && areasInfo.total > 0) {
|
|
409
448
|
out += `
|
|
@@ -1020,7 +1059,36 @@ function formatContextText(result, ctx = "cli") {
|
|
|
1020
1059
|
out += ` Constants: ${result.constants.length}
|
|
1021
1060
|
`;
|
|
1022
1061
|
}
|
|
1023
|
-
out +=
|
|
1062
|
+
out += `
|
|
1063
|
+
\u{1F4D6} Proximos passos:
|
|
1064
|
+
`;
|
|
1065
|
+
const exportedFns = result.functions.filter((f) => f.isExported);
|
|
1066
|
+
const exportedTypes = result.types.filter((t) => t.isExported);
|
|
1067
|
+
if (exportedFns.length > 0) {
|
|
1068
|
+
const mainExport = exportedFns[0].name;
|
|
1069
|
+
out += ` \u2192 ${hint("find", ctx, { "<termo>": mainExport })} - ver onde ${mainExport} e usado
|
|
1070
|
+
`;
|
|
1071
|
+
}
|
|
1072
|
+
const isHook = result.category === "hook" || result.functions.some((f) => f.name.startsWith("use"));
|
|
1073
|
+
if (isHook) {
|
|
1074
|
+
out += ` \u2192 ${hint("impact", ctx, { "<arquivo>": result.file })} - ver quem usa este hook
|
|
1075
|
+
`;
|
|
1076
|
+
}
|
|
1077
|
+
if (result.category === "service") {
|
|
1078
|
+
out += ` \u2192 ${hint("impact", ctx, { "<arquivo>": result.file })} - ver quem usa este service
|
|
1079
|
+
`;
|
|
1080
|
+
}
|
|
1081
|
+
if (exportedTypes.length > 0) {
|
|
1082
|
+
const mainType = exportedTypes[0].name;
|
|
1083
|
+
out += ` \u2192 ${hint("find", ctx, { "<termo>": mainType })} - ver onde ${mainType} e usado
|
|
1084
|
+
`;
|
|
1085
|
+
}
|
|
1086
|
+
if (!isHook && result.category !== "service") {
|
|
1087
|
+
out += ` \u2192 ${hint("impact", ctx, { "<arquivo>": result.file })} - ver quem sera afetado por mudancas
|
|
1088
|
+
`;
|
|
1089
|
+
}
|
|
1090
|
+
out += ` \u2192 ${hint("suggest", ctx, { "<arquivo>": result.file })} - o que ler antes de editar
|
|
1091
|
+
`;
|
|
1024
1092
|
return out;
|
|
1025
1093
|
}
|
|
1026
1094
|
function formatAreasText(result, ctx = "cli") {
|
|
@@ -1995,10 +2063,37 @@ function formatOutput(result, format, textFormatter, cacheMarker) {
|
|
|
1995
2063
|
}
|
|
1996
2064
|
|
|
1997
2065
|
// src/commands/map.ts
|
|
2066
|
+
import { existsSync as existsSync4 } from "fs";
|
|
2067
|
+
import { join as join4 } from "path";
|
|
2068
|
+
function detectFramework(cwd) {
|
|
2069
|
+
if (existsSync4(join4(cwd, "next.config.js")) || existsSync4(join4(cwd, "next.config.ts")) || existsSync4(join4(cwd, "next.config.mjs"))) {
|
|
2070
|
+
return "Next.js";
|
|
2071
|
+
}
|
|
2072
|
+
if (existsSync4(join4(cwd, "vite.config.ts")) || existsSync4(join4(cwd, "vite.config.js"))) {
|
|
2073
|
+
return "Vite";
|
|
2074
|
+
}
|
|
2075
|
+
if (existsSync4(join4(cwd, "remix.config.js")) || existsSync4(join4(cwd, "remix.config.ts"))) {
|
|
2076
|
+
return "Remix";
|
|
2077
|
+
}
|
|
2078
|
+
if (existsSync4(join4(cwd, "astro.config.mjs")) || existsSync4(join4(cwd, "astro.config.ts"))) {
|
|
2079
|
+
return "Astro";
|
|
2080
|
+
}
|
|
2081
|
+
if (existsSync4(join4(cwd, "nuxt.config.ts")) || existsSync4(join4(cwd, "nuxt.config.js"))) {
|
|
2082
|
+
return "Nuxt";
|
|
2083
|
+
}
|
|
2084
|
+
if (existsSync4(join4(cwd, "svelte.config.js")) || existsSync4(join4(cwd, "svelte.config.ts"))) {
|
|
2085
|
+
return "SvelteKit";
|
|
2086
|
+
}
|
|
2087
|
+
if (existsSync4(join4(cwd, ".firebaserc")) || existsSync4(join4(cwd, "firebase.json"))) {
|
|
2088
|
+
return "Firebase";
|
|
2089
|
+
}
|
|
2090
|
+
return void 0;
|
|
2091
|
+
}
|
|
1998
2092
|
async function map(options = {}) {
|
|
1999
2093
|
const { cwd, format } = parseCommandOptions(options);
|
|
2000
2094
|
const useCache = options.cache !== false;
|
|
2001
2095
|
const full = options.full ?? false;
|
|
2096
|
+
const ctx = options.ctx || "cli";
|
|
2002
2097
|
if (useCache && isCacheValid(cwd)) {
|
|
2003
2098
|
const cached = getCachedMapResult(cwd);
|
|
2004
2099
|
if (cached) {
|
|
@@ -2007,10 +2102,10 @@ async function map(options = {}) {
|
|
|
2007
2102
|
return JSON.stringify(result, null, 2);
|
|
2008
2103
|
}
|
|
2009
2104
|
if (full) {
|
|
2010
|
-
return formatMapText(result) + "\n\n\u{1F4E6} (resultado do cache)";
|
|
2105
|
+
return formatMapText(result, ctx) + "\n\n\u{1F4E6} (resultado do cache)";
|
|
2011
2106
|
}
|
|
2012
2107
|
const areasInfo = detectAreasInfo(cwd, result.files.map((f) => f.path));
|
|
2013
|
-
return formatMapSummary(result, areasInfo) + "\n\u{1F4E6} (cache)";
|
|
2108
|
+
return formatMapSummary(result, areasInfo, ctx) + "\n\u{1F4E6} (cache)";
|
|
2014
2109
|
}
|
|
2015
2110
|
}
|
|
2016
2111
|
try {
|
|
@@ -2064,10 +2159,12 @@ async function map(options = {}) {
|
|
|
2064
2159
|
categories[file.category] = (categories[file.category] || 0) + 1;
|
|
2065
2160
|
}
|
|
2066
2161
|
const circular = findCircularDependencies();
|
|
2162
|
+
const framework = detectFramework(cwd);
|
|
2067
2163
|
const result = {
|
|
2068
2164
|
version: "1.0.0",
|
|
2069
2165
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2070
2166
|
cwd,
|
|
2167
|
+
framework,
|
|
2071
2168
|
summary: {
|
|
2072
2169
|
totalFiles: files.length,
|
|
2073
2170
|
totalFolders: folderMap.size,
|
|
@@ -2085,10 +2182,10 @@ async function map(options = {}) {
|
|
|
2085
2182
|
return JSON.stringify(result, null, 2);
|
|
2086
2183
|
}
|
|
2087
2184
|
if (full) {
|
|
2088
|
-
return formatMapText(result);
|
|
2185
|
+
return formatMapText(result, ctx);
|
|
2089
2186
|
}
|
|
2090
2187
|
const areasInfo = detectAreasInfo(cwd, files.map((f) => f.path));
|
|
2091
|
-
return formatMapSummary(result, areasInfo);
|
|
2188
|
+
return formatMapSummary(result, areasInfo, ctx);
|
|
2092
2189
|
} catch (error) {
|
|
2093
2190
|
const message = error instanceof Error ? error.message : String(error);
|
|
2094
2191
|
throw new Error(`Erro ao executar map: ${message}`);
|
|
@@ -2126,8 +2223,8 @@ function detectAreasInfo(cwd, filePaths) {
|
|
|
2126
2223
|
|
|
2127
2224
|
// src/commands/dead.ts
|
|
2128
2225
|
import { execSync } from "child_process";
|
|
2129
|
-
import { writeFileSync as writeFileSync3, unlinkSync, existsSync as
|
|
2130
|
-
import { join as
|
|
2226
|
+
import { writeFileSync as writeFileSync3, unlinkSync, existsSync as existsSync5 } from "fs";
|
|
2227
|
+
import { join as join5 } from "path";
|
|
2131
2228
|
function generateKnipConfig(cwd) {
|
|
2132
2229
|
const ignorePatterns = getIgnorePatterns(cwd);
|
|
2133
2230
|
if (ignorePatterns.length === 0) {
|
|
@@ -2137,7 +2234,7 @@ function generateKnipConfig(cwd) {
|
|
|
2137
2234
|
$schema: "https://unpkg.com/knip@5/schema.json",
|
|
2138
2235
|
ignore: ignorePatterns
|
|
2139
2236
|
};
|
|
2140
|
-
const configPath =
|
|
2237
|
+
const configPath = join5(cwd, ".knip.ai-tool.json");
|
|
2141
2238
|
writeFileSync3(configPath, JSON.stringify(knipConfig, null, 2), "utf-8");
|
|
2142
2239
|
return configPath;
|
|
2143
2240
|
}
|
|
@@ -2175,7 +2272,7 @@ async function dead(options = {}) {
|
|
|
2175
2272
|
knipOutput = {};
|
|
2176
2273
|
}
|
|
2177
2274
|
} finally {
|
|
2178
|
-
if (knipConfigPath &&
|
|
2275
|
+
if (knipConfigPath && existsSync5(knipConfigPath)) {
|
|
2179
2276
|
try {
|
|
2180
2277
|
unlinkSync(knipConfigPath);
|
|
2181
2278
|
} catch {
|
|
@@ -2258,29 +2355,33 @@ ${output}`;
|
|
|
2258
2355
|
import skott2 from "skott";
|
|
2259
2356
|
|
|
2260
2357
|
// src/utils/errors.ts
|
|
2261
|
-
var
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2358
|
+
var COMMAND_REFERENCE_KEYS = ["map", "areas", "area", "suggest", "context", "impact", "dead", "find", "describe"];
|
|
2359
|
+
var COMMAND_DESCRIPTIONS = {
|
|
2360
|
+
map: "Resumo do projeto",
|
|
2361
|
+
areas: "Listar todas as areas",
|
|
2362
|
+
area: "Arquivos de uma area especifica",
|
|
2265
2363
|
suggest: "O que ler antes de editar",
|
|
2266
2364
|
context: "API/assinaturas de um arquivo",
|
|
2267
2365
|
impact: "Quem usa este arquivo",
|
|
2268
|
-
dead: "
|
|
2366
|
+
dead: "Codigo morto",
|
|
2367
|
+
find: "Buscar simbolos no codigo",
|
|
2368
|
+
describe: "Buscar areas por descricao"
|
|
2269
2369
|
};
|
|
2270
|
-
function getCommandReferenceSection(excludeCommand) {
|
|
2370
|
+
function getCommandReferenceSection(ctx, excludeCommand) {
|
|
2271
2371
|
let out = `
|
|
2272
|
-
\u{1F4CC} Comandos
|
|
2372
|
+
\u{1F4CC} Comandos uteis:
|
|
2273
2373
|
`;
|
|
2274
|
-
for (const
|
|
2374
|
+
for (const cmd of COMMAND_REFERENCE_KEYS) {
|
|
2275
2375
|
if (cmd !== excludeCommand) {
|
|
2276
|
-
|
|
2376
|
+
const desc = COMMAND_DESCRIPTIONS[cmd];
|
|
2377
|
+
out += ` \u2192 ${hint(cmd, ctx)} - ${desc}
|
|
2277
2378
|
`;
|
|
2278
2379
|
}
|
|
2279
2380
|
}
|
|
2280
2381
|
return out;
|
|
2281
2382
|
}
|
|
2282
2383
|
function formatFileNotFound(options) {
|
|
2283
|
-
const { target, allFiles, command } = options;
|
|
2384
|
+
const { target, allFiles, command, ctx = "cli" } = options;
|
|
2284
2385
|
const similarFiles = findSimilar(target, allFiles, {
|
|
2285
2386
|
maxDistance: 3,
|
|
2286
2387
|
limit: 5,
|
|
@@ -2288,14 +2389,20 @@ function formatFileNotFound(options) {
|
|
|
2288
2389
|
});
|
|
2289
2390
|
const bestMatch = findBestMatch(target, allFiles, extractFileName);
|
|
2290
2391
|
let out = `
|
|
2291
|
-
\u274C Arquivo
|
|
2392
|
+
\u274C Arquivo nao encontrado: "${target}"
|
|
2292
2393
|
|
|
2293
2394
|
`;
|
|
2294
2395
|
out += `\u{1F4CA} Total de arquivos indexados: ${allFiles.length}
|
|
2295
2396
|
|
|
2296
2397
|
`;
|
|
2297
|
-
if (bestMatch) {
|
|
2298
|
-
out += `\u{1F4A1}
|
|
2398
|
+
if (bestMatch && command) {
|
|
2399
|
+
out += `\u{1F4A1} Voce quis dizer?
|
|
2400
|
+
`;
|
|
2401
|
+
out += ` \u2192 ${hint(command, ctx, { "<arquivo>": bestMatch, "<termo>": bestMatch })}
|
|
2402
|
+
|
|
2403
|
+
`;
|
|
2404
|
+
} else if (bestMatch) {
|
|
2405
|
+
out += `\u{1F4A1} Voce quis dizer?
|
|
2299
2406
|
`;
|
|
2300
2407
|
out += ` \u2192 ${bestMatch}
|
|
2301
2408
|
|
|
@@ -2318,15 +2425,15 @@ function formatFileNotFound(options) {
|
|
|
2318
2425
|
`;
|
|
2319
2426
|
out += ` \u2022 Ou apenas o nome do arquivo: Header
|
|
2320
2427
|
`;
|
|
2321
|
-
out += ` \u2022 Verifique se o arquivo
|
|
2428
|
+
out += ` \u2022 Verifique se o arquivo esta em uma pasta incluida no scan
|
|
2322
2429
|
`;
|
|
2323
2430
|
if (command) {
|
|
2324
|
-
out += getCommandReferenceSection(command);
|
|
2431
|
+
out += getCommandReferenceSection(ctx, command);
|
|
2325
2432
|
}
|
|
2326
2433
|
return out;
|
|
2327
2434
|
}
|
|
2328
2435
|
function formatAreaNotFound(options) {
|
|
2329
|
-
const { target, availableAreas } = options;
|
|
2436
|
+
const { target, availableAreas, ctx = "cli" } = options;
|
|
2330
2437
|
const areaIds = availableAreas.map((a) => a.id);
|
|
2331
2438
|
const bestMatchId = findBestMatch(target, areaIds);
|
|
2332
2439
|
const similarAreaIds = findSimilar(target, areaIds, {
|
|
@@ -2334,18 +2441,18 @@ function formatAreaNotFound(options) {
|
|
|
2334
2441
|
limit: 5
|
|
2335
2442
|
});
|
|
2336
2443
|
let out = `
|
|
2337
|
-
\u274C
|
|
2444
|
+
\u274C Area nao encontrada: "${target}"
|
|
2338
2445
|
|
|
2339
2446
|
`;
|
|
2340
2447
|
if (bestMatchId) {
|
|
2341
|
-
out += `\u{1F4A1}
|
|
2448
|
+
out += `\u{1F4A1} Voce quis dizer?
|
|
2342
2449
|
`;
|
|
2343
|
-
out += ` \u2192
|
|
2450
|
+
out += ` \u2192 ${hint("area", ctx, { "<nome>": bestMatchId })}
|
|
2344
2451
|
|
|
2345
2452
|
`;
|
|
2346
2453
|
}
|
|
2347
2454
|
if (availableAreas.length > 0) {
|
|
2348
|
-
out += `\u{1F4E6}
|
|
2455
|
+
out += `\u{1F4E6} Areas disponiveis:
|
|
2349
2456
|
|
|
2350
2457
|
`;
|
|
2351
2458
|
if (similarAreaIds.length > 0 && !bestMatchId) {
|
|
@@ -2374,67 +2481,57 @@ function formatAreaNotFound(options) {
|
|
|
2374
2481
|
}
|
|
2375
2482
|
out += `\u{1F4D6} Dicas:
|
|
2376
2483
|
`;
|
|
2377
|
-
out += ` \
|
|
2484
|
+
out += ` \u2192 ${hint("areas", ctx)} - listar todas as areas
|
|
2378
2485
|
`;
|
|
2379
|
-
out += ` \
|
|
2486
|
+
out += ` \u2192 ${hint("describe", ctx)} - buscar areas por descricao
|
|
2380
2487
|
`;
|
|
2381
|
-
out += ` \
|
|
2382
|
-
`;
|
|
2383
|
-
out += `
|
|
2384
|
-
\u{1F4CC} Comandos relacionados:
|
|
2385
|
-
`;
|
|
2386
|
-
out += ` ai-tool areas Listar todas as \xE1reas
|
|
2387
|
-
`;
|
|
2388
|
-
out += ` ai-tool map Ver estrutura do projeto
|
|
2488
|
+
out += ` \u2192 IDs sao case-sensitive (Auth \u2260 auth)
|
|
2389
2489
|
`;
|
|
2390
2490
|
return out;
|
|
2391
2491
|
}
|
|
2392
|
-
function formatMissingTarget(command) {
|
|
2492
|
+
function formatMissingTarget(command, ctx = "cli") {
|
|
2393
2493
|
let out = `
|
|
2394
|
-
\u274C Erro:
|
|
2494
|
+
\u274C Erro: parametro "target" e OBRIGATORIO para o comando "${command}".
|
|
2395
2495
|
|
|
2396
2496
|
`;
|
|
2397
2497
|
out += `\u{1F4DD} Exemplos:
|
|
2398
2498
|
`;
|
|
2399
2499
|
if (command === "area") {
|
|
2400
|
-
out += `
|
|
2500
|
+
out += ` ${hint("area", ctx, { "<nome>": "auth" })}
|
|
2401
2501
|
`;
|
|
2402
|
-
out += `
|
|
2403
|
-
`;
|
|
2404
|
-
out += ` ai-tool area billing --type=hook
|
|
2502
|
+
out += ` ${hint("area", ctx, { "<nome>": "dashboard" })}
|
|
2405
2503
|
|
|
2406
2504
|
`;
|
|
2407
|
-
out += `\u{1F4A1}
|
|
2505
|
+
out += `\u{1F4A1} ${hint("areas", ctx)} - listar todas as areas disponiveis
|
|
2408
2506
|
`;
|
|
2409
2507
|
} else {
|
|
2410
|
-
out += `
|
|
2411
|
-
`;
|
|
2412
|
-
out += ` ai-tool ${command} Button.tsx
|
|
2508
|
+
out += ` ${hint(command, ctx, { "<arquivo>": "useAuth", "<termo>": "useAuth" })}
|
|
2413
2509
|
`;
|
|
2414
|
-
out += `
|
|
2510
|
+
out += ` ${hint(command, ctx, { "<arquivo>": "Button.tsx", "<termo>": "Button" })}
|
|
2415
2511
|
`;
|
|
2416
2512
|
}
|
|
2417
|
-
out += getCommandReferenceSection(command);
|
|
2513
|
+
out += getCommandReferenceSection(ctx, command);
|
|
2418
2514
|
return out;
|
|
2419
2515
|
}
|
|
2420
|
-
function formatInvalidCommand(command) {
|
|
2421
|
-
const validCommands = Object.keys(
|
|
2516
|
+
function formatInvalidCommand(command, ctx = "cli") {
|
|
2517
|
+
const validCommands = Object.keys(COMMAND_DESCRIPTIONS);
|
|
2422
2518
|
const bestMatch = findBestMatch(command, validCommands);
|
|
2423
2519
|
let out = `
|
|
2424
|
-
\u274C Comando
|
|
2520
|
+
\u274C Comando invalido: "${command}"
|
|
2425
2521
|
|
|
2426
2522
|
`;
|
|
2427
2523
|
if (bestMatch) {
|
|
2428
|
-
out += `\u{1F4A1}
|
|
2524
|
+
out += `\u{1F4A1} Voce quis dizer?
|
|
2429
2525
|
`;
|
|
2430
|
-
out += ` \u2192
|
|
2526
|
+
out += ` \u2192 ${hint(bestMatch, ctx)}
|
|
2431
2527
|
|
|
2432
2528
|
`;
|
|
2433
2529
|
}
|
|
2434
|
-
out += `\u{1F4CC} Comandos
|
|
2530
|
+
out += `\u{1F4CC} Comandos disponiveis:
|
|
2435
2531
|
`;
|
|
2436
|
-
for (const
|
|
2437
|
-
|
|
2532
|
+
for (const cmd of COMMAND_REFERENCE_KEYS) {
|
|
2533
|
+
const desc = COMMAND_DESCRIPTIONS[cmd];
|
|
2534
|
+
out += ` \u2192 ${hint(cmd, ctx)} - ${desc}
|
|
2438
2535
|
`;
|
|
2439
2536
|
}
|
|
2440
2537
|
return out;
|
|
@@ -2490,10 +2587,10 @@ function findTargetFile(target, allFiles) {
|
|
|
2490
2587
|
}
|
|
2491
2588
|
|
|
2492
2589
|
// src/integrations/git.ts
|
|
2493
|
-
import { existsSync as
|
|
2590
|
+
import { existsSync as existsSync6 } from "fs";
|
|
2494
2591
|
import { execSync as execSync2 } from "child_process";
|
|
2495
2592
|
function hasGitRepo(cwd) {
|
|
2496
|
-
return
|
|
2593
|
+
return existsSync6(cwd + "/.git");
|
|
2497
2594
|
}
|
|
2498
2595
|
async function getCommitsForFile(filePath, cwd, limit = 10) {
|
|
2499
2596
|
if (!hasGitRepo(cwd)) {
|
|
@@ -2532,6 +2629,7 @@ async function getCommitsForFile(filePath, cwd, limit = 10) {
|
|
|
2532
2629
|
async function impact(target, options = {}) {
|
|
2533
2630
|
const { cwd, format } = parseCommandOptions(options);
|
|
2534
2631
|
const useCache = options.cache !== false;
|
|
2632
|
+
const ctx = options.ctx || "cli";
|
|
2535
2633
|
if (!target) {
|
|
2536
2634
|
throw new Error("Target \xE9 obrigat\xF3rio. Exemplo: ai-tool impact src/components/Button.tsx");
|
|
2537
2635
|
}
|
|
@@ -2577,7 +2675,7 @@ async function impact(target, options = {}) {
|
|
|
2577
2675
|
}
|
|
2578
2676
|
const targetPath = findTargetFile(target, allFiles);
|
|
2579
2677
|
if (!targetPath) {
|
|
2580
|
-
return formatFileNotFound({ target, allFiles, command: "impact" });
|
|
2678
|
+
return formatFileNotFound({ target, allFiles, command: "impact", ctx });
|
|
2581
2679
|
}
|
|
2582
2680
|
const { directUpstream, indirectUpstream, directDownstream, indirectDownstream } = calculateDependencies(targetPath, graph);
|
|
2583
2681
|
const dependingOn = [...directUpstream, ...indirectUpstream];
|
|
@@ -2610,7 +2708,7 @@ async function impact(target, options = {}) {
|
|
|
2610
2708
|
suggestions,
|
|
2611
2709
|
gitHistory
|
|
2612
2710
|
};
|
|
2613
|
-
return formatOutput(result, format, formatImpactText, fromCache);
|
|
2711
|
+
return formatOutput(result, format, (r) => formatImpactText(r, ctx), fromCache);
|
|
2614
2712
|
} catch (error) {
|
|
2615
2713
|
const message = error instanceof Error ? error.message : String(error);
|
|
2616
2714
|
throw new Error(`Erro ao executar impact: ${message}`);
|
|
@@ -2772,6 +2870,7 @@ async function suggest(target, options = {}) {
|
|
|
2772
2870
|
const { cwd, format } = parseCommandOptions(options);
|
|
2773
2871
|
const useCache = options.cache !== false;
|
|
2774
2872
|
const limit = options.limit || 10;
|
|
2873
|
+
const ctx = options.ctx || "cli";
|
|
2775
2874
|
if (!target) {
|
|
2776
2875
|
throw new Error("Target e obrigatorio. Exemplo: ai-tool suggest src/components/Button.tsx");
|
|
2777
2876
|
}
|
|
@@ -2813,7 +2912,7 @@ async function suggest(target, options = {}) {
|
|
|
2813
2912
|
}
|
|
2814
2913
|
const targetPath = findTargetFile(target, allFiles);
|
|
2815
2914
|
if (!targetPath) {
|
|
2816
|
-
return formatFileNotFound({ target, allFiles, command: "suggest" });
|
|
2915
|
+
return formatFileNotFound({ target, allFiles, command: "suggest", ctx });
|
|
2817
2916
|
}
|
|
2818
2917
|
const suggestions = collectSuggestions(targetPath, graph, allFiles, limit);
|
|
2819
2918
|
const testSuggestions = generateTestSuggestions(suggestions, allFiles);
|
|
@@ -2825,7 +2924,7 @@ async function suggest(target, options = {}) {
|
|
|
2825
2924
|
suggestions,
|
|
2826
2925
|
testSuggestions
|
|
2827
2926
|
};
|
|
2828
|
-
return formatOutput(result, format, formatSuggestText, fromCache);
|
|
2927
|
+
return formatOutput(result, format, (r) => formatSuggestText(r, ctx), fromCache);
|
|
2829
2928
|
} catch (error) {
|
|
2830
2929
|
const message = error instanceof Error ? error.message : String(error);
|
|
2831
2930
|
throw new Error(`Erro ao executar suggest: ${message}`);
|
|
@@ -3020,8 +3119,8 @@ function generateTestSuggestions(suggestions, allFiles) {
|
|
|
3020
3119
|
}
|
|
3021
3120
|
|
|
3022
3121
|
// src/commands/context.ts
|
|
3023
|
-
import { existsSync as
|
|
3024
|
-
import { join as
|
|
3122
|
+
import { existsSync as existsSync7, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
|
|
3123
|
+
import { join as join7, resolve as resolve2, basename, extname as extname3 } from "path";
|
|
3025
3124
|
import { SyntaxKind as SyntaxKind3 } from "ts-morph";
|
|
3026
3125
|
|
|
3027
3126
|
// src/ts/extractor.ts
|
|
@@ -3257,7 +3356,7 @@ import { SyntaxKind as SyntaxKind2 } from "ts-morph";
|
|
|
3257
3356
|
|
|
3258
3357
|
// src/ts/index.ts
|
|
3259
3358
|
import { readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
3260
|
-
import { join as
|
|
3359
|
+
import { join as join6, extname as extname2 } from "path";
|
|
3261
3360
|
import { Project as Project2 } from "ts-morph";
|
|
3262
3361
|
|
|
3263
3362
|
// src/utils/logger.ts
|
|
@@ -3372,7 +3471,7 @@ function getAllCodeFiles(dir, files = [], baseDir = dir) {
|
|
|
3372
3471
|
try {
|
|
3373
3472
|
const entries = readdirSync2(dir);
|
|
3374
3473
|
for (const entry of entries) {
|
|
3375
|
-
const fullPath =
|
|
3474
|
+
const fullPath = join6(dir, entry);
|
|
3376
3475
|
if (IGNORED_DIRS.has(entry) || entry.startsWith(".")) {
|
|
3377
3476
|
continue;
|
|
3378
3477
|
}
|
|
@@ -3888,6 +3987,7 @@ function indexProject(cwd) {
|
|
|
3888
3987
|
// src/commands/context.ts
|
|
3889
3988
|
async function context(target, options = {}) {
|
|
3890
3989
|
const { cwd, format } = parseCommandOptions(options);
|
|
3990
|
+
const ctx = options.ctx || "cli";
|
|
3891
3991
|
if (!target) {
|
|
3892
3992
|
throw new Error("Target e obrigatorio. Exemplo: ai-tool context src/components/Button.tsx");
|
|
3893
3993
|
}
|
|
@@ -3895,7 +3995,7 @@ async function context(target, options = {}) {
|
|
|
3895
3995
|
const targetPath = findTargetFile2(target, cwd);
|
|
3896
3996
|
if (!targetPath) {
|
|
3897
3997
|
const allFiles = getAllCodeFiles2(cwd);
|
|
3898
|
-
return formatFileNotFound({ target, allFiles, command: "context" });
|
|
3998
|
+
return formatFileNotFound({ target, allFiles, command: "context", ctx });
|
|
3899
3999
|
}
|
|
3900
4000
|
const project = createProject(cwd);
|
|
3901
4001
|
const absolutePath = resolve2(cwd, targetPath);
|
|
@@ -3933,7 +4033,7 @@ async function context(target, options = {}) {
|
|
|
3933
4033
|
types,
|
|
3934
4034
|
constants: constants.length > 0 ? constants : void 0
|
|
3935
4035
|
};
|
|
3936
|
-
return formatOutput(result, format, formatContextText);
|
|
4036
|
+
return formatOutput(result, format, (r) => formatContextText(r, ctx));
|
|
3937
4037
|
} catch (error) {
|
|
3938
4038
|
const message = error instanceof Error ? error.message : String(error);
|
|
3939
4039
|
throw new Error(`Erro ao executar context: ${message}`);
|
|
@@ -3943,12 +4043,12 @@ var CODE_EXTENSIONS3 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".
|
|
|
3943
4043
|
function findTargetFile2(target, cwd) {
|
|
3944
4044
|
const normalizedTarget = target.replace(/\\/g, "/");
|
|
3945
4045
|
const directPath = resolve2(cwd, normalizedTarget);
|
|
3946
|
-
if (
|
|
4046
|
+
if (existsSync7(directPath) && isCodeFile2(directPath)) {
|
|
3947
4047
|
return normalizedTarget;
|
|
3948
4048
|
}
|
|
3949
4049
|
for (const ext of CODE_EXTENSIONS3) {
|
|
3950
4050
|
const withExt = directPath + ext;
|
|
3951
|
-
if (
|
|
4051
|
+
if (existsSync7(withExt)) {
|
|
3952
4052
|
return normalizedTarget + ext;
|
|
3953
4053
|
}
|
|
3954
4054
|
}
|
|
@@ -3978,7 +4078,7 @@ function getAllCodeFiles2(dir, files = [], baseDir = dir) {
|
|
|
3978
4078
|
try {
|
|
3979
4079
|
const entries = readdirSync3(dir);
|
|
3980
4080
|
for (const entry of entries) {
|
|
3981
|
-
const fullPath =
|
|
4081
|
+
const fullPath = join7(dir, entry);
|
|
3982
4082
|
if (shouldIgnore(entry)) {
|
|
3983
4083
|
continue;
|
|
3984
4084
|
}
|
|
@@ -4015,10 +4115,32 @@ async function areaContext(areaName, options = {}) {
|
|
|
4015
4115
|
const cwd = options.cwd || process.cwd();
|
|
4016
4116
|
const format = options.format || "text";
|
|
4017
4117
|
const useCache = options.cache !== false;
|
|
4118
|
+
const ctx = options.ctx || "cli";
|
|
4018
4119
|
if (!areaName) {
|
|
4019
4120
|
throw new Error("Nome da \xE1rea \xE9 obrigat\xF3rio. Exemplo: ai-tool context --area=auth");
|
|
4020
4121
|
}
|
|
4021
4122
|
try {
|
|
4123
|
+
if (!configExists(cwd) || Object.keys(readConfig(cwd).areas).length === 0) {
|
|
4124
|
+
let out = `\u26A0\uFE0F Nenhuma area configurada neste projeto.
|
|
4125
|
+
|
|
4126
|
+
`;
|
|
4127
|
+
out += `O comando area_context requer areas configuradas em .analyze/areas.config.json
|
|
4128
|
+
`;
|
|
4129
|
+
out += `Para configurar:
|
|
4130
|
+
`;
|
|
4131
|
+
out += ` 1. ${hint("areas_init", ctx)} - gerar arquivo de configuracao
|
|
4132
|
+
`;
|
|
4133
|
+
out += ` 2. Edite .analyze/areas.config.json com as areas do projeto
|
|
4134
|
+
|
|
4135
|
+
`;
|
|
4136
|
+
out += `Enquanto isso, use:
|
|
4137
|
+
`;
|
|
4138
|
+
out += ` \u2192 ${hint("context", ctx)} - ver assinaturas de um arquivo especifico
|
|
4139
|
+
`;
|
|
4140
|
+
out += ` \u2192 ${hint("find", ctx)} - buscar simbolos no codigo
|
|
4141
|
+
`;
|
|
4142
|
+
return out;
|
|
4143
|
+
}
|
|
4022
4144
|
const config = readConfig(cwd);
|
|
4023
4145
|
let index;
|
|
4024
4146
|
if (useCache && isCacheValid(cwd)) {
|
|
@@ -4062,7 +4184,7 @@ async function areaContext(areaName, options = {}) {
|
|
|
4062
4184
|
if (format === "json") {
|
|
4063
4185
|
return JSON.stringify({ error: `Area nao encontrada: "${areaName}"`, availableAreas: areaList });
|
|
4064
4186
|
}
|
|
4065
|
-
return formatAreaNotFound({ target: areaName, availableAreas: areaList });
|
|
4187
|
+
return formatAreaNotFound({ target: areaName, availableAreas: areaList, ctx });
|
|
4066
4188
|
}
|
|
4067
4189
|
const types = [];
|
|
4068
4190
|
const hooks = [];
|
|
@@ -4168,7 +4290,7 @@ async function areaContext(areaName, options = {}) {
|
|
|
4168
4290
|
if (format === "json") {
|
|
4169
4291
|
return JSON.stringify(result, null, 2);
|
|
4170
4292
|
}
|
|
4171
|
-
return formatAreaContextText(result);
|
|
4293
|
+
return formatAreaContextText(result, ctx);
|
|
4172
4294
|
} catch (error) {
|
|
4173
4295
|
const message = error instanceof Error ? error.message : String(error);
|
|
4174
4296
|
throw new Error(`Erro ao executar context --area: ${message}`);
|
|
@@ -4195,7 +4317,7 @@ function findRealAreaIdFromIndex(target, areaFiles, config) {
|
|
|
4195
4317
|
|
|
4196
4318
|
// src/commands/areas.ts
|
|
4197
4319
|
import { readdirSync as readdirSync4, statSync as statSync4 } from "fs";
|
|
4198
|
-
import { join as
|
|
4320
|
+
import { join as join8, extname as extname4 } from "path";
|
|
4199
4321
|
var CODE_EXTENSIONS4 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
4200
4322
|
var IGNORED_DIRS2 = /* @__PURE__ */ new Set([
|
|
4201
4323
|
"node_modules",
|
|
@@ -4212,6 +4334,7 @@ var IGNORED_DIRS2 = /* @__PURE__ */ new Set([
|
|
|
4212
4334
|
async function areas(options = {}) {
|
|
4213
4335
|
const cwd = options.cwd || process.cwd();
|
|
4214
4336
|
const format = options.format || "text";
|
|
4337
|
+
const ctx = options.ctx || "cli";
|
|
4215
4338
|
try {
|
|
4216
4339
|
const config = readConfig(cwd);
|
|
4217
4340
|
const allFiles = getAllCodeFiles3(cwd);
|
|
@@ -4273,7 +4396,7 @@ async function areas(options = {}) {
|
|
|
4273
4396
|
if (format === "json") {
|
|
4274
4397
|
return JSON.stringify(result, null, 2);
|
|
4275
4398
|
}
|
|
4276
|
-
return formatAreasText(result);
|
|
4399
|
+
return formatAreasText(result, ctx);
|
|
4277
4400
|
} catch (error) {
|
|
4278
4401
|
const message = error instanceof Error ? error.message : String(error);
|
|
4279
4402
|
throw new Error(`Erro ao executar areas: ${message}`);
|
|
@@ -4283,7 +4406,7 @@ function getAllCodeFiles3(dir, files = [], baseDir = dir) {
|
|
|
4283
4406
|
try {
|
|
4284
4407
|
const entries = readdirSync4(dir);
|
|
4285
4408
|
for (const entry of entries) {
|
|
4286
|
-
const fullPath =
|
|
4409
|
+
const fullPath = join8(dir, entry);
|
|
4287
4410
|
if (IGNORED_DIRS2.has(entry) || entry.startsWith(".")) {
|
|
4288
4411
|
continue;
|
|
4289
4412
|
}
|
|
@@ -4308,7 +4431,7 @@ function getAllCodeFiles3(dir, files = [], baseDir = dir) {
|
|
|
4308
4431
|
|
|
4309
4432
|
// src/commands/area.ts
|
|
4310
4433
|
import { readdirSync as readdirSync5, statSync as statSync5 } from "fs";
|
|
4311
|
-
import { join as
|
|
4434
|
+
import { join as join9, extname as extname5 } from "path";
|
|
4312
4435
|
var CODE_EXTENSIONS5 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
4313
4436
|
var IGNORED_DIRS3 = /* @__PURE__ */ new Set([
|
|
4314
4437
|
"node_modules",
|
|
@@ -4357,10 +4480,32 @@ async function area(target, options = {}) {
|
|
|
4357
4480
|
const format = options.format || "text";
|
|
4358
4481
|
const filterType = options.type;
|
|
4359
4482
|
const full = options.full ?? false;
|
|
4483
|
+
const ctx = options.ctx || "cli";
|
|
4360
4484
|
if (!target) {
|
|
4361
4485
|
throw new Error("Nome da \xE1rea \xE9 obrigat\xF3rio. Exemplo: ai-tool area auth");
|
|
4362
4486
|
}
|
|
4363
4487
|
try {
|
|
4488
|
+
if (!configExists(cwd) || Object.keys(readConfig(cwd).areas).length === 0) {
|
|
4489
|
+
let out = `\u26A0\uFE0F Nenhuma area configurada neste projeto.
|
|
4490
|
+
|
|
4491
|
+
`;
|
|
4492
|
+
out += `O comando area requer areas configuradas em .analyze/areas.config.json
|
|
4493
|
+
`;
|
|
4494
|
+
out += `Para configurar:
|
|
4495
|
+
`;
|
|
4496
|
+
out += ` 1. ${hint("areas_init", ctx)} - gerar arquivo de configuracao
|
|
4497
|
+
`;
|
|
4498
|
+
out += ` 2. Edite .analyze/areas.config.json com as areas do projeto
|
|
4499
|
+
|
|
4500
|
+
`;
|
|
4501
|
+
out += `Enquanto isso, use:
|
|
4502
|
+
`;
|
|
4503
|
+
out += ` \u2192 ${hint("map", ctx)} - ver estrutura do projeto
|
|
4504
|
+
`;
|
|
4505
|
+
out += ` \u2192 ${hint("find", ctx)} - buscar simbolos no codigo
|
|
4506
|
+
`;
|
|
4507
|
+
return out;
|
|
4508
|
+
}
|
|
4364
4509
|
const config = readConfig(cwd);
|
|
4365
4510
|
const allFiles = getAllCodeFiles4(cwd);
|
|
4366
4511
|
const resolvedTarget = resolveAreaId(target, config, allFiles);
|
|
@@ -4390,7 +4535,7 @@ async function area(target, options = {}) {
|
|
|
4390
4535
|
}
|
|
4391
4536
|
if (areaFiles.length === 0) {
|
|
4392
4537
|
const availableAreas = getAvailableAreas(filteredFiles, config);
|
|
4393
|
-
return formatAreaNotFound2(target, availableAreas);
|
|
4538
|
+
return formatAreaNotFound2(target, availableAreas, ctx);
|
|
4394
4539
|
}
|
|
4395
4540
|
const byCategory = {};
|
|
4396
4541
|
const categories = {};
|
|
@@ -4427,7 +4572,7 @@ async function area(target, options = {}) {
|
|
|
4427
4572
|
if (format === "json") {
|
|
4428
4573
|
return JSON.stringify(result, null, 2);
|
|
4429
4574
|
}
|
|
4430
|
-
const output = formatAreaDetailText(result, { full, filterType });
|
|
4575
|
+
const output = formatAreaDetailText(result, { full, filterType }, ctx);
|
|
4431
4576
|
return nameConversionMsg + output;
|
|
4432
4577
|
} catch (error) {
|
|
4433
4578
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -4462,14 +4607,14 @@ function getAvailableAreas(allFiles, config) {
|
|
|
4462
4607
|
}
|
|
4463
4608
|
return [...areaCounts.entries()].map(([id, count]) => ({ id, count })).sort((a, b) => b.count - a.count);
|
|
4464
4609
|
}
|
|
4465
|
-
function formatAreaNotFound2(target, availableAreas) {
|
|
4466
|
-
return formatAreaNotFound({ target, availableAreas });
|
|
4610
|
+
function formatAreaNotFound2(target, availableAreas, ctx = "cli") {
|
|
4611
|
+
return formatAreaNotFound({ target, availableAreas, ctx });
|
|
4467
4612
|
}
|
|
4468
4613
|
function getAllCodeFiles4(dir, files = [], baseDir = dir) {
|
|
4469
4614
|
try {
|
|
4470
4615
|
const entries = readdirSync5(dir);
|
|
4471
4616
|
for (const entry of entries) {
|
|
4472
|
-
const fullPath =
|
|
4617
|
+
const fullPath = join9(dir, entry);
|
|
4473
4618
|
if (IGNORED_DIRS3.has(entry) || entry.startsWith(".")) {
|
|
4474
4619
|
continue;
|
|
4475
4620
|
}
|
|
@@ -4494,7 +4639,7 @@ function getAllCodeFiles4(dir, files = [], baseDir = dir) {
|
|
|
4494
4639
|
|
|
4495
4640
|
// src/commands/areas-init.ts
|
|
4496
4641
|
import { readdirSync as readdirSync6, statSync as statSync6 } from "fs";
|
|
4497
|
-
import { join as
|
|
4642
|
+
import { join as join10, extname as extname6 } from "path";
|
|
4498
4643
|
var CODE_EXTENSIONS6 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
4499
4644
|
var IGNORED_DIRS4 = /* @__PURE__ */ new Set([
|
|
4500
4645
|
"node_modules",
|
|
@@ -4510,7 +4655,7 @@ var IGNORED_DIRS4 = /* @__PURE__ */ new Set([
|
|
|
4510
4655
|
]);
|
|
4511
4656
|
function createInitialConfig(files) {
|
|
4512
4657
|
const suggestedIgnore = detectSuggestedIgnorePatterns(files);
|
|
4513
|
-
const framework =
|
|
4658
|
+
const framework = detectFramework2(files);
|
|
4514
4659
|
return {
|
|
4515
4660
|
$schema: "./areas.schema.json",
|
|
4516
4661
|
version: "1.0.0",
|
|
@@ -4524,7 +4669,7 @@ function createInitialConfig(files) {
|
|
|
4524
4669
|
}
|
|
4525
4670
|
};
|
|
4526
4671
|
}
|
|
4527
|
-
function
|
|
4672
|
+
function detectFramework2(files) {
|
|
4528
4673
|
const hasAppDir = files.some((f) => f.startsWith("app/"));
|
|
4529
4674
|
const hasPagesDir = files.some((f) => f.startsWith("pages/"));
|
|
4530
4675
|
const hasSrcDir = files.some((f) => f.startsWith("src/"));
|
|
@@ -4679,7 +4824,7 @@ Ou edite manualmente o arquivo existente.
|
|
|
4679
4824
|
const allFiles = getAllCodeFiles5(cwd);
|
|
4680
4825
|
const newConfig = createInitialConfig(allFiles);
|
|
4681
4826
|
writeConfig(cwd, newConfig);
|
|
4682
|
-
const framework =
|
|
4827
|
+
const framework = detectFramework2(allFiles);
|
|
4683
4828
|
const frameworkName = {
|
|
4684
4829
|
"nextjs-app": "Next.js (App Router)",
|
|
4685
4830
|
"nextjs-pages": "Next.js (Pages Router)",
|
|
@@ -4783,7 +4928,7 @@ function getAllCodeFiles5(dir, files = [], baseDir = dir) {
|
|
|
4783
4928
|
try {
|
|
4784
4929
|
const entries = readdirSync6(dir);
|
|
4785
4930
|
for (const entry of entries) {
|
|
4786
|
-
const fullPath =
|
|
4931
|
+
const fullPath = join10(dir, entry);
|
|
4787
4932
|
if (IGNORED_DIRS4.has(entry) || entry.startsWith(".")) {
|
|
4788
4933
|
continue;
|
|
4789
4934
|
}
|
|
@@ -4814,6 +4959,7 @@ async function functions(options = {}) {
|
|
|
4814
4959
|
const format = options.format || "text";
|
|
4815
4960
|
const useCache = options.cache !== false;
|
|
4816
4961
|
const filterTrigger = options.trigger;
|
|
4962
|
+
const ctx = options.ctx || "cli";
|
|
4817
4963
|
if (!isFirebaseProject(cwd)) {
|
|
4818
4964
|
const errorMsg = "Este nao e um projeto Firebase (nao encontrou .firebaserc ou firebase.json)";
|
|
4819
4965
|
if (format === "json") {
|
|
@@ -4824,11 +4970,11 @@ async function functions(options = {}) {
|
|
|
4824
4970
|
out += `
|
|
4825
4971
|
\u{1F4A1} Comandos disponiveis para este projeto:
|
|
4826
4972
|
`;
|
|
4827
|
-
out += ` \u2192
|
|
4973
|
+
out += ` \u2192 ${hint("map", ctx)} - ver estrutura do projeto
|
|
4828
4974
|
`;
|
|
4829
|
-
out += ` \u2192
|
|
4975
|
+
out += ` \u2192 ${hint("find", ctx)} - buscar simbolos no codigo
|
|
4830
4976
|
`;
|
|
4831
|
-
out += ` \u2192
|
|
4977
|
+
out += ` \u2192 ${hint("areas", ctx)} - listar areas funcionais
|
|
4832
4978
|
`;
|
|
4833
4979
|
return out;
|
|
4834
4980
|
}
|
|
@@ -4915,13 +5061,13 @@ async function functions(options = {}) {
|
|
|
4915
5061
|
if (format === "json") {
|
|
4916
5062
|
return JSON.stringify(result, null, 2);
|
|
4917
5063
|
}
|
|
4918
|
-
return formatFunctionsText(result);
|
|
5064
|
+
return formatFunctionsText(result, ctx);
|
|
4919
5065
|
} catch (error) {
|
|
4920
5066
|
const message = error instanceof Error ? error.message : String(error);
|
|
4921
5067
|
throw new Error(`Erro ao executar functions: ${message}`);
|
|
4922
5068
|
}
|
|
4923
5069
|
}
|
|
4924
|
-
function formatFunctionsText(result) {
|
|
5070
|
+
function formatFunctionsText(result, ctx = "cli") {
|
|
4925
5071
|
let out = "";
|
|
4926
5072
|
out += `
|
|
4927
5073
|
`;
|
|
@@ -4938,42 +5084,35 @@ function formatFunctionsText(result) {
|
|
|
4938
5084
|
`;
|
|
4939
5085
|
out += ` Exportadas: ${result.summary.exported}
|
|
4940
5086
|
`;
|
|
4941
|
-
if (result.summary.total > 0) {
|
|
4942
|
-
out += `
|
|
4943
|
-
\u{1F4A1} Filtros dispon\xEDveis:
|
|
4944
|
-
`;
|
|
4945
|
-
out += ` ai-tool functions --trigger=onCall
|
|
4946
|
-
`;
|
|
4947
|
-
out += ` ai-tool functions --trigger=onDocumentCreated
|
|
4948
|
-
`;
|
|
4949
|
-
}
|
|
4950
5087
|
out += `
|
|
4951
5088
|
`;
|
|
4952
5089
|
if (result.summary.total === 0) {
|
|
4953
5090
|
out += ` \u26A0\uFE0F NENHUMA CLOUD FUNCTION DETECTADA
|
|
4954
5091
|
|
|
4955
5092
|
`;
|
|
4956
|
-
out += `
|
|
5093
|
+
out += ` Possiveis causas:
|
|
4957
5094
|
`;
|
|
4958
|
-
out += ` 1. O projeto
|
|
5095
|
+
out += ` 1. O projeto nao e Firebase (nao encontrou .firebaserc ou firebase.json)
|
|
4959
5096
|
`;
|
|
4960
|
-
out += ` 2.
|
|
5097
|
+
out += ` 2. Nao ha arquivo functions/src/index.ts
|
|
4961
5098
|
`;
|
|
4962
|
-
out += ` 3. Os triggers
|
|
5099
|
+
out += ` 3. Os triggers nao usam padroes v2 (onCall, onDocumentCreated, etc)
|
|
4963
5100
|
`;
|
|
4964
|
-
out += ` 4. O cache
|
|
4965
|
-
`;
|
|
4966
|
-
out += ` \u2192 Tente: ai-tool functions --no-cache
|
|
5101
|
+
out += ` 4. O cache esta desatualizado
|
|
4967
5102
|
|
|
4968
5103
|
`;
|
|
4969
|
-
out += `
|
|
5104
|
+
out += ` Padroes suportados:
|
|
4970
5105
|
`;
|
|
4971
5106
|
out += ` export const minhaFunc = onCall((request) => { ... })
|
|
4972
5107
|
`;
|
|
4973
5108
|
out += ` export const minhaFunc = onDocumentCreated("path", (event) => { ... })
|
|
4974
5109
|
|
|
4975
5110
|
`;
|
|
4976
|
-
out +=
|
|
5111
|
+
out += `\u{1F4A1} Dicas:
|
|
5112
|
+
`;
|
|
5113
|
+
out += ` \u2192 ${hint("map", ctx)} - ver estrutura do projeto
|
|
5114
|
+
`;
|
|
5115
|
+
out += ` \u2192 ${hint("find", ctx)} - buscar simbolos no codigo
|
|
4977
5116
|
`;
|
|
4978
5117
|
return out;
|
|
4979
5118
|
}
|
|
@@ -5043,6 +5182,7 @@ function formatFunctionsText(result) {
|
|
|
5043
5182
|
out += `
|
|
5044
5183
|
`;
|
|
5045
5184
|
}
|
|
5185
|
+
out += nextSteps("functions", ctx);
|
|
5046
5186
|
return out;
|
|
5047
5187
|
}
|
|
5048
5188
|
function getTriggerIcon(trigger) {
|
|
@@ -5060,6 +5200,8 @@ function getTriggerIcon(trigger) {
|
|
|
5060
5200
|
}
|
|
5061
5201
|
|
|
5062
5202
|
// src/commands/find.ts
|
|
5203
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
5204
|
+
import { join as join11 } from "path";
|
|
5063
5205
|
async function find(query, options = {}) {
|
|
5064
5206
|
const { cwd, format } = parseCommandOptions(options);
|
|
5065
5207
|
const filterType = options.type || "all";
|
|
@@ -5067,6 +5209,7 @@ async function find(query, options = {}) {
|
|
|
5067
5209
|
const defOnly = options.def ?? false;
|
|
5068
5210
|
const refsOnly = options.refs ?? false;
|
|
5069
5211
|
const useCache = options.cache !== false;
|
|
5212
|
+
const ctx = options.ctx || "cli";
|
|
5070
5213
|
const listAllMode = !query && defOnly && filterType && filterType !== "all";
|
|
5071
5214
|
if (!query && !listAllMode) {
|
|
5072
5215
|
throw new Error("Query \xE9 obrigat\xF3ria. Exemplo: ai-tool find useAuth\n Ou use --def para listar todos de um tipo: ai-tool find --type=trigger --def");
|
|
@@ -5119,10 +5262,10 @@ async function find(query, options = {}) {
|
|
|
5119
5262
|
if (format === "json") {
|
|
5120
5263
|
return JSON.stringify({ error: `Nenhum arquivo encontrado na area "${filterArea}"`, availableAreas: areaList });
|
|
5121
5264
|
}
|
|
5122
|
-
return formatAreaNotFound({ target: filterArea, availableAreas: areaList });
|
|
5265
|
+
return formatAreaNotFound({ target: filterArea, availableAreas: areaList, ctx });
|
|
5123
5266
|
}
|
|
5124
5267
|
}
|
|
5125
|
-
const matches = searchInIndex(index, query, filterType, allowedFiles);
|
|
5268
|
+
const matches = searchInIndex(index, query, filterType, allowedFiles, cwd);
|
|
5126
5269
|
let definition = null;
|
|
5127
5270
|
let references = [];
|
|
5128
5271
|
for (const match of matches) {
|
|
@@ -5165,13 +5308,13 @@ async function find(query, options = {}) {
|
|
|
5165
5308
|
fromCache
|
|
5166
5309
|
};
|
|
5167
5310
|
const allSymbolNames = Object.keys(index.symbolsByName);
|
|
5168
|
-
return formatOutput(result, format, (r) => formatFindText(r,
|
|
5311
|
+
return formatOutput(result, format, (r) => formatFindText(r, ctx, allSymbolNames), fromCache);
|
|
5169
5312
|
} catch (error) {
|
|
5170
5313
|
const message = error instanceof Error ? error.message : String(error);
|
|
5171
5314
|
throw new Error(`Erro ao executar find: ${message}`);
|
|
5172
5315
|
}
|
|
5173
5316
|
}
|
|
5174
|
-
function searchInIndex(index, query, filterType, allowedFiles) {
|
|
5317
|
+
function searchInIndex(index, query, filterType, allowedFiles, cwd) {
|
|
5175
5318
|
const matches = [];
|
|
5176
5319
|
const queryLower = query?.toLowerCase() || "";
|
|
5177
5320
|
const processedSymbols = /* @__PURE__ */ new Set();
|
|
@@ -5224,6 +5367,45 @@ function searchInIndex(index, query, filterType, allowedFiles) {
|
|
|
5224
5367
|
}
|
|
5225
5368
|
}
|
|
5226
5369
|
}
|
|
5370
|
+
if (!listAllMode && query) {
|
|
5371
|
+
const importFiles = matches.filter((m) => m.matchType === "import").map((m) => m.file);
|
|
5372
|
+
const baseCwd = cwd || "";
|
|
5373
|
+
const MAX_USAGE_FILES = 10;
|
|
5374
|
+
const MAX_USAGES_PER_FILE = 3;
|
|
5375
|
+
for (const filePath of importFiles.slice(0, MAX_USAGE_FILES)) {
|
|
5376
|
+
try {
|
|
5377
|
+
const fullPath = baseCwd ? join11(baseCwd, filePath) : filePath;
|
|
5378
|
+
const content = readFileSync4(fullPath, "utf-8");
|
|
5379
|
+
const lines = content.split("\n");
|
|
5380
|
+
const usageRegex = new RegExp(`\\b${escapeRegex(query)}\\b`);
|
|
5381
|
+
let usagesFound = 0;
|
|
5382
|
+
for (let i = 0; i < lines.length && usagesFound < MAX_USAGES_PER_FILE; i++) {
|
|
5383
|
+
const line = lines[i].trim();
|
|
5384
|
+
if (line.startsWith("import ") || line.startsWith("export ") || line.startsWith("//") || line.startsWith("*") || line.startsWith("/*")) {
|
|
5385
|
+
continue;
|
|
5386
|
+
}
|
|
5387
|
+
if (usageRegex.test(line)) {
|
|
5388
|
+
const key = `usage:${filePath}:${i + 1}:${query}`;
|
|
5389
|
+
if (processedSymbols.has(key)) continue;
|
|
5390
|
+
processedSymbols.add(key);
|
|
5391
|
+
const fileData = index.files[filePath];
|
|
5392
|
+
const codeLine = line.length > 100 ? line.substring(0, 100) + "..." : line;
|
|
5393
|
+
matches.push({
|
|
5394
|
+
file: filePath,
|
|
5395
|
+
line: i + 1,
|
|
5396
|
+
column: 0,
|
|
5397
|
+
code: codeLine,
|
|
5398
|
+
matchType: "usage",
|
|
5399
|
+
symbolType: inferSymbolTypeFromName(query),
|
|
5400
|
+
category: fileData?.category || "other"
|
|
5401
|
+
});
|
|
5402
|
+
usagesFound++;
|
|
5403
|
+
}
|
|
5404
|
+
}
|
|
5405
|
+
} catch {
|
|
5406
|
+
}
|
|
5407
|
+
}
|
|
5408
|
+
}
|
|
5227
5409
|
matches.sort((a, b) => {
|
|
5228
5410
|
const order = { definition: 0, import: 1, usage: 2 };
|
|
5229
5411
|
const orderDiff = order[a.matchType] - order[b.matchType];
|
|
@@ -5232,6 +5414,9 @@ function searchInIndex(index, query, filterType, allowedFiles) {
|
|
|
5232
5414
|
});
|
|
5233
5415
|
return matches;
|
|
5234
5416
|
}
|
|
5417
|
+
function escapeRegex(str) {
|
|
5418
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5419
|
+
}
|
|
5235
5420
|
function matchesType(kind, filter) {
|
|
5236
5421
|
if (filter === "all") return true;
|
|
5237
5422
|
switch (filter) {
|
|
@@ -5327,7 +5512,6 @@ export {
|
|
|
5327
5512
|
map,
|
|
5328
5513
|
dead,
|
|
5329
5514
|
deadFix,
|
|
5330
|
-
COMMAND_REFERENCE,
|
|
5331
5515
|
formatFileNotFound,
|
|
5332
5516
|
formatAreaNotFound,
|
|
5333
5517
|
formatMissingTarget,
|