@appilots/cli 0.10.0 → 0.11.3

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/index.d.mts CHANGED
@@ -1,3 +1,5 @@
1
+ import * as t from '@babel/types';
2
+
1
3
  /**
2
4
  * Core types for the MCP Generator.
3
5
  */
@@ -57,6 +59,36 @@ interface ScreenPermissionDescriptor {
57
59
  blockedActions?: string[];
58
60
  isPii?: boolean;
59
61
  }
62
+ /**
63
+ * O que a varredura alcançou — para o relatório do CLI, não para o agente.
64
+ *
65
+ * Mora aqui e NÃO dentro do `MCPDocument`: o documento é contrato com o SDK,
66
+ * a API e o dashboard, e "quantos arquivos eu li" não é dado do app. Todo
67
+ * campo é opcional para quem consome, porque um analisador de plataforma
68
+ * pode não produzir nenhum — o relatório omite a seção em vez de mentir zeros.
69
+ */
70
+ interface AnalysisDiagnostics {
71
+ navigation?: {
72
+ /** Arquivos que o glob de navegação alcançou. */
73
+ filesScanned: number;
74
+ /** Destes, quantos mencionam fábrica de navegador, ParamList, `.Navigator` ou `.Screen`. */
75
+ filesWithEvidence: number;
76
+ /** Fábricas reconhecidas atribuídas a um nome. Candidatos não contam. */
77
+ navigatorsDeclared: number;
78
+ /** Navegadores confirmados por `<X.Navigator>` no JSX. */
79
+ navigatorsWithJsx: number;
80
+ /** Rotas únicas no documento. */
81
+ routes: number;
82
+ /** Destas, quantas resolvem `component=` até um arquivo. */
83
+ routesLinkedToFile: number;
84
+ /** Roteador de arquivo encontrado, quando é o caso. */
85
+ fileBasedRouter?: {
86
+ kind: 'expo-router';
87
+ routeDir: string;
88
+ routes: number;
89
+ };
90
+ };
91
+ }
60
92
  interface NavigationGraph {
61
93
  screens: Record<string, NavigationNode>;
62
94
  initialScreen: string;
@@ -167,6 +199,21 @@ interface ActionDescriptor {
167
199
  effect?: 'read' | 'write' | 'destructive' | string;
168
200
  riskLevel?: 'low' | 'medium' | 'high' | string;
169
201
  requiresConfirmation?: boolean;
202
+ /**
203
+ * How long this action's work takes, in milliseconds, as DECLARED by
204
+ * the app in `registerScreen()`. Read from the source, never inferred:
205
+ * the analyzer can prove a handler awaits something (see
206
+ * `appilotsInferred.isAsyncTrigger`) but never how long that await
207
+ * takes, and the gap between the two is what a wrong constant costs —
208
+ * the SDK stops waiting mid-operation and the agent plans against a
209
+ * loading screen with no controls on it.
210
+ *
211
+ * Carried into the document verbatim and into `waitPolicy.maxMs`,
212
+ * where it overrides the inferred 10s default. The SDK clamps it to
213
+ * its own ceiling; nothing here caps it, because the analyzer has no
214
+ * business overruling what the app said about its own operation.
215
+ */
216
+ asyncBudgetMs?: number;
170
217
  /**
171
218
  * Static-analysis hints derived by the MCP generator from the
172
219
  * action's handler body. Used by the SDK to refine waiting and
@@ -308,11 +355,22 @@ declare class ScreenAnalyzer {
308
355
  private strictScreens;
309
356
  /** Glob patterns that identify screen files in strict mode */
310
357
  private screenPatterns;
358
+ /**
359
+ * Arquivos que uma ROTA monta (`component={…}` resolvido pelo
360
+ * `NavigationAnalyzer`). Passam pelo filtro estrito sem depender de
361
+ * convenção de nome, porque um componente que uma rota monta É uma tela por
362
+ * definição — não é heurística, é o que o app declarou.
363
+ *
364
+ * É isto que resolve o caso `rocketchat`, cujas telas se chamam `*View.tsx`
365
+ * em `app/views/`, e o `coopcycle`, que não tem diretório `screens/` nenhum.
366
+ */
367
+ private routeTargetFiles;
311
368
  /** §D: Count of screens filtered out in strict mode (available after analyze()) */
312
369
  screensFilteredOut: number;
313
370
  constructor(config: AnalyzerConfig, options?: {
314
371
  strictScreens?: boolean;
315
372
  screenPatterns?: string[];
373
+ routeTargetFiles?: Set<string>;
316
374
  });
317
375
  /** Analyze all screens in the project */
318
376
  analyze(): Promise<ScreenDescriptor[]>;
@@ -446,6 +504,64 @@ declare class ScreenAnalyzer {
446
504
  private extractScreenName;
447
505
  }
448
506
 
507
+ declare class ModuleGraph {
508
+ private readonly asts;
509
+ private readonly resolved;
510
+ /** `@src/*` → `<root>/src/*`, lido do tsconfig do app. */
511
+ private readonly aliases;
512
+ /** `uniswap` → `<repo>/packages/uniswap`, lido do workspace do monorepo. */
513
+ private readonly workspacePackages;
514
+ constructor(rootDir?: string);
515
+ /** AST de um arquivo, memoizada. `null` quando não parseia. */
516
+ parse(file: string): t.File | null;
517
+ /**
518
+ * `./account/Home` a partir de `src/navigation/index.tsx` → caminho absoluto.
519
+ * Só resolve caminho relativo: import de pacote (`@react-navigation/native`)
520
+ * é de terceiro e não tem tela nossa dentro.
521
+ */
522
+ resolve(fromFile: string, spec: string): string | null;
523
+ /** `import`s do arquivo, por nome local. */
524
+ private imports;
525
+ /** `const X = <init>` no topo do arquivo, incluindo `export const`. */
526
+ private topLevelInit;
527
+ /**
528
+ * Onde `name` é DEFINIDO — segue import e reexport de barrel.
529
+ * Devolve o arquivo e o nome sob o qual ele é definido lá.
530
+ */
531
+ resolveBinding(file: string, name: string, hops?: number): {
532
+ file: string;
533
+ name: string;
534
+ } | null;
535
+ /**
536
+ * O valor string de uma expressão de nome de rota, ou `null`.
537
+ *
538
+ * Cobre `"Chat"`, `ROUTES.CHAT`, `ROUTES.ONBOARDING.SPLASH` e `SOME_CONST` —
539
+ * seguindo import quando o objeto vem de outro arquivo. NÃO cobre template
540
+ * com interpolação nem valor calculado, de propósito.
541
+ */
542
+ stringConstant(file: string, node: t.Node | null | undefined, hops?: number): string | null;
543
+ /**
544
+ * O ARQUIVO onde vive o componente de uma rota, ou `null`.
545
+ *
546
+ * Aceita as três formas que o corpus mostrou: identificador
547
+ * (`component={Home}`), membro de barrel (`component={screens.AccountHome}`)
548
+ * e componente embrulhado em HOC (`component={gestureHandlerRootHOC(Chat)}`,
549
+ * que é como o `pocketpal` monta todas as telas do Drawer).
550
+ */
551
+ componentFile(file: string, node: t.Node | null | undefined, hops?: number): string | null;
552
+ /**
553
+ * Os arquivos DO PROJETO que este arquivo renderiza como JSX.
554
+ *
555
+ * `<ChatView …/>` em `ChatScreen.tsx` → `components/ChatView/ChatView.tsx`.
556
+ * Import de pacote devolve `null` no `resolve` e fica de fora: componente de
557
+ * terceiro não tem tela nossa dentro.
558
+ *
559
+ * Ignora `<X.Screen>` e `<X.Navigator>` de propósito — navegação é outro
560
+ * extractor, e um navegador não é conteúdo de tela.
561
+ */
562
+ renderedComponentFiles(file: string): string[];
563
+ }
564
+
449
565
  /**
450
566
  * Analyzes React Navigation configuration to build a navigation graph.
451
567
  */
@@ -455,16 +571,140 @@ declare class NavigationAnalyzer {
455
571
  private navigationInclude;
456
572
  /** Extra glob patterns to exclude from navigation analysis */
457
573
  private navigationExclude;
574
+ /** Exposto para a composição reusar o cache de AST em vez de reparsear a árvore. */
575
+ readonly graph: ModuleGraph;
576
+ /**
577
+ * Rota → arquivo do componente que ela monta. É a única ligação entre o grafo
578
+ * de navegação e a árvore de telas; sem ela as duas metades do documento
579
+ * falam de coisas diferentes. Populada por `analyze()`.
580
+ */
581
+ readonly routeTargets: Map<string, string>;
582
+ /**
583
+ * Arquivos que DECLARAM uma fábrica de navegador.
584
+ *
585
+ * Um navegador aninhado é montado como `component=` de uma rota — no
586
+ * `apps/example-app`, `<Stack.Screen name="Main" component={MainTabNavigator} />`.
587
+ * Promover esse arquivo a tela criaria seis "telas" sem uma única ação, que é
588
+ * exatamente o falso positivo que o corpus existe para não repetir. Um
589
+ * navegador é um contêiner de rotas; a tela está um nível abaixo.
590
+ *
591
+ * Entram aqui os dois lados: o arquivo que CHAMA a fábrica e o arquivo que
592
+ * RENDERIZA `<X.Navigator>`. Não são o mesmo — `bluewallet` declara
593
+ * `DetailViewStack` num arquivo e escreve o JSX em
594
+ * `navigation/DetailViewScreensStack.tsx`, e checar só o primeiro deixava o
595
+ * segundo entrar como tela.
596
+ */
597
+ readonly navigatorFiles: Set<string>;
598
+ /** Preenchido por `analyze()`. Vazio antes disso. */
599
+ diagnostics: NonNullable<AnalysisDiagnostics['navigation']>;
458
600
  constructor(config: AnalyzerConfig, options?: {
459
601
  navigationInclude?: string[];
460
602
  navigationExclude?: string[];
461
603
  });
462
604
  /** Build the full navigation graph */
463
605
  analyze(): Promise<NavigationGraph>;
464
- /** Find all navigation-related files */
606
+ /** Fase 1: as fábricas `create*Navigator()` atribuídas a um nome neste arquivo. */
607
+ private collectDeclarations;
608
+ /**
609
+ * As rotas de `createXNavigator({ screens: { … } })`.
610
+ *
611
+ * Quatro formas no corpus, todas em `rocketchat`:
612
+ * `NewServerView` — shorthand, nome = componente
613
+ * `LoginView: createNativeStackScreen({ screen })` — embrulho da própria lib
614
+ * `SelectListView: SelectListViewScreen` — identificador direto
615
+ * `ChatsStackNavigator: ChatsStack` — navegador aninhado
616
+ *
617
+ * `groups: { G: { screens: { … } } }` também entra: faz parte da API estática
618
+ * e agrupar rotas não muda o que elas são.
619
+ */
620
+ private parseStaticScreens;
621
+ /**
622
+ * Todo arquivo-fonte do projeto — não os que ficam num diretório com o nome
623
+ * certo.
624
+ *
625
+ * POR QUE ISTO MUDOU. A lista anterior era `**\/navigation/**`,
626
+ * `**\/navigator*` e `**\/routes*`, o que fazia da descoberta de navegação uma
627
+ * convenção de caminho. Medido contra 20 apps React Native de terceiros
628
+ * (`qa/eval-corpus`), o filtro errava por motivos que nada têm a ver com o
629
+ * app não ter navegação:
630
+ *
631
+ * - `pocketpal` declara 4 navegadores em `App.tsx` e dentro de `src/screens/`;
632
+ * - `comapeo` usa `src/frontend/Navigation/` — `N` maiúsculo, e o glob é
633
+ * sensível a caixa;
634
+ * - `abacus` usa `src/routes/index.tsx` — `routes` é o DIRETÓRIO, e o glob
635
+ * pedia um arquivo chamado `routes*`;
636
+ * - `discourse` declara em `js/Discourse.js` — o glob só aceitava `.ts`/`.tsx`;
637
+ * - `rainbow` tem 11 arquivos com navegador e o glob alcançava 1.
638
+ *
639
+ * Varrer tudo não custa uma varredura nova: `ReactNativePlatformAnalyzer` já
640
+ * globa e parseia a árvore inteira para `ComponentAnalyzer` e `FormAnalyzer`.
641
+ * O que segura o custo aqui é o portão por evidência em `analyze()`, que só
642
+ * parseia arquivo cujo texto menciona uma fábrica de navegador.
643
+ *
644
+ * Os globs antigos continuam na lista: se um projeto restringir `include`, o
645
+ * que era encontrado antes continua sendo.
646
+ */
465
647
  private findNavigationFiles;
466
- /** Parse navigator definitions from a file */
467
- private parseNavigators;
648
+ /**
649
+ * Fase 2: o JSX `<X.Navigator>` deste arquivo, ligado à declaração de `X` —
650
+ * que pode estar aqui ou em qualquer arquivo que este importe.
651
+ */
652
+ private parseNavigatorUsages;
653
+ /**
654
+ * Rotas declaradas FORA de qualquer `<X.Navigator>`.
655
+ *
656
+ * O CASO. A fase 2 desce a partir do `<X.Navigator>` e lê as `<X.Screen>`
657
+ * que estão DENTRO dele. Dois alvos do corpus não escrevem assim, e entre os
658
+ * dois são 171 rotas invisíveis:
659
+ *
660
+ * `bluesky` — `function commonScreens(Stack: typeof Flat) { return (<>
661
+ * <Stack.Screen name="NotFound" … /> … </>) }`, chamada de
662
+ * dentro de seis navegadores diferentes. 70 rotas.
663
+ * `comapeo` — `export const createAppScreens = ({intl}) => (<>
664
+ * <RootStack.Group><RootStack.Screen … /></RootStack.Group></>)`,
665
+ * num arquivo sem `<RootStack.Navigator>` nenhum. 101 rotas.
666
+ *
667
+ * A REGRA. Uma `<X.Screen name="…">` sem `<Y.Navigator>` ancestral é rota do
668
+ * navegador ao qual `X` se resolve. Não há palpite em jogo: o nome da rota é
669
+ * literal do fonte (ou constante que o resolvedor segue), e `X` precisa
670
+ * chegar a uma declaração de navegador que já existe. O que não resolve não
671
+ * vira nada.
672
+ *
673
+ * O ancestral é o que evita contar duas vezes — dentro do `<X.Navigator>` a
674
+ * fase 2 já leu, e somar aqui duplicaria cada rota do corpus inteiro.
675
+ *
676
+ * DUAS FORMAS DE RESOLVER `X`, e a segunda é o que o `bluesky` exige. A
677
+ * primeira é a de sempre (declaração local, ou `import` seguido até a
678
+ * origem) e resolve o `comapeo`. A segunda lê a ANOTAÇÃO DE TIPO do
679
+ * parâmetro: em `commonScreens(Stack: typeof Flat)`, quem diz que `Stack` é
680
+ * o navegador `Flat` é o próprio app, no fonte.
681
+ *
682
+ * O QUE ISTO NÃO FAZ. As 70 do `bluesky` ficam atribuídas a `Flat` — que as
683
+ * contém de fato (`{commonScreens(Flat, numUnread)}`) — e não aos outros
684
+ * cinco stacks que também chamam a mesma função. Seguir os seis pontos de
685
+ * chamada é análise interprocedural, e o ganho seria só de atribuição: o nome
686
+ * da rota e o arquivo da tela, que é o que o agente usa, já saem certos.
687
+ */
688
+ private parseDetachedScreens;
689
+ /**
690
+ * De um `X` usado como `<X.Screen>` até a declaração do navegador.
691
+ *
692
+ * Além do caminho normal — declaração local ou `import` seguido até a origem
693
+ * — aceita `X` como PARÂMETRO anotado com `typeof Y`. É o idioma do
694
+ * `bluesky`, e a anotação é declaração do app: nada aqui é inferido do nome.
695
+ */
696
+ private resolveDetachedNavigator;
697
+ /**
698
+ * De `<X.Navigator>` até a declaração de `X`.
699
+ *
700
+ * Primeiro no próprio arquivo; se não estiver, segue o `import` até onde `X`
701
+ * é definido. Resolver pelo import é o que torna a junção entre arquivos
702
+ * segura: dois `Stack` de arquivos diferentes nunca colidem, porque a chave
703
+ * é o arquivo de DEFINIÇÃO.
704
+ */
705
+ private resolveNavigator;
706
+ /** O valor string de um atributo JSX, resolvendo constante importada. */
707
+ private attributeString;
468
708
  /**
469
709
  * Is this JSX element `<navigatorVarName.MEMBER …>`?
470
710
  */
@@ -487,6 +727,14 @@ declare class NavigationAnalyzer {
487
727
  private collectScreenElements;
488
728
  /** Extract screens from a navigator JSX element */
489
729
  private extractScreensFromNavigator;
730
+ /**
731
+ * Uma `<X.Screen>` isolada até a rota que ela declara.
732
+ *
733
+ * Separado de `extractScreensFromNavigator` porque a MESMA leitura serve para
734
+ * a `<X.Screen>` que mora dentro do `<X.Navigator>` e para a que mora fora
735
+ * dele — só a forma de chegar até o elemento muda.
736
+ */
737
+ private parseScreenElement;
490
738
  /** Extract string attribute value from JSX attributes */
491
739
  private extractAttributeValue;
492
740
  /** Parse TypeScript type exports (ParamList types) */
@@ -571,6 +819,11 @@ interface PlatformAnalyzerResult {
571
819
  analyzedFiles: number;
572
820
  /** Number of files excluded by strict screen filtering, when applicable. */
573
821
  screensFilteredOut?: number;
822
+ /**
823
+ * O que a varredura alcançou. Opcional: o analisador web ainda não produz,
824
+ * e o relatório do CLI degrada omitindo a seção em vez de mentir zeros.
825
+ */
826
+ diagnostics?: AnalysisDiagnostics;
574
827
  }
575
828
  /**
576
829
  * Seam `MCPGenerator` consumes to turn a project's source tree into
@@ -907,6 +1160,14 @@ interface MCPOutput {
907
1160
  filePath: string;
908
1161
  format: 'json';
909
1162
  checksum: string;
1163
+ /**
1164
+ * O que a varredura alcançou, para o relatório do CLI.
1165
+ *
1166
+ * Fica FORA do `MCPDocument` de propósito: o documento é contrato com o
1167
+ * SDK, a API e o dashboard, e diagnóstico não é dado do app. Opcional
1168
+ * porque o analisador web não produz — quem consome trata a ausência.
1169
+ */
1170
+ diagnostics?: AnalysisDiagnostics;
910
1171
  }
911
1172
 
912
1173
  /**
@@ -1117,7 +1378,20 @@ declare function getEnvOverrides(env?: NodeJS.ProcessEnv): EnvOverrides;
1117
1378
  * @returns AppilotsConfig if a file or APPILOTS_API_KEY exists, null otherwise
1118
1379
  * @throws when the file is unparseable or the merged config fails validation
1119
1380
  */
1120
- declare function loadConfig(onWarn?: (message: string) => void): AppilotsConfig | null;
1381
+ interface LoadConfigOptions {
1382
+ /**
1383
+ * `false` quando o comando não fala com o servidor.
1384
+ *
1385
+ * `generate` é local: não precisa de `apiKey` e não deveria ser impedido por
1386
+ * ela faltar. Com `requireApiKey: true` (o padrão), um `.appilotsrc` que só
1387
+ * declara `screenPatterns` derruba a validação, `generate` engole o erro e
1388
+ * IGNORA a config inteira — o comentário dele dizia "não pode bloquear" e o
1389
+ * efeito real era "não pode ser lida". O resultado prático: `generate` e
1390
+ * `sync` produzem documentos diferentes para o mesmo projeto.
1391
+ */
1392
+ requireApiKey?: boolean;
1393
+ }
1394
+ declare function loadConfig(onWarn?: (message: string) => void, options?: LoadConfigOptions): AppilotsConfig | null;
1121
1395
  /**
1122
1396
  * Saves configuration to .appilotsrc in the given directory
1123
1397
  *
@@ -1322,6 +1596,6 @@ declare class AppilotsAPIClient {
1322
1596
  * banner. Kept identical to package.json by the release flow;
1323
1597
  * `version.test.ts` fails if the two ever disagree.
1324
1598
  */
1325
- declare const CLI_VERSION = "0.10.0";
1599
+ declare const CLI_VERSION = "0.11.3";
1326
1600
 
1327
1601
  export { type ActionDescriptor, type AnalyzerConfig, AppilotsAPIClient, type AppilotsConfig, type AppilotsManifest, CLI_VERSION, ComponentAnalyzer, type ComponentDescriptor, DEFAULT_MANIFEST_FILENAME, DEFAULT_WEB_SCREEN_PATTERNS, type EnvOverrides, type FlowDescriptor, type FlowStepDescriptor, FormAnalyzer, type FormDescriptor, type FormFieldDescriptor, GenericPlatformAnalyzer, type LoadManifestResult, type LocatorDescriptor, type MCPDocument, MCPGenerator, type MCPGeneratorConfig, type MCPGeneratorOptions, type MCPOutput, type MetadataLintWarning, NavigationAnalyzer, type NavigationGraph, type NavigationNode, type NavigatorDescriptor, type ParamDescriptor, type PlatformAnalyzer, type PlatformAnalyzerOptions, type PlatformAnalyzerResult, ReactNativePlatformAnalyzer, ReactWebPlatformAnalyzer, type ScreenAgentHints, ScreenAnalyzer, type ScreenDescriptor, type SignalDescriptor, type StatusResult, type SyncResult, type TargetDescriptor, type WaitPolicyDescriptor, WebNavigationAnalyzer, type WebNavigationResult, type WebRoute, WebScreenAnalyzer, formatMetadataWarnings, getConfigPath, getEnvOverrides, lintActionMetadata, loadConfig, loadManifest, mergeManifestNavigation, mergeManifestScreens, resolvePathToScreen, saveConfig, screenNameFromPath, validateConfig };