raptor-aios 0.8.2 → 0.9.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/CHANGELOG.md +18 -0
- package/README.md +27 -27
- package/dist/_core/dist/agents/context-file.js +8 -0
- package/dist/_core/dist/extensions/types.js +2 -0
- package/dist/_core/dist/presets/gol-smiles-deep.js +268 -0
- package/dist/_core/dist/presets/gol-smiles-gates.js +341 -0
- package/dist/_core/dist/presets/gol-smiles.js +77 -0
- package/dist/_core/dist/presets/index.js +4 -1
- package/dist/_core/dist/presets/registry.js +4 -0
- package/dist/_core/dist/presets/renderer.js +6 -0
- package/dist/_core/dist/verify/arch.js +82 -0
- package/dist/_core/dist/verify/i18n.js +182 -0
- package/dist/_core/dist/verify/index.js +3 -0
- package/dist/_core/dist/verify/tangerina.js +645 -0
- package/dist/_core/package.json +1 -1
- package/dist/_core/templates/plan.md.hbs +23 -2
- package/dist/_core/templates/raptor.yml.hbs +1 -1
- package/dist/_core/templates/spec.md.hbs +20 -1
- package/dist/commands/init.js +51 -18
- package/dist/commands/new.js +3 -0
- package/dist/commands/plan.js +3 -1
- package/dist/commands/preset/add.js +26 -1
- package/dist/commands/upgrade.js +13 -3
- package/dist/commands/verify/arch.js +116 -0
- package/dist/commands/verify/i18n.js +79 -0
- package/dist/commands/verify/tangerina.js +111 -0
- package/dist/shared/gol-verify.js +51 -0
- package/dist/shared/scaffold.js +60 -5
- package/extensions/gol-smiles/extension.yml +28 -0
- package/extensions/gol-smiles/knowledge/app-kit-catalog.md +54 -0
- package/extensions/gol-smiles/knowledge/composition-recipes.md +84 -0
- package/extensions/gol-smiles/knowledge/conformity-checklist.md +34 -0
- package/extensions/gol-smiles/knowledge/figma-tangerina-map.json +927 -0
- package/extensions/gol-smiles/knowledge/tangerina-catalog.md +39 -0
- package/extensions/gol-smiles/knowledge/tokens-reference.md +32 -0
- package/package.json +1 -1
- package/scripts/generate-tangerina-design-map.mjs +147 -0
- package/scripts/prepare-npm.mjs +1 -1
- package/templates/plan-template.md +3 -0
- package/templates/spec-template.md +12 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,24 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
5
|
|
|
6
|
+
## [0.9.0] - 2026-06-11
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **Preset `gol-smiles` (G1–G6), empilhável sobre `mobile-opinionated`.** Novo preset bundled (`packages/core/src/presets/gol-smiles.ts` + `gol-smiles-gates.ts`) focado no ecossistema Gol/Smiles (RN + Design System Tangerina, arquitetura `modernization/`): **G1** Tangerina-first UI (vocabulário fechado de componentes/receitas), **G2** arquitetura moderna (Zustand + RHF/Zod), **G3** i18n pt-BR/en/es, **G4** contrato de analytics, **G5** rollout por feature flag, **G6** paridade multi-marca — todos com opt-out honesto. Registrado no `REGISTRY` com aliases `gol`/`smiles`. `init --preset mobile-opinionated,gol-smiles` empilha os 12 artigos e 12 gates; `raptor preset add gol-smiles` ativa num projeto existente.
|
|
11
|
+
- **Render multi-preset na constituição (D-10).** `raptor init --preset a,b` aceita um stack comma-separated e renderiza os artigos de **todos** os presets (`renderPresetsArticles`, atribuição de fonte por preset), gravando `presets:` no `raptor.yml`; `raptor preset add` **anexa** os artigos do novo preset à constituição (append seguro e idempotente, sem sobrescrever artigos do projeto).
|
|
12
|
+
- **Knowledge pack via extensão de preset (D-3).** O schema `raptor.extension/v1` ganhou `provides.knowledge[]` e `requires.preset`; o scaffold instala extensões **condicionalmente ao preset ativo** (`installBundledExtensions`/`collectFrameworkFiles` filtram; `collectKnowledgeRefs` alimenta o bloco de contexto). `buildContextBlock` ganhou a seção "Knowledge packs". A extensão `extensions/gol-smiles/` traz 5 catálogos (Tangerina, kit interno com classificação A/B/C/D, receitas de composição, tokens, 19 regras de conformidade) + o dicionário Figma→Tangerina, referenciados no `CLAUDE.md`/`AGENTS.md`.
|
|
13
|
+
- **Frontmatter ciente do preset (D-2).** `spec.md.hbs`/`plan.md.hbs` emitem os blocos G* (`ui`/`i18n`/`analytics`/`brands` na spec; `architecture`/`rollout` no plan) **somente** quando `gol-smiles` está ativo — projetos non-gol ficam intactos. Mirrors de doc sincronizados.
|
|
14
|
+
- **Verificadores `raptor verify tangerina | i18n | arch` (Fase 3).** Runners reais que medem o código (`packages/core/src/verify/`): tokens literais/lista negra do kit/receita-base e receitas declaradas (tangerina, com modo `--design`), paridade de chaves pt-BR/en/es (i18n), imports banidos e aliases (arch). Achados carregam `regra`/`receita#asserção` + `arquivo:linha` + severidade (`error` bloqueia; `warning` só com `--strict`).
|
|
15
|
+
- **Modo deep gate→verify (D-7), opt-in.** Com `gol_smiles.deep_verify: true` no `raptor.yml`, os gates G1/G2/G3 também executam os runners na avaliação e agregam o resultado em `details` (erro determinístico no código reprova o gate). Sem o opt-in, os gates seguem puramente declarativos (padrão dos gates mobile).
|
|
16
|
+
|
|
17
|
+
## [0.8.3] - 2026-06-10
|
|
18
|
+
|
|
19
|
+
### Documentation
|
|
20
|
+
|
|
21
|
+
- **Glossário Canônico (`docs/glossary.md`) reescrito e certificado contra o código.** Passou de ~16 termos para ~60 conceitos cobrindo todo o ecossistema (Gate, GateLevel, ADR, Override, Canonical, Blueprint, Priority Stack, Preset, Extension, Hook, Skill, MCP, Harness, SDD, Stage, Workflow, Traceability/Coverage/Trace, Staleness, Readiness, M1–M6/M7/M8, C1–C5, D#, artefatos e memória), cada um no modelo Nome/Objetivo/Definição/Responsabilidades/Quando usar/Quando não usar/Relação/Prioridade/Exemplos/Anti-padrões. Inclui apêndices com as hierarquias de governança/decisão/documentos/execução/contexto, a distinção C/D/M, o catálogo dos 42 gates (nível + artigo) e o catálogo de comandos CLI. Cada entrada cita `arquivo:linha`.
|
|
22
|
+
- **README atualizado para refletir o código atual.** Nova seção "Documentação & glossário" (com link em destaque para o glossário, tabela dos docs e diagrama mermaid da hierarquia de governança) e entrada no índice. Correções confrontadas com `packages/core`/`packages/cli`: contagem de testes (1019 = 784 core + 201 cli + 34 e2e), workflows bundled (`sdd-cycle`, `sdd-cycle-quick`), hooks (34 pontos = before/after de 17 comandos), tabelas "Capacidades" e "Referência CLI" com markdown corrigido, badge de versão, e menção ao preset `lean`.
|
|
23
|
+
|
|
6
24
|
## [0.8.2] - 2026-06-10
|
|
7
25
|
|
|
8
26
|
### Fixed
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
[](https://nodejs.org)
|
|
9
9
|
[](https://reactnative.dev)
|
|
10
10
|
[](#️-desenvolvimento-local)
|
|
11
|
-
[](CHANGELOG.md)
|
|
12
12
|
[](LICENSE)
|
|
13
13
|
|
|
14
14
|
</div>
|
|
@@ -360,14 +360,14 @@ Gates são checagens versionadas com um **nível**: 🔴 `critical` (bloqueia, i
|
|
|
360
360
|
|
|
361
361
|
Cada gate exige campos no *frontmatter* de `spec.md` ou `plan.md` — declarativos, verificáveis e (para alguns) **mensuráveis** via `verify`.
|
|
362
362
|
|
|
363
|
-
| Gate | Nível |
|
|
364
|
-
| --------------------------------------------- | ----- |
|
|
363
|
+
| Gate | Nível | Exige no frontmatter | Verificável por |
|
|
364
|
+
| --------------------------------------------- | ----- | --------------------------------------------------------------------------------------------------------------------- | ------------------ |
|
|
365
365
|
| **M1 — App/Play Store Compliance** | 🟡 | `spec.stores.ios_permissions`, `.android_permissions` (ou `stores.no_restricted_apis: "<motivo>"` p/ opt-out honesto) | `verify stores` |
|
|
366
|
-
| **M2 — Privacidade & Residência (LGPD/GDPR)** | 🟡 | `plan.privacy.lawful_basis`, `.residency`, `.retention`
|
|
367
|
-
| **M3 — Acessibilidade (WCAG AA)** | 🟡 | `spec.a11y.wcag_level`, `.criteria`
|
|
368
|
-
| **M4 — Matriz de SO** | 🟡 | `plan.os_matrix.ios_min`, `.android_min`
|
|
369
|
-
| **M5 — Orçamento de Performance** | 🟡 | `plan.perf_budget.ttfi_ms`, `.interactions_ms`
|
|
370
|
-
| **M6 — Crash-Free SLO & Observabilidade** | 🔵 | `plan.crash_free_slo` (≥99.5), `plan.observability.provider`
|
|
366
|
+
| **M2 — Privacidade & Residência (LGPD/GDPR)** | 🟡 | `plan.privacy.lawful_basis`, `.residency`, `.retention` | — |
|
|
367
|
+
| **M3 — Acessibilidade (WCAG AA)** | 🟡 | `spec.a11y.wcag_level`, `.criteria` | `verify a11y` |
|
|
368
|
+
| **M4 — Matriz de SO** | 🟡 | `plan.os_matrix.ios_min`, `.android_min` | `verify os-matrix` |
|
|
369
|
+
| **M5 — Orçamento de Performance** | 🟡 | `plan.perf_budget.ttfi_ms`, `.interactions_ms` | `verify perf` |
|
|
370
|
+
| **M6 — Crash-Free SLO & Observabilidade** | 🔵 | `plan.crash_free_slo` (≥99.5), `plan.observability.provider` | — |
|
|
371
371
|
|
|
372
372
|
### 🔗 M7 — Rastreabilidade código↔spec
|
|
373
373
|
|
|
@@ -454,17 +454,17 @@ Declarar não é cumprir. O Raptor **mede** e compara com o declarado:
|
|
|
454
454
|
|
|
455
455
|
## 🧰 Capacidades do Raptor
|
|
456
456
|
|
|
457
|
-
| Capacidade |
|
|
458
|
-
| ----------------------- |
|
|
459
|
-
| 🎚️ **Presets** | Conjuntos reusáveis de artigos + gates, empilháveis. Bundled: `mobile-opinionated`, `lean`.
|
|
457
|
+
| Capacidade | Para que serve | Comandos |
|
|
458
|
+
| ----------------------- | -------------------------------------------------------------------------------------------------- | -------------------------------------------------------- |
|
|
459
|
+
| 🎚️ **Presets** | Conjuntos reusáveis de artigos + gates, empilháveis. Bundled: `mobile-opinionated`, `lean`. | `raptor preset list · add · info · remove` |
|
|
460
460
|
| 🔁 **Workflows** | Orquestram o ciclo via YAML, com gates de revisão humana. Bundled: `sdd-cycle`, `sdd-cycle-quick`. | `raptor workflow catalog · run · status · resume · list` |
|
|
461
|
-
| 🤖 **Agentes** | Materializa os slash commands por agente.
|
|
462
|
-
| 🧠 **Skills** | Habilidades reusáveis materializadas para os agentes.
|
|
463
|
-
| 🔌 **MCP** | Registra servidores MCP e materializa nas configs nativas dos agentes.
|
|
464
|
-
| 🪝 **Hooks** | Scripts disparados em **34 pontos** do ciclo (`before_*`/`after_*` de 17 comandos).
|
|
465
|
-
| 📦 **Extensões** | Pacotes self-contained (templates/gates/hooks/workflows).
|
|
466
|
-
| 🎟️ **Jira** | Puxa issues do Jira (somente leitura) para semear specs.
|
|
467
|
-
| 📜 **Auditoria & trace** | Consulta a trilha de eventos e a rastreabilidade.
|
|
461
|
+
| 🤖 **Agentes** | Materializa os slash commands por agente. | `raptor add-agent` · `raptor list-agents` |
|
|
462
|
+
| 🧠 **Skills** | Habilidades reusáveis materializadas para os agentes. | `raptor skill add · list · sync · remove` |
|
|
463
|
+
| 🔌 **MCP** | Registra servidores MCP e materializa nas configs nativas dos agentes. | `raptor mcp add · list · sync · remove` |
|
|
464
|
+
| 🪝 **Hooks** | Scripts disparados em **34 pontos** do ciclo (`before_*`/`after_*` de 17 comandos). | `raptor hook list · run` |
|
|
465
|
+
| 📦 **Extensões** | Pacotes self-contained (templates/gates/hooks/workflows). | `raptor extension add · list · info · remove` |
|
|
466
|
+
| 🎟️ **Jira** | Puxa issues do Jira (somente leitura) para semear specs. | `raptor jira connect · status · pull · disconnect` |
|
|
467
|
+
| 📜 **Auditoria & trace** | Consulta a trilha de eventos e a rastreabilidade. | `raptor audit query · show` · `raptor trace` |
|
|
468
468
|
|
|
469
469
|
### 🎟️ Integração com Jira
|
|
470
470
|
|
|
@@ -554,15 +554,15 @@ CLAUDE.md # 📎 bloco de contexto do agente
|
|
|
554
554
|
|
|
555
555
|
Comece pelo **[📖 Glossário Canônico](docs/glossary.md)** — a referência normativa de todos os termos, gates, hierarquias e anti-padrões do Raptor (Gate, ADR, Override, Canonical, Preset, M1–M8, C1–C5, traceability…). É a base de onboarding para usuários, mantenedores e agentes IA.
|
|
556
556
|
|
|
557
|
-
|
|
|
558
|
-
|
|
|
559
|
-
| **[📖 docs/glossary.md](docs/glossary.md)**
|
|
560
|
-
| [🛡️ docs/readiness-gates.md](docs/readiness-gates.md)
|
|
561
|
-
| [🎨 docs/design-gate.md](docs/design-gate.md)
|
|
562
|
-
| [🔗 docs/artifact-chain.md](docs/artifact-chain.md)
|
|
563
|
-
| [🎟️ docs/jira-spec-enrichment.md](docs/jira-spec-enrichment.md)
|
|
564
|
-
| [📐 docs/templates-architecture.md](docs/templates-architecture.md) | Dualidade de templates (`.hbs` vs `*-template.md`) e Priority Stack
|
|
565
|
-
| [✅ docs/spec-kit-parity.md](docs/spec-kit-parity.md)
|
|
557
|
+
| Documento | Sobre |
|
|
558
|
+
| ------------------------------------------------------------------ | ---------------------------------------------------------------------------------- |
|
|
559
|
+
| **[📖 docs/glossary.md](docs/glossary.md)** | Glossário canônico: conceitos, hierarquias, catálogo de 42 gates e de comandos CLI |
|
|
560
|
+
| [🛡️ docs/readiness-gates.md](docs/readiness-gates.md) | Gates de prontidão (`spec/plan/tasks.ready`) em detalhe |
|
|
561
|
+
| [🎨 docs/design-gate.md](docs/design-gate.md) | Design gate, integração Figma e `assets-manifest.json` |
|
|
562
|
+
| [🔗 docs/artifact-chain.md](docs/artifact-chain.md) | Cadeia de artefatos e rastreabilidade spec→plan→tasks→código |
|
|
563
|
+
| [🎟️ docs/jira-spec-enrichment.md](docs/jira-spec-enrichment.md) | Enriquecimento de spec a partir de cards do Jira |
|
|
564
|
+
| [📐 docs/templates-architecture.md](docs/templates-architecture.md) | Dualidade de templates (`.hbs` vs `*-template.md`) e Priority Stack |
|
|
565
|
+
| [✅ docs/spec-kit-parity.md](docs/spec-kit-parity.md) | Paridade com o GitHub Spec Kit |
|
|
566
566
|
|
|
567
567
|
### Hierarquia de governança (quem manda)
|
|
568
568
|
|
|
@@ -21,6 +21,14 @@ export function buildContextBlock(opts) {
|
|
|
21
21
|
lines.push(`\n## Applicable articles\n`);
|
|
22
22
|
lines.push(`- ${opts.constitutionArticles.join(", ")}`);
|
|
23
23
|
}
|
|
24
|
+
if (opts.knowledge && opts.knowledge.length > 0) {
|
|
25
|
+
lines.push(`\n## Knowledge packs\n`);
|
|
26
|
+
lines.push("Preset knowledge installed for this project. READ the relevant file before generating code so the output matches the house patterns (components, recipes, tokens, conventions):");
|
|
27
|
+
lines.push("");
|
|
28
|
+
for (const ref of opts.knowledge) {
|
|
29
|
+
lines.push(`- \`${ref}\``);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
24
32
|
lines.push("\n## Raptor Commands (slash commands)\n");
|
|
25
33
|
lines.push("- `/raptor-new` — create a new feature spec");
|
|
26
34
|
lines.push("- `/raptor-clarify` — detect and resolve [NEEDS CLARIFICATION] in spec");
|
|
@@ -26,6 +26,7 @@ export const EXTENSION_JSON_SCHEMA = {
|
|
|
26
26
|
type: "object",
|
|
27
27
|
properties: {
|
|
28
28
|
raptor_version: { type: "string" },
|
|
29
|
+
preset: { type: "string" },
|
|
29
30
|
},
|
|
30
31
|
additionalProperties: false,
|
|
31
32
|
},
|
|
@@ -37,6 +38,7 @@ export const EXTENSION_JSON_SCHEMA = {
|
|
|
37
38
|
hooks: { type: "array", items: { type: "string" } },
|
|
38
39
|
workflows: { type: "array", items: { type: "string" } },
|
|
39
40
|
presets: { type: "array", items: { type: "string" } },
|
|
41
|
+
knowledge: { type: "array", items: { type: "string" } },
|
|
40
42
|
},
|
|
41
43
|
additionalProperties: false,
|
|
42
44
|
},
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { parse as parseYaml } from "yaml";
|
|
4
|
+
import { parseFrontmatter } from "../frontmatter/index.js";
|
|
5
|
+
import { analyzeGolArch, analyzeI18nUsage, parseTangerinaDesignMap, scanTangerinaFiles, } from "../verify/index.js";
|
|
6
|
+
export function readGolDeepConfig(projectRoot) {
|
|
7
|
+
const path = join(projectRoot, ".raptor", "raptor.yml");
|
|
8
|
+
if (!existsSync(path))
|
|
9
|
+
return { enabled: false };
|
|
10
|
+
let parsed;
|
|
11
|
+
try {
|
|
12
|
+
parsed = parseYaml(readFileSync(path, "utf8"));
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return { enabled: false };
|
|
16
|
+
}
|
|
17
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
18
|
+
return { enabled: false };
|
|
19
|
+
const section = parsed["gol_smiles"];
|
|
20
|
+
if (typeof section !== "object" || section === null)
|
|
21
|
+
return { enabled: false };
|
|
22
|
+
const s = section;
|
|
23
|
+
const cfg = { enabled: s["deep_verify"] === true };
|
|
24
|
+
if (typeof s["source_root"] === "string")
|
|
25
|
+
cfg.sourceRoot = s["source_root"];
|
|
26
|
+
return cfg;
|
|
27
|
+
}
|
|
28
|
+
const SKIP_DIRS = new Set([
|
|
29
|
+
"node_modules",
|
|
30
|
+
".raptor",
|
|
31
|
+
".git",
|
|
32
|
+
"android",
|
|
33
|
+
"ios",
|
|
34
|
+
"dist",
|
|
35
|
+
"build",
|
|
36
|
+
"__tests__",
|
|
37
|
+
"__test__",
|
|
38
|
+
]);
|
|
39
|
+
export function collectGolSourceFiles(dir, exts) {
|
|
40
|
+
const out = [];
|
|
41
|
+
const walk = (d) => {
|
|
42
|
+
let entries;
|
|
43
|
+
try {
|
|
44
|
+
entries = readdirSync(d);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
for (const e of entries) {
|
|
50
|
+
if (SKIP_DIRS.has(e))
|
|
51
|
+
continue;
|
|
52
|
+
const p = join(d, e);
|
|
53
|
+
let st;
|
|
54
|
+
try {
|
|
55
|
+
st = statSync(p);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (st.isDirectory())
|
|
61
|
+
walk(p);
|
|
62
|
+
else if (exts.some((x) => e.endsWith(x)) && !/\.test\.|\.spec\./.test(e)) {
|
|
63
|
+
out.push(p);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
walk(dir);
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
export function readGolFeatureDecls(featureDir) {
|
|
71
|
+
const decls = { recipes: [] };
|
|
72
|
+
const specPath = join(featureDir, "spec.md");
|
|
73
|
+
if (existsSync(specPath)) {
|
|
74
|
+
const { data } = parseFrontmatter(readFileSync(specPath, "utf8"));
|
|
75
|
+
if (Array.isArray(data.ui?.recipes))
|
|
76
|
+
decls.recipes = data.ui.recipes;
|
|
77
|
+
if (typeof data.ui?.scope === "string")
|
|
78
|
+
decls.uiScope = data.ui.scope;
|
|
79
|
+
if (typeof data.i18n?.scope === "string")
|
|
80
|
+
decls.i18nScope = data.i18n.scope;
|
|
81
|
+
}
|
|
82
|
+
const planPath = join(featureDir, "plan.md");
|
|
83
|
+
if (existsSync(planPath)) {
|
|
84
|
+
const { data } = parseFrontmatter(readFileSync(planPath, "utf8"));
|
|
85
|
+
if (typeof data.architecture?.module === "string")
|
|
86
|
+
decls.module = data.architecture.module;
|
|
87
|
+
if (typeof data.architecture?.layer === "string")
|
|
88
|
+
decls.layer = data.architecture.layer;
|
|
89
|
+
}
|
|
90
|
+
return decls;
|
|
91
|
+
}
|
|
92
|
+
export function resolveGolScanRoot(projectRoot, opts = {}) {
|
|
93
|
+
if (opts.pathFlag) {
|
|
94
|
+
const target = join(projectRoot, opts.pathFlag);
|
|
95
|
+
return {
|
|
96
|
+
scanRoot: existsSync(target) ? target : projectRoot,
|
|
97
|
+
source: "path-flag",
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (opts.sourceRoot) {
|
|
101
|
+
const target = join(projectRoot, opts.sourceRoot);
|
|
102
|
+
if (existsSync(target))
|
|
103
|
+
return { scanRoot: target, source: "config" };
|
|
104
|
+
}
|
|
105
|
+
if (opts.module) {
|
|
106
|
+
const modulesDir = join(projectRoot, "modernization", "modules");
|
|
107
|
+
if (existsSync(modulesDir)) {
|
|
108
|
+
const match = readdirSync(modulesDir).find((d) => d.toLowerCase() === opts.module.toLowerCase());
|
|
109
|
+
if (match)
|
|
110
|
+
return { scanRoot: join(modulesDir, match), source: "plan-module" };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
for (const candidate of ["modernization", "src"]) {
|
|
114
|
+
const p = join(projectRoot, candidate);
|
|
115
|
+
if (existsSync(p))
|
|
116
|
+
return { scanRoot: p, source: "default" };
|
|
117
|
+
}
|
|
118
|
+
return { scanRoot: projectRoot, source: "default" };
|
|
119
|
+
}
|
|
120
|
+
export const GOL_DESIGN_MAP_PATH = join(".raptor", "extensions", "gol-smiles", "knowledge", "figma-tangerina-map.json");
|
|
121
|
+
export function loadInstalledDesignMap(projectRoot) {
|
|
122
|
+
const path = join(projectRoot, GOL_DESIGN_MAP_PATH);
|
|
123
|
+
if (!existsSync(path))
|
|
124
|
+
return null;
|
|
125
|
+
const { map } = parseTangerinaDesignMap(readFileSync(path, "utf8"));
|
|
126
|
+
return map;
|
|
127
|
+
}
|
|
128
|
+
export function collectGolTranslationResources(projectRoot, scanRoot, languages) {
|
|
129
|
+
const out = [];
|
|
130
|
+
const seen = new Set();
|
|
131
|
+
const push = (p, language, scope) => {
|
|
132
|
+
if (seen.has(p) || !existsSync(p))
|
|
133
|
+
return;
|
|
134
|
+
seen.add(p);
|
|
135
|
+
out.push({
|
|
136
|
+
language,
|
|
137
|
+
path: p.replace(projectRoot + "/", ""),
|
|
138
|
+
content: readFileSync(p, "utf8"),
|
|
139
|
+
scope,
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
const walk = (d, scope) => {
|
|
143
|
+
let entries;
|
|
144
|
+
try {
|
|
145
|
+
entries = readdirSync(d);
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
for (const e of entries) {
|
|
151
|
+
if (SKIP_DIRS.has(e))
|
|
152
|
+
continue;
|
|
153
|
+
const p = join(d, e);
|
|
154
|
+
let st;
|
|
155
|
+
try {
|
|
156
|
+
st = statSync(p);
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (!st.isDirectory())
|
|
162
|
+
continue;
|
|
163
|
+
const effective = scope === "module" || p === scanRoot || p.startsWith(scanRoot + "/")
|
|
164
|
+
? "module"
|
|
165
|
+
: "global";
|
|
166
|
+
if (e.toLowerCase() === "translations") {
|
|
167
|
+
for (const lang of languages)
|
|
168
|
+
push(join(p, `${lang}.json`), lang, effective);
|
|
169
|
+
}
|
|
170
|
+
walk(p, effective);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
walk(scanRoot, "module");
|
|
174
|
+
for (const base of ["modernization", "src", "modules"]) {
|
|
175
|
+
const dir = join(projectRoot, base);
|
|
176
|
+
if (existsSync(dir))
|
|
177
|
+
walk(dir, "global");
|
|
178
|
+
}
|
|
179
|
+
for (const lang of languages) {
|
|
180
|
+
push(join(projectRoot, "src", "Translations", "lang", `${lang}.json`), lang, "global");
|
|
181
|
+
}
|
|
182
|
+
return out;
|
|
183
|
+
}
|
|
184
|
+
function readRel(projectRoot, files) {
|
|
185
|
+
return files.map((p) => ({
|
|
186
|
+
path: p.replace(projectRoot + "/", ""),
|
|
187
|
+
content: readFileSync(p, "utf8"),
|
|
188
|
+
}));
|
|
189
|
+
}
|
|
190
|
+
const SAMPLE = 5;
|
|
191
|
+
function sampleOf(report) {
|
|
192
|
+
return report.findings
|
|
193
|
+
.filter((f) => f.severity === "error")
|
|
194
|
+
.slice(0, SAMPLE)
|
|
195
|
+
.map((f) => ({ rule: f.rule, file: f.file, line: f.line, message: f.message }));
|
|
196
|
+
}
|
|
197
|
+
export function runGolDeepTangerina(projectRoot, featureDir) {
|
|
198
|
+
const cfg = readGolDeepConfig(projectRoot);
|
|
199
|
+
if (!cfg.enabled)
|
|
200
|
+
return { skipped: "deep_verify off" };
|
|
201
|
+
const decls = readGolFeatureDecls(featureDir);
|
|
202
|
+
if (decls.uiScope === "none")
|
|
203
|
+
return { skipped: "ui.scope: none" };
|
|
204
|
+
const { scanRoot } = resolveGolScanRoot(projectRoot, {
|
|
205
|
+
sourceRoot: cfg.sourceRoot,
|
|
206
|
+
module: decls.module,
|
|
207
|
+
});
|
|
208
|
+
const files = collectGolSourceFiles(scanRoot, [".tsx", ".jsx"]);
|
|
209
|
+
if (files.length === 0)
|
|
210
|
+
return { skipped: `no UI source under ${scanRoot}` };
|
|
211
|
+
const map = loadInstalledDesignMap(projectRoot);
|
|
212
|
+
const report = scanTangerinaFiles(readRel(projectRoot, files), {
|
|
213
|
+
declaredRecipes: decls.recipes,
|
|
214
|
+
knownIcons: map?.icons ?? [],
|
|
215
|
+
});
|
|
216
|
+
return {
|
|
217
|
+
filesScanned: report.filesScanned,
|
|
218
|
+
errors: report.errors,
|
|
219
|
+
warnings: report.warnings,
|
|
220
|
+
sample: sampleOf(report),
|
|
221
|
+
command: "raptor verify tangerina",
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
export function runGolDeepArch(projectRoot, featureDir) {
|
|
225
|
+
const cfg = readGolDeepConfig(projectRoot);
|
|
226
|
+
if (!cfg.enabled)
|
|
227
|
+
return { skipped: "deep_verify off" };
|
|
228
|
+
const decls = readGolFeatureDecls(featureDir);
|
|
229
|
+
const { scanRoot } = resolveGolScanRoot(projectRoot, {
|
|
230
|
+
sourceRoot: cfg.sourceRoot,
|
|
231
|
+
module: decls.module,
|
|
232
|
+
});
|
|
233
|
+
const files = collectGolSourceFiles(scanRoot, [".ts", ".tsx", ".js", ".jsx"]);
|
|
234
|
+
if (files.length === 0)
|
|
235
|
+
return { skipped: `no source under ${scanRoot}` };
|
|
236
|
+
const report = analyzeGolArch(readRel(projectRoot, files));
|
|
237
|
+
return {
|
|
238
|
+
filesScanned: report.filesScanned,
|
|
239
|
+
errors: report.errors,
|
|
240
|
+
warnings: report.warnings,
|
|
241
|
+
sample: sampleOf(report),
|
|
242
|
+
command: "raptor verify arch",
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
export function runGolDeepI18n(projectRoot, featureDir, languages) {
|
|
246
|
+
const cfg = readGolDeepConfig(projectRoot);
|
|
247
|
+
if (!cfg.enabled)
|
|
248
|
+
return { skipped: "deep_verify off" };
|
|
249
|
+
const decls = readGolFeatureDecls(featureDir);
|
|
250
|
+
if (decls.i18nScope === "none")
|
|
251
|
+
return { skipped: "i18n.scope: none" };
|
|
252
|
+
const { scanRoot } = resolveGolScanRoot(projectRoot, {
|
|
253
|
+
sourceRoot: cfg.sourceRoot,
|
|
254
|
+
module: decls.module,
|
|
255
|
+
});
|
|
256
|
+
const files = collectGolSourceFiles(scanRoot, [".ts", ".tsx", ".js", ".jsx"]);
|
|
257
|
+
if (files.length === 0)
|
|
258
|
+
return { skipped: `no source under ${scanRoot}` };
|
|
259
|
+
const resources = collectGolTranslationResources(projectRoot, scanRoot, languages);
|
|
260
|
+
const report = analyzeI18nUsage(readRel(projectRoot, files), resources, { languages });
|
|
261
|
+
return {
|
|
262
|
+
filesScanned: report.filesScanned,
|
|
263
|
+
errors: report.errors,
|
|
264
|
+
warnings: report.warnings,
|
|
265
|
+
sample: sampleOf(report),
|
|
266
|
+
command: "raptor verify i18n",
|
|
267
|
+
};
|
|
268
|
+
}
|