shark-ai 0.4.13 → 0.4.15

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/bin/shark.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  t,
10
10
  tokenStorage,
11
11
  tui
12
- } from "../chunk-WVBOO6LE.js";
12
+ } from "../chunk-H6S7DYRG.js";
13
13
 
14
14
  // src/core/error/crash-handler.ts
15
15
  import fs from "fs";
@@ -422,6 +422,7 @@ var AgentActionSchema = z2.object({
422
422
  "modify_file",
423
423
  "list_files",
424
424
  "search_file",
425
+ "search_code",
425
426
  "read_file",
426
427
  "delete_file",
427
428
  "list_structure",
@@ -429,6 +430,7 @@ var AgentActionSchema = z2.object({
429
430
  "search_ast",
430
431
  "run_command",
431
432
  "talk_with_user",
433
+ "use_mcp_tool",
432
434
  "ast_list_structure",
433
435
  "ast_get_method",
434
436
  "ast_add_method",
@@ -457,6 +459,9 @@ var AgentActionSchema = z2.object({
457
459
  tool_name: z2.string().nullable().optional(),
458
460
  tool_args: z2.string().nullable().optional(),
459
461
  // JSON string argument
462
+ // search_code fields
463
+ query: z2.string().nullable().optional(),
464
+ is_regex: z2.boolean().nullable().optional(),
460
465
  // AST-Grep fields
461
466
  pattern: z2.string().nullable().optional(),
462
467
  fix: z2.string().nullable().optional(),
@@ -704,6 +709,12 @@ function getAgentId(overrideId) {
704
709
  if (config.agents?.ba) return config.agents.ba;
705
710
  return process.env.STACKSPOT_BA_AGENT_ID || "01KEJ95G304TNNAKGH5XNEEBVD";
706
711
  }
712
+ function getAgentVersion(overrideVersion) {
713
+ if (overrideVersion) return overrideVersion;
714
+ const config = ConfigManager.getInstance().getConfig();
715
+ if (config.agentVersions?.ba) return config.agentVersions.ba;
716
+ return process.env.STACKSPOT_BA_AGENT_VERSION;
717
+ }
707
718
  async function runBusinessAnalystAgent(prompt, options = {}) {
708
719
  const { agentId, onChunk, onComplete } = options;
709
720
  const realm = await getActiveRealm();
@@ -721,6 +732,10 @@ async function runBusinessAnalystAgent(prompt, options = {}) {
721
732
  deep_search_ks: false,
722
733
  conversation_id: existingConversationId
723
734
  };
735
+ const agentVersion = getAgentVersion();
736
+ if (agentVersion) {
737
+ requestPayload.agent_version_number = agentVersion;
738
+ }
724
739
  const effectiveAgentId = getAgentId(options.agentId);
725
740
  const agentUrl = `${STACKSPOT_AGENT_API_BASE}/v1/agent/${effectiveAgentId}/chat`;
726
741
  const headers = {
@@ -1349,6 +1364,49 @@ function handleSearchFile(pattern) {
1349
1364
  return `Error searching files: ${e.message}`;
1350
1365
  }
1351
1366
  }
1367
+ function handleSearchCode(globPattern, query, isRegex = false) {
1368
+ const MAX_MATCHES = 50;
1369
+ const MAX_FILE_SIZE_BYTES = 500 * 1024;
1370
+ try {
1371
+ const files = fg.sync(globPattern, { dot: true, absolute: false });
1372
+ if (files.length === 0) return `No files found matching pattern: "${globPattern}"`;
1373
+ let searchRegex;
1374
+ try {
1375
+ searchRegex = isRegex ? new RegExp(query, "gi") : new RegExp(query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "gi");
1376
+ } catch {
1377
+ return `Error: Invalid regex pattern: "${query}"`;
1378
+ }
1379
+ const results = [];
1380
+ let totalMatches = 0;
1381
+ for (const filePath of files) {
1382
+ if (totalMatches >= MAX_MATCHES) break;
1383
+ try {
1384
+ const fullPath = path5.resolve(process.cwd(), filePath);
1385
+ const stats = fs4.statSync(fullPath);
1386
+ if (stats.size > MAX_FILE_SIZE_BYTES) continue;
1387
+ const content = fs4.readFileSync(fullPath, "utf-8");
1388
+ const lines = content.split("\n");
1389
+ for (let i = 0; i < lines.length; i++) {
1390
+ if (totalMatches >= MAX_MATCHES) break;
1391
+ searchRegex.lastIndex = 0;
1392
+ if (searchRegex.test(lines[i])) {
1393
+ results.push(`${filePath}:${i + 1}: ${lines[i].trim()}`);
1394
+ totalMatches++;
1395
+ }
1396
+ }
1397
+ } catch {
1398
+ }
1399
+ }
1400
+ if (results.length === 0) {
1401
+ return `No matches found for "${query}" in files matching "${globPattern}"`;
1402
+ }
1403
+ const limited = totalMatches >= MAX_MATCHES ? ` (limited to ${MAX_MATCHES})` : "";
1404
+ return `Found ${totalMatches} match(es) for "${query}" in "${globPattern}"${limited}:
1405
+ ${results.join("\n")}`;
1406
+ } catch (e) {
1407
+ return `Error searching code: ${e.message}`;
1408
+ }
1409
+ }
1352
1410
  function startSmartReplace(filePath, newContent, targetContent, tui2) {
1353
1411
  if (!fs4.existsSync(filePath)) {
1354
1412
  tui2.log.error(`\u274C File not found for modification: ${filePath}`);
@@ -1715,6 +1773,12 @@ function getAgentId2(overrideId) {
1715
1773
  if (config.agents?.spec) return config.agents.spec;
1716
1774
  return process.env.STACKSPOT_SPEC_AGENT_ID || "01KEPXTX37FTB4N672TZST4SGP";
1717
1775
  }
1776
+ function getAgentVersion2(overrideVersion) {
1777
+ if (overrideVersion) return overrideVersion;
1778
+ const config = ConfigManager.getInstance().getConfig();
1779
+ if (config.agentVersions?.spec) return config.agentVersions.spec;
1780
+ return process.env.STACKSPOT_SPEC_AGENT_VERSION;
1781
+ }
1718
1782
  async function interactiveSpecificationAgent(options = {}) {
1719
1783
  FileLogger.init();
1720
1784
  tui.intro("\u{1F3D7}\uFE0F Specification Agent (Template-Based)");
@@ -1774,31 +1838,17 @@ async function interactiveSpecificationAgent(options = {}) {
1774
1838
  }
1775
1839
  let initialPrompt = `
1776
1840
  Voc\xEA \xE9 o **Shark Spec**, um Arquiteto de Software S\xEAnior e Tech Lead.
1777
- Seu objetivo \xE9 produzir uma especifica\xE7\xE3o t\xE9cnica precisa e espec\xEDfica para a tarefa no arquivo \`_sharkrc/tech-spec.md\`.
1841
+ Seu objetivo final \xE9 produzir uma especifica\xE7\xE3o t\xE9cnica precisa para a tarefa no arquivo \`_sharkrc/tech-spec.md\`.
1778
1842
 
1779
- \u26A0\uFE0F WORKFLOW OBRIGAT\xD3RIO \u2013 SIGA ESSA SEQU\xCANCIA ESTRITAMENTE. N\xC3O PULE ETAPAS.
1843
+ \u26A0\uFE0F O SEU WORKFLOW \xC9 GUIADO POR FASES.
1844
+ N\xC3O TENTE ADIANTAR O TRABALHO (ex: investigar c\xF3digo ou preencher template agora).
1780
1845
 
1781
- **FASE 1 \u2013 ENTENDA A TAREFA (COMECE AQUI):**
1846
+ **VOC\xCA EST\xC1 NA FASE 1: ENTENDIMENTO DA TAREFA**
1782
1847
  - Use \`talk_with_user\` para perguntar ao usu\xE1rio qual tarefa espec\xEDfica, funcionalidade ou bug ele precisa especificar.
1783
- - N\xC3O explore o c\xF3digo ou leia arquivos ainda.
1784
- - Confirme o escopo e os limites com o usu\xE1rio antes de prosseguir.
1785
-
1786
- **FASE 2 \u2013 INVESTIGUE (FOCADO APENAS NA TAREFA):**
1787
- - Use \`list_files\` e \`read_file\` APENAS em arquivos diretamente relevantes para a tarefa confirmada.
1788
- - N\xC3O leia o projeto de forma gen\xE9rica.
1789
- - REGRA DE LEITURA PR\xC9VIA: Voc\xEA N\xC3O PODE adicionar uma tarefa referenciando um arquivo que voc\xEA n\xE3o leu nesta sess\xE3o (exce\xE7\xE3o: novos arquivos a serem criados).
1790
-
1791
- **FASE 3 \u2013 PREENCHA O TEMPLATE:**
1792
- - Use \`modify_file\` em \`_sharkrc/tech-spec.md\` para substituir os placeholders com conte\xFAdo real e espec\xEDfico da tarefa.
1793
- - As Se\xE7\xF5es 1-4 (Stack, Arquitetura, Modelo de Dados, API) devem descrever o contexto da TAREFA, n\xE3o do projeto como um todo.
1794
- - Passos de Implementa\xE7\xE3o: APENAS checkboxes markdown: \`- [ ] [Verbo de A\xE7\xE3o] [O Que] em [Caminho Relativo]\`. SEM listas numeradas, SEM subn\xEDveis.
1795
- - Quando todos os placeholders acabarem e voc\xEA estiver satisfeito, retorne: \`SPEC_UPDATED: Complete\`.
1796
-
1797
- **REGRAS CR\xCDTICAS:**
1798
- - NUNCA preencha a Technology Stack ou Architecture com informa\xE7\xF5es gen\xE9ricas do projeto. Essas se\xE7\xF5es devem refletir o que a tarefa ir\xE1 usar ou alterar.
1799
- - Use \`talk_with_user\` sempre que os requisitos forem amb\xEDguos.
1800
- - N\xC3O tente escrever todas as se\xE7\xF5es de uma vez. Trabalhe de forma incremental, uma se\xE7\xE3o por vez.
1801
- - IMPORTANTE: Toda a sua comunica\xE7\xE3o e a especifica\xE7\xE3o gerada DEVEM ser em Portugu\xEAs.
1848
+ - Confirme o escopo e os limites com o usu\xE1rio.
1849
+ - Se o escopo estiver perfeitamente claro e confirmado (com o usu\xE1rio), emita "PHASE_COMPLETED" no campo "summary" do JSON para avan\xE7ar.
1850
+
1851
+ IMPORTANTE: Toda a sua comunica\xE7\xE3o DEVE ser em Portugu\xEAs.
1802
1852
  `;
1803
1853
  if (briefingContent) {
1804
1854
  initialPrompt += `
@@ -1836,6 +1886,7 @@ async function runSpecLoop(initialMessage, targetPath, overrideAgentId) {
1836
1886
  let nextPrompt = initialMessage;
1837
1887
  let keepGoing = true;
1838
1888
  let stepCount = 0;
1889
+ let currentPhase = 1;
1839
1890
  const MAX_STEPS = 30;
1840
1891
  while (keepGoing && stepCount < MAX_STEPS) {
1841
1892
  stepCount++;
@@ -1860,7 +1911,42 @@ async function runSpecLoop(initialMessage, targetPath, overrideAgentId) {
1860
1911
  let executionResults = "";
1861
1912
  let waitingForUser = false;
1862
1913
  let specUpdated = false;
1914
+ if (lastResponse.message && lastResponse.message.includes("PHASE_COMPLETED")) {
1915
+ if (currentPhase === 1) {
1916
+ currentPhase = 2;
1917
+ tui.log.success(`\u2705 Fase 1 Conclu\xEDda. Iniciando Fase 2 (Investiga\xE7\xE3o).`);
1918
+ nextPrompt = `[System Message]
1919
+ Voc\xEA completou a FASE 1 com sucesso.
1920
+
1921
+ **VOC\xCA AGORA EST\xC1 NA FASE 2: INVESTIGA\xC7\xC3O**
1922
+ - Use \`search_code\` e \`list_files\` para explorar os arquivos relevantes \xE0 tarefa.
1923
+ - Prefira \`search_code\` em vez de \`read_file\` para buscar c\xF3digo sem inflar o contexto.
1924
+ - N\xC3O leia o projeto inteiro de forma gen\xE9rica.
1925
+ - REGRA DE OURO (READ-FIRST): Voc\xEA N\xC3O PODE referenciar um arquivo na especifica\xE7\xE3o t\xE9cnica que n\xE3o tenha investigado nesta fase.
1926
+ - Quando achar que possui toda a clareza t\xE9cnica sobre onde e o que deve ser feito no c\xF3digo, emita "PHASE_COMPLETED" no summary.`;
1927
+ continue;
1928
+ } else if (currentPhase === 2) {
1929
+ currentPhase = 3;
1930
+ tui.log.success(`\u2705 Fase 2 Conclu\xEDda. Iniciando Fase 3 (Preenchimento).`);
1931
+ nextPrompt = `[System Message]
1932
+ Voc\xEA completou a FASE 2 com sucesso.
1933
+
1934
+ **VOC\xCA AGORA EST\xC1 NA FASE 3: PREENCHIMENTO DO TEMPLATE**
1935
+ - Use \`modify_file\` no arquivo \`${targetPath}\` para substituir os placeholders pelo conte\xFAdo real levantado na fase de investiga\xE7\xE3o.
1936
+ - As se\xE7\xF5es 1-4 devem descrever o contexto da TAREFA, e n\xE3o o projeto como um todo.
1937
+ - Passos de Implementa\xE7\xE3o (Implementation Steps): APENAS checkboxes markdown: \`- [ ] [Verbo de A\xE7\xE3o] [O Que] em [Caminho Relativo]\`.
1938
+ - Quando TODOS os placeholders ([TO BE ANALYZED] ou [TO BE FILLED]) forem substitu\xEDdos e o trabalho conclu\xEDdo, emita "SPEC_UPDATED: Complete" no summary para finalizar.`;
1939
+ continue;
1940
+ }
1941
+ }
1863
1942
  if (lastResponse.message && lastResponse.message.includes("SPEC_UPDATED:")) {
1943
+ if (currentPhase < 3) {
1944
+ tui.log.warning(`O agente tentou finalizar prematuramente. For\xE7ando retorno para a fase atual...`);
1945
+ nextPrompt = `[System Error]: Voc\xEA tentou finalizar a especifica\xE7\xE3o prematuramente emitindo SPEC_UPDATED, mas ainda est\xE1 na Fase ${currentPhase}. Voc\xEA s\xF3 pode finalizar quando estiver na Fase 3.
1946
+
1947
+ Continue seu trabalho na Fase ${currentPhase} ou emita "PHASE_COMPLETED" se terminou esta etapa atual.`;
1948
+ continue;
1949
+ }
1864
1950
  const content = fs5.existsSync(targetPath) ? fs5.readFileSync(targetPath, "utf-8") : "";
1865
1951
  if (content.includes("[TO BE")) {
1866
1952
  const pendingMatches = [...content.matchAll(/## ([^\n]+)[\s\S]*?\[TO BE/g)].map((m) => m[1]);
@@ -1902,6 +1988,16 @@ ${result}
1902
1988
  executionResults += `[Action search_file(${action.path}) Result]:
1903
1989
  ${result}
1904
1990
 
1991
+ `;
1992
+ } else if (action.type === "search_code") {
1993
+ const glob = action.path || "src/**/*";
1994
+ const query = action.query || "";
1995
+ const isRegex = action.is_regex === true;
1996
+ tui.log.info(`\u{1F50E} Search code: ${colors.dim(`"${query}" in ${glob}`)}`);
1997
+ const result = handleSearchCode(glob, query, isRegex);
1998
+ executionResults += `[Action search_code("${query}" in "${glob}") Result]:
1999
+ ${result}
2000
+
1905
2001
  `;
1906
2002
  } else if (["create_file", "modify_file"].includes(action.type)) {
1907
2003
  let actionPath = path6.resolve(action.path || "");
@@ -2013,6 +2109,10 @@ async function callSpecAgentApi(prompt, onChunk, agentId) {
2013
2109
  use_conversation: true,
2014
2110
  conversation_id: conversationId
2015
2111
  };
2112
+ const agentVersion = getAgentVersion2();
2113
+ if (agentVersion) {
2114
+ payload.agent_version_number = agentVersion;
2115
+ }
2016
2116
  const effectiveAgentId = getAgentId2(agentId);
2017
2117
  const url = `${STACKSPOT_AGENT_API_BASE}/v1/agent/${effectiveAgentId}/chat`;
2018
2118
  let fullMsg = "";
@@ -2050,6 +2150,11 @@ function getAgentId3() {
2050
2150
  if (config.agents?.scan) return config.agents.scan;
2051
2151
  return process.env.STACKSPOT_SCAN_AGENT_ID || "01KEQ9AHWB550J2244YBH3QATN";
2052
2152
  }
2153
+ function getAgentVersion3() {
2154
+ const config = ConfigManager.getInstance().getConfig();
2155
+ if (config.agentVersions?.scan) return config.agentVersions.scan;
2156
+ return process.env.STACKSPOT_SCAN_AGENT_VERSION;
2157
+ }
2053
2158
  async function interactiveScanAgent(options = {}) {
2054
2159
  FileLogger.init();
2055
2160
  const config = ConfigManager.getInstance().getConfig();
@@ -2505,8 +2610,7 @@ ${result}
2505
2610
  }
2506
2611
  async function callScanAgentApi(prompt, onChunk) {
2507
2612
  const realm = await getActiveRealm();
2508
- const token = await tokenStorage.getToken(realm);
2509
- if (!token) throw new Error("Not logged in");
2613
+ const token = await ensureValidToken(realm);
2510
2614
  let conversationId = await conversationManager.getConversationId(AGENT_TYPE3);
2511
2615
  const payload = {
2512
2616
  user_prompt: prompt,
@@ -2516,6 +2620,10 @@ async function callScanAgentApi(prompt, onChunk) {
2516
2620
  use_conversation: true,
2517
2621
  conversation_id: conversationId
2518
2622
  };
2623
+ const agentVersion = getAgentVersion3();
2624
+ if (agentVersion) {
2625
+ payload.agent_version_number = agentVersion;
2626
+ }
2519
2627
  const url = `${STACKSPOT_AGENT_API_BASE}/v1/agent/${getAgentId3()}/chat`;
2520
2628
  let fullMsg = "";
2521
2629
  let raw = {};
@@ -2578,6 +2686,13 @@ function getAgentId4(overrideId) {
2578
2686
  if (process.env.STACKSPOT_DEV_AGENT_ID) return process.env.STACKSPOT_DEV_AGENT_ID;
2579
2687
  return "01KEQCGJ65YENRA4QBXVN1YFFX";
2580
2688
  }
2689
+ function getAgentVersion4(overrideVersion) {
2690
+ if (overrideVersion) return overrideVersion;
2691
+ const config = ConfigManager.getInstance().getConfig();
2692
+ if (config.agentVersions?.dev) return config.agentVersions.dev;
2693
+ if (process.env.STACKSPOT_DEV_AGENT_VERSION) return process.env.STACKSPOT_DEV_AGENT_VERSION;
2694
+ return void 0;
2695
+ }
2581
2696
  async function interactiveDeveloperAgent(options = {}) {
2582
2697
  FileLogger.init();
2583
2698
  const agentId = getAgentId4();
@@ -2887,6 +3002,10 @@ async function callDevAgentApi(prompt, onChunk, conversationKey = AGENT_TYPE4) {
2887
3002
  conversation_id: conversationId,
2888
3003
  stackspot_knowledge: false
2889
3004
  };
3005
+ const agentVersion = getAgentVersion4();
3006
+ if (agentVersion) {
3007
+ payload.agent_version_number = agentVersion;
3008
+ }
2890
3009
  const url = `${STACKSPOT_AGENT_API_BASE}/v1/agent/${getAgentId4()}/chat`;
2891
3010
  let fullMsg = "";
2892
3011
  let raw = {};
@@ -3169,6 +3288,11 @@ function getAgentId5() {
3169
3288
  if (config.agents?.qa) return config.agents.qa;
3170
3289
  return process.env.STACKSPOT_QA_AGENT_ID || "01KEQFJZ3Q3JER11NH22HEZX9X";
3171
3290
  }
3291
+ function getAgentVersion5() {
3292
+ const config = ConfigManager.getInstance().getConfig();
3293
+ if (config.agentVersions?.qa) return config.agentVersions.qa;
3294
+ return process.env.STACKSPOT_QA_AGENT_VERSION;
3295
+ }
3172
3296
  var ChromeDevToolsClient = class {
3173
3297
  client = null;
3174
3298
  transport = null;
@@ -3264,7 +3388,8 @@ ${projectContext}
3264
3388
  user_prompt: userMessage,
3265
3389
  streaming: true,
3266
3390
  use_conversation: true,
3267
- conversation_id: existingConversationId
3391
+ conversation_id: existingConversationId,
3392
+ ...getAgentVersion5() ? { agent_version_number: getAgentVersion5() } : {}
3268
3393
  },
3269
3394
  {
3270
3395
  "Authorization": `Bearer ${token}`
@@ -3296,11 +3421,12 @@ ${projectContext}
3296
3421
  keepRunning = false;
3297
3422
  break;
3298
3423
  }
3299
- if (!agentResponse) continue;
3300
- if (agentResponse.summary) {
3301
- tui.log.info(colors.primary(`\u{1F4CB} Plan: ${agentResponse.summary}`));
3424
+ const currentResponse = agentResponse;
3425
+ if (!currentResponse) continue;
3426
+ if (currentResponse.summary) {
3427
+ tui.log.info(colors.primary(`\u{1F4CB} Plan: ${currentResponse.summary}`));
3302
3428
  }
3303
- if (agentResponse.actions.length === 0) {
3429
+ if (currentResponse.actions.length === 0) {
3304
3430
  const reply = await tui.text({
3305
3431
  message: "\u{1F916} Shark QA:",
3306
3432
  placeholder: "Your reply..."
@@ -3312,7 +3438,7 @@ ${projectContext}
3312
3438
  }
3313
3439
  continue;
3314
3440
  }
3315
- for (const action of agentResponse.actions) {
3441
+ for (const action of currentResponse.actions) {
3316
3442
  tui.log.info(colors.dim(`Executing: ${action.type}`));
3317
3443
  let result = "";
3318
3444
  try {