jarvis-ai-framework 2.1.0 → 3.0.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/AGENTS.md +22 -122
- package/agents/AGENTS.md +20 -24
- package/agents/README.md +25 -129
- package/agents/engineering/data/eng.data-engineer.agent.md +12 -35
- package/agents/engineering/eng.agent.md +18 -30
- package/agents/engineering/eng.bug-hunter.md +9 -12
- package/agents/engineering/eng.dev-code-reviewer.md +5 -1
- package/agents/engineering/eng.docs-writer.md +3 -3
- package/agents/engineering/eng.frontend.agent.md +13 -8
- package/agents/engineering/eng.rpa.agent.md +5 -13
- package/agents/engineering/eng.ux-designer.agent.md +4 -0
- package/agents/engineering/qa/eng.qa.quality-champion-task-agent.md +4 -0
- package/agents/engineering/qa/eng.qa.quality-strategist.md +13 -8
- package/agents/engineering/qa/eng.qa.test-architect.md +16 -15
- package/agents/engineering/qa/eng.qa.test-planner.md +22 -17
- package/agents/engineering/qa/eng.qa.testing-engineer.md +99 -15
- package/agents/product/prod.discovery-interviewer.md +22 -3
- package/agents/product/prod.pm-checker.md +4 -0
- package/bin/README.md +142 -0
- package/bin/commands/docs-publish.js +105 -141
- package/bin/commands/docs-sync.js +66 -104
- package/bin/commands/info.js +7 -27
- package/bin/commands/init.js +23 -51
- package/bin/commands/install-rtk.js +9 -3
- package/bin/commands/list.js +5 -18
- package/bin/commands/map.js +58 -0
- package/bin/commands/qa-signoff.js +58 -55
- package/bin/commands/tokens.js +90 -0
- package/bin/commands/whoami.js +30 -26
- package/bin/jarvis.js +9 -153
- package/bin/lib/auth/session.js +20 -20
- package/bin/lib/cli/program.js +219 -0
- package/bin/lib/cli/shared-options.js +29 -0
- package/bin/lib/config/constants.js +70 -2
- package/bin/lib/config/ide-config.js +4 -8
- package/bin/lib/core/profile-filter.js +116 -0
- package/bin/lib/core/scanner.js +5 -31
- package/bin/lib/core/sync-engine.js +21 -14
- package/bin/lib/core/token-report.js +100 -0
- package/bin/lib/docs/fetch-file.js +39 -0
- package/bin/lib/docs/publish-file.js +194 -0
- package/bin/lib/docs/redis-cache.js +49 -0
- package/bin/lib/docs/validate-frontmatter.js +9 -39
- package/bin/lib/env-loader.js +11 -34
- package/bin/lib/flow-map/index.js +65 -0
- package/bin/lib/flow-map/parse.js +223 -0
- package/bin/lib/flow-map/render.js +89 -0
- package/bin/lib/flow-map/tree.js +248 -0
- package/bin/lib/tasks/comment.js +40 -35
- package/bin/lib/utils/frontmatter.js +57 -0
- package/bin/lib/utils/git-parser.js +4 -6
- package/bin/lib/utils/npmrc-parser.js +6 -13
- package/bin/lib/utils/paths.js +2 -4
- package/bin/lib/utils/ui.js +3 -9
- package/bin/lib/vcs/api.js +25 -41
- package/bin/lib/vcs/create-issue.js +49 -33
- package/bin/lib/vcs/create-merge.js +47 -31
- package/bin/lib/vcs/fetch-raw.js +35 -23
- package/members.md +1 -1
- package/package.json +15 -4
- package/rules/AGENTS.md +18 -7
- package/rules/engineering/eng-rules.md +3 -13
- package/rules/engineering/eng-security-rules.md +19 -18
- package/rules/engineering/eng.bump-rules.md +3 -2
- package/rules/engineering/eng.docs-scraping-rules.md +4 -4
- package/rules/engineering/eng.integrations-rules.md +3 -7
- package/rules/engineering/eng.skills-rules.md +1 -5
- package/rules/engineering/eng.specializations-rules.md +18 -8
- package/rules/engineering/frontend/eng.frontend-rules.md +1 -4
- package/rules/engineering/qa/eng.qa.cypress-standards-rules.md +1 -1
- package/rules/engineering/rpa/eng.rpa-rules.md +4 -9
- package/rules/product/README.md +1 -0
- package/rules/product/prod-rules.md +1 -4
- package/rules/product/prod.spec-rules.md +0 -4
- package/rules/rtk-rules.md +0 -1
- package/{rules → rules-on-demand}/engineering/eng.breakdown-subtasks-rules.md +0 -4
- package/{rules → rules-on-demand}/engineering/eng.plan-rules.md +0 -4
- package/{rules → rules-on-demand}/engineering/eng.pr-rules.md +0 -4
- package/{rules → rules-on-demand}/engineering/eng.pre-pr-rules.md +0 -4
- package/{rules → rules-on-demand}/engineering/eng.start-rules.md +0 -4
- package/{rules → rules-on-demand}/engineering/eng.tech-spec-rules.md +1 -2
- package/{rules → rules-on-demand}/engineering/eng.work-rules.md +0 -4
- package/skills/AGENTS.md +69 -106
- package/skills/SKILLS-ROADMAP.md +47 -132
- package/skills/eng-ai/SKILL.md +270 -0
- package/skills/eng-ai/references/1-fundamentos-ml.md +24 -0
- package/skills/eng-ai/references/10-arquitetura-solucoes-ia.md +24 -0
- package/skills/eng-ai/references/11-governanca-ia.md +24 -0
- package/skills/eng-ai/references/12-seguranca-ia-responsavel.md +25 -0
- package/skills/eng-ai/references/2-matematica-estatistica.md +24 -0
- package/skills/eng-ai/references/3-dados-para-ia.md +24 -0
- package/skills/eng-ai/references/4-deep-learning-fundacionais.md +24 -0
- package/skills/eng-ai/references/5-llms-chatbots-agentes-rag.md +25 -0
- package/skills/eng-ai/references/6-avaliacao-experimentacao.md +24 -0
- package/skills/eng-ai/references/7-engenharia-sistemas-ia.md +24 -0
- package/skills/eng-ai/references/8-mlops-plataforma.md +24 -0
- package/skills/eng-ai/references/9-produto-ia.md +24 -0
- package/skills/eng-ai/references/especializacoes.md +17 -0
- package/skills/eng-automation/SKILL.md +290 -0
- package/skills/eng-automation/references/1-fundamentos-automacao.md +25 -0
- package/skills/eng-automation/references/10-ingestao-dados.md +25 -0
- package/skills/eng-automation/references/11-captcha-antibot.md +25 -0
- package/skills/eng-automation/references/12-limites-taxa-sessoes.md +25 -0
- package/skills/eng-automation/references/13-agendamento-orquestracao.md +24 -0
- package/skills/eng-automation/references/14-retentativa-erros.md +25 -0
- package/skills/eng-automation/references/15-autenticacao-credenciais.md +24 -0
- package/skills/eng-automation/references/16-observabilidade-confiabilidade.md +24 -0
- package/skills/eng-automation/references/2-aquisicao-dados.md +25 -0
- package/skills/eng-automation/references/3-automacao-navegador.md +24 -0
- package/skills/eng-automation/references/4-scraping-crawling.md +25 -0
- package/skills/eng-automation/references/5-apis-rest-graphql-soap.md +26 -0
- package/skills/eng-automation/references/6-webhooks-integracao.md +25 -0
- package/skills/eng-automation/references/7-rpa.md +25 -0
- package/skills/eng-automation/references/8-extracao-estruturada.md +25 -0
- package/skills/eng-automation/references/9-extracao-documentos.md +25 -0
- package/skills/eng-automation/references/especializacoes.md +18 -0
- package/skills/eng-backend/SKILL.md +6 -0
- package/skills/eng-backend-arch-c4/SKILL.md +10 -4
- package/skills/eng-backend-microservices-trace/SKILL.md +6 -0
- package/skills/eng-backend-nestjs/SKILL.md +17 -462
- package/skills/eng-backend-nestjs/references/1-arquitetura-modulos.md +54 -0
- package/skills/eng-backend-nestjs/references/2-guards-autenticacao.md +118 -0
- package/skills/eng-backend-nestjs/references/3-interceptors-pipes-filters.md +113 -0
- package/skills/eng-backend-nestjs/references/4-config-logging.md +61 -0
- package/skills/eng-backend-nestjs/references/5-testes.md +83 -0
- package/skills/eng-backend-nestjs/references/6-problemas-comuns.md +53 -0
- package/skills/eng-backend-rabbitmq/SKILL.md +15 -298
- package/skills/eng-backend-rabbitmq/references/1-referencia-endpoints.md +28 -0
- package/skills/eng-backend-rabbitmq/references/2-codigo-consumer-producer.md +90 -0
- package/skills/eng-backend-rabbitmq/references/3-arquiteturas-comuns.md +101 -0
- package/skills/eng-backend-rabbitmq/references/4-troubleshooting.md +93 -0
- package/skills/eng-data/SKILL.md +271 -0
- package/skills/eng-data/references/1-fundamentos-dados.md +24 -0
- package/skills/eng-data/references/10-ciencia-dados.md +24 -0
- package/skills/eng-data/references/11-dataops-plataforma.md +24 -0
- package/skills/eng-data/references/12-seguranca-privacidade.md +24 -0
- package/skills/eng-data/references/2-modelagem-dados.md +24 -0
- package/skills/eng-data/references/3-bancos-dados.md +24 -0
- package/skills/eng-data/references/4-arquitetura-dados.md +24 -0
- package/skills/eng-data/references/5-pipelines-integracao.md +24 -0
- package/skills/eng-data/references/6-qualidade-dados.md +24 -0
- package/skills/eng-data/references/7-governanca-metadados.md +24 -0
- package/skills/eng-data/references/8-estatistica-analise.md +24 -0
- package/skills/eng-data/references/9-analytics-bi.md +24 -0
- package/skills/eng-data/references/especializacoes.md +20 -0
- package/skills/eng-frontend/SKILL.md +18 -5
- package/skills/eng-frontend/references/13-design-system.md +48 -0
- package/skills/eng-frontend/references/14-microfrontend.md +46 -0
- package/skills/eng-global-docs-write/SKILL.md +10 -5
- package/skills/eng-global-pr/SKILL.md +7 -1
- package/skills/eng-global-task-comment/SKILL.md +8 -2
- package/skills/eng-platform/SKILL.md +331 -0
- package/skills/eng-platform/references/1-fundamentos-sistemas.md +23 -0
- package/skills/eng-platform/references/10-resiliencia-arquitetura.md +24 -0
- package/skills/eng-platform/references/11-plataforma-devex.md +24 -0
- package/skills/eng-platform/references/12-finops-custos.md +24 -0
- package/skills/eng-platform/references/13-seguranca-fundamentos.md +26 -0
- package/skills/eng-platform/references/14-profiling-performance.md +23 -0
- package/skills/eng-platform/references/15-testes-carga-chaos.md +23 -0
- package/skills/eng-platform/references/16-cache-multicamada.md +22 -0
- package/skills/eng-platform/references/2-redes.md +23 -0
- package/skills/eng-platform/references/3-cloud.md +24 -0
- package/skills/eng-platform/references/4-iac-automacao.md +24 -0
- package/skills/eng-platform/references/5-containers-orquestracao.md +25 -0
- package/skills/eng-platform/references/6-cicd-entrega.md +24 -0
- package/skills/eng-platform/references/7-observabilidade.md +24 -0
- package/skills/eng-platform/references/8-confiabilidade-sre.md +24 -0
- package/skills/eng-platform/references/9-seguranca-infra.md +26 -0
- package/skills/eng-platform/references/especializacoes.md +34 -0
- package/skills/eng-qa/SKILL.md +283 -0
- package/skills/eng-qa/references/1-fundamentos-qualidade.md +25 -0
- package/skills/eng-qa/references/10-confiabilidade.md +25 -0
- package/skills/eng-qa/references/11-seguranca.md +24 -0
- package/skills/eng-qa/references/12-acessibilidade.md +24 -0
- package/skills/eng-qa/references/13-dados-testes.md +24 -0
- package/skills/eng-qa/references/14-gestao-testes-defeitos.md +25 -0
- package/skills/eng-qa/references/15-arquitetura-qa.md +25 -0
- package/skills/eng-qa/references/2-estrategia-planejamento.md +25 -0
- package/skills/eng-qa/references/3-design-testes.md +25 -0
- package/skills/eng-qa/references/4-manual-exploratorio.md +24 -0
- package/skills/eng-qa/references/5-automacao-testes.md +25 -0
- package/skills/eng-qa/references/6-api-integracao.md +25 -0
- package/skills/eng-qa/references/7-contrato.md +24 -0
- package/skills/eng-qa/references/8-interface-compat-mobile.md +24 -0
- package/skills/eng-qa/references/9-performance.md +24 -0
- package/skills/eng-qa/references/especializacoes.md +29 -0
- package/skills/eng-qa-planner/SKILL.md +264 -0
- package/skills/jarvis-context-detect/SKILL.md +7 -1
- package/skills/jarvis-context-detect/assets/context-profile-template.md +1 -1
- package/skills/jarvis-create-specialization/SKILL.md +14 -0
- package/skills/jarvis-docs-central/README.md +31 -28
- package/skills/jarvis-docs-central/SKILL.md +11 -5
- package/skills/jarvis-docs-index/SKILL.md +10 -2
- package/skills/jarvis-evolution-architect/SKILL.md +6 -0
- package/skills/jarvis-init/SKILL.md +22 -15
- package/skills/jarvis-init/assets/passo-adicionar-stacks.md +47 -18
- package/skills/jarvis-list-specializations/SKILL.md +36 -17
- package/skills/jarvis-report-issue/SKILL.md +7 -1
- package/skills/product-roadmap-report/SKILL.md +8 -0
- package/skills/product-specs/SKILL.md +9 -0
- package/skills/product-specs-update/SKILL.md +6 -0
- package/taxonomy.md +16 -28
- package/templates/AGENTS.md +2 -0
- package/templates/ENV-template.md +10 -2
- package/templates/engineering/qa/qa.sprint-plan-template.md +1 -1
- package/templates/engineering/tech-spec-delivery-template.md +179 -0
- package/workflows/AGENTS.md +14 -2
- package/workflows/README.md +8 -16
- package/workflows/all-tools.md +7 -1
- package/workflows/engineering/data/data.contract.md +8 -0
- package/workflows/engineering/data/data.new-pipeline.md +15 -4
- package/workflows/engineering/eng.breakdown-subtasks.md +9 -0
- package/workflows/engineering/eng.bug-audit.md +10 -13
- package/workflows/engineering/eng.build-tech-spec.md +17 -146
- package/workflows/engineering/eng.create-ard-from-code.md +8 -0
- package/workflows/engineering/eng.create-ard.md +8 -0
- package/workflows/engineering/eng.create-rfc.md +7 -0
- package/workflows/engineering/eng.debug.md +5 -21
- package/workflows/engineering/eng.docs.md +9 -0
- package/workflows/engineering/eng.light-arch.md +7 -0
- package/workflows/engineering/eng.plan.md +15 -2
- package/workflows/engineering/eng.pr.md +24 -30
- package/workflows/engineering/eng.pre-pr.md +33 -15
- package/workflows/engineering/eng.review.md +18 -11
- package/workflows/engineering/eng.rpa.robot.md +17 -7
- package/workflows/engineering/eng.start.md +33 -12
- package/workflows/engineering/eng.work.md +25 -17
- package/workflows/engineering/frontend/eng.frontend-component.md +7 -0
- package/workflows/engineering/frontend/eng.frontend-perf-audit.md +8 -0
- package/workflows/engineering/frontend/eng.frontend-review.md +8 -0
- package/workflows/engineering/qa/eng.qa-refinement-entry.md +7 -0
- package/workflows/engineering/qa/eng.qa-release-signoff.md +6 -0
- package/workflows/engineering/qa/eng.qa-sprint-planning.md +7 -1
- package/workflows/product/prod.roadmap.preview.md +9 -0
- package/workflows/product/prod.spec.ard.md +97 -0
- package/workflows/product/prod.spec.breakdown.md +10 -0
- package/workflows/product/prod.spec.clarify.md +8 -2
- package/workflows/product/prod.spec.discovery.md +11 -1
- package/workflows/product/prod.spec.epic.md +8 -0
- package/workflows/product/prod.spec.frd.md +7 -0
- package/workflows/product/prod.spec.issue.md +6 -0
- package/workflows/product/prod.spec.md +15 -1
- package/workflows/product/prod.spec.prd.md +7 -0
- package/workflows/taxonomy.md +58 -6
- package/workflows/warm-up.md +47 -60
- package/agents/engineering/eng.cybersecurity.agent.md +0 -488
- package/agents/engineering/qa/eng.qa.cypress-specialist.md +0 -109
- package/bin/lib/docs/fetch-file.sh +0 -41
- package/bin/lib/docs/publish-file.sh +0 -284
- package/rules/engineering/eng.downstream-flow-rules.md +0 -297
- package/rules/engineering/qa/eng.qa.exploratory-session-rules.md +0 -137
- package/rules/engineering/qa/eng.qa.quality-gate-scoring-rules.md +0 -181
- package/rules/engineering/qa/eng.qa.tech-spec-validation-criteria-rules.md +0 -120
- package/skills/eng-ai-engineer/SKILL.md +0 -297
- package/skills/eng-data-bi/SKILL.md +0 -200
- package/skills/eng-data-debug/SKILL.md +0 -308
- package/skills/eng-data-engineer/SKILL.md +0 -257
- package/skills/eng-data-onboard/SKILL.md +0 -311
- package/skills/eng-data-orchestrator/SKILL.md +0 -427
- package/skills/eng-devops-performance-engineer/SKILL.md +0 -313
- package/skills/eng-frontend-design-system/SKILL.md +0 -621
- package/skills/eng-frontend-microfrontend/SKILL.md +0 -604
- package/skills/eng-global-browser-extension-builder/SKILL.md +0 -386
- package/skills/eng-global-jira-comment/SKILL.md +0 -21
- package/skills/eng-qa-a11y-audit/SKILL.md +0 -270
- package/skills/eng-qa-bug-report/SKILL.md +0 -1089
- package/skills/eng-qa-bug-report/TASK_MANAGERS.md +0 -138
- package/skills/eng-qa-cypress-e2e/SKILL.md +0 -178
- package/skills/eng-qa-dev-guide/SKILL.md +0 -165
- package/skills/eng-qa-e2e/SKILL.md +0 -401
- package/skills/eng-qa-e2e-spec-writer/SKILL.md +0 -323
- package/skills/eng-qa-exploratory/SKILL.md +0 -189
- package/skills/eng-qa-gate/SKILL.md +0 -371
- package/skills/eng-qa-gate/assets/checklist-validacao.md +0 -291
- package/skills/eng-qa-graphql-contract/SKILL.md +0 -257
- package/skills/eng-qa-quality-report/SKILL.md +0 -413
- package/skills/eng-qa-test-plan/SKILL.md +0 -467
- package/skills/eng-qa-testsprite/SKILL.md +0 -326
- package/skills/eng-qa-testsprite/references/testsprite-mcp.md +0 -224
- package/skills/eng-qa-unit-test/SKILL.md +0 -472
- package/skills/eng-scraper/SKILL.md +0 -684
- package/skills/eng-scraper-robot-builder/SKILL.md +0 -371
- package/skills/eng-security-cybersecurity/SKILL.md +0 -646
- package/skills/eng-security-patch/SKILL.md +0 -382
- package/skills/eng-security-threat-model/SKILL.md +0 -165
- package/skills/eng-security-triage/SKILL.md +0 -270
- package/templates/engineering/qa/eng.qa.quality-gate-examples-template.md +0 -311
- package/templates/engineering/qa/eng.qa.quality-gate-report-template.md +0 -249
- package/templates/engineering/qa/qa.cypress-test-template.md +0 -172
- package/templates/engineering/qa/qa.exploratory-session-template.md +0 -148
- package/templates/engineering/qa/qa.quality-report-template.md +0 -130
- package/workflows/engineering/eng.security-audit.md +0 -312
- package/workflows/engineering/eng.security-incident.md +0 -275
- package/workflows/engineering/eng.security-pipeline.md +0 -210
- package/workflows/engineering/eng.security-review.md +0 -235
- package/workflows/engineering/qa/eng.qa-dev-quality-guide.md +0 -51
- package/workflows/engineering/qa/eng.qa-e2e-test-generation.md +0 -51
- package/workflows/engineering/qa/eng.qa-exploratory-session.md +0 -60
- package/workflows/engineering/qa/eng.qa-quality-gate-validation.md +0 -202
- package/workflows/engineering/qa/eng.qa-quality-report.md +0 -78
- /package/skills/{eng-qa-test-plan → eng-qa-planner}/assets/test-coverage-template.md +0 -0
- /package/skills/{eng-qa-test-plan → eng-qa-planner}/assets/test-patterns.md +0 -0
- /package/templates/{CDD aplicado a Prompts.md → cdd-aplicado-a-prompts.md} +0 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Leitor das declarações padronizadas de chamadas.
|
|
3
|
+
*
|
|
4
|
+
* Só lê a estrutura (sem interpretar prosa, sem IA):
|
|
5
|
+
* - workflows e skills: tabela `| Passo | Skill | Condição |` sob
|
|
6
|
+
* `### Skills invocados durante o workflow` (ou `... durante a execução do skill`);
|
|
7
|
+
* - agentes: `## Skills Disponíveis` (`### {skill}`) e `## Workflows e Agentes Relacionados`.
|
|
8
|
+
* @module flow-map/parse
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export const WORKFLOW_TITLE = "Skills invocados durante o workflow";
|
|
12
|
+
export const SKILL_TITLE = "Skills invocados durante a execução do skill";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {Object} Target
|
|
16
|
+
* @property {"internal" | "agent" | "cli" | "mcp" | "external" | "unresolved"} kind
|
|
17
|
+
* @property {string} name
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @typedef {Object} Row
|
|
22
|
+
* @property {string | null} passo
|
|
23
|
+
* @property {string} condicao
|
|
24
|
+
* @property {Target[]} targets
|
|
25
|
+
* @property {string | null} via - Texto de `(via ...)`, quando houver
|
|
26
|
+
* @property {"quebrada" | "a confirmar" | null} marker
|
|
27
|
+
* @property {boolean} selfContained - Linha "Nenhuma — … autocontido"
|
|
28
|
+
* @property {boolean} available - Skill que o agente pode usar (não é chamada fixa)
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/** Troca o conteúdo dos blocos de código por linhas em branco (títulos dentro deles não valem). */
|
|
32
|
+
function stripFences(text) {
|
|
33
|
+
let fence = false;
|
|
34
|
+
return text
|
|
35
|
+
.split("\n")
|
|
36
|
+
.map((line) => {
|
|
37
|
+
if (line.trimStart().startsWith("```")) {
|
|
38
|
+
fence = !fence;
|
|
39
|
+
return "";
|
|
40
|
+
}
|
|
41
|
+
return fence ? "" : line;
|
|
42
|
+
})
|
|
43
|
+
.join("\n");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Divide uma linha de tabela em células, ignorando `|` dentro de crases. */
|
|
47
|
+
export function splitCells(line) {
|
|
48
|
+
const cells = [];
|
|
49
|
+
let current = "";
|
|
50
|
+
let inCode = false;
|
|
51
|
+
for (const ch of line.trim()) {
|
|
52
|
+
if (ch === "`") inCode = !inCode;
|
|
53
|
+
if (ch === "|" && !inCode) {
|
|
54
|
+
cells.push(current.trim());
|
|
55
|
+
current = "";
|
|
56
|
+
} else {
|
|
57
|
+
current += ch;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
cells.push(current.trim());
|
|
61
|
+
if (cells[0] === "") cells.shift();
|
|
62
|
+
if (cells[cells.length - 1] === "") cells.pop();
|
|
63
|
+
return cells;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Classifica a célula `Skill` de uma linha da tabela.
|
|
68
|
+
* @param {string} cell
|
|
69
|
+
* @returns {{ targets: Target[], via: string | null, marker: Row["marker"], selfContained: boolean }}
|
|
70
|
+
*/
|
|
71
|
+
export function classifyCell(cell) {
|
|
72
|
+
const text = cell.trim();
|
|
73
|
+
if (/^nenhuma/i.test(text)) return { targets: [], via: null, marker: null, selfContained: true };
|
|
74
|
+
|
|
75
|
+
const marker = /refer[eê]ncia quebrada/i.test(text)
|
|
76
|
+
? "quebrada"
|
|
77
|
+
: /refer[eê]ncia a confirmar/i.test(text)
|
|
78
|
+
? "a confirmar"
|
|
79
|
+
: null;
|
|
80
|
+
|
|
81
|
+
let via = null;
|
|
82
|
+
let analyzed = text;
|
|
83
|
+
const viaMatch = text.match(/\(via\s+([^)]*)\)/);
|
|
84
|
+
if (viaMatch) {
|
|
85
|
+
via = viaMatch[1].replace(/`/g, "").trim();
|
|
86
|
+
analyzed = text.replace(viaMatch[0], "");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const spans = [...analyzed.matchAll(/`([^`]+)`(\s*\(agente\))?/g)];
|
|
90
|
+
const targets = [];
|
|
91
|
+
const external = /skill externo/i.test(analyzed); // skill de fora do framework: não está no índice
|
|
92
|
+
|
|
93
|
+
if (/\(MCP/.test(analyzed) && spans[0]) {
|
|
94
|
+
targets.push({ kind: "mcp", name: spans[0][1].trim() });
|
|
95
|
+
return { targets, via, marker, selfContained: false };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
for (const span of spans) {
|
|
99
|
+
const token = span[1].trim();
|
|
100
|
+
if (span[2]) targets.push({ kind: "agent", name: token });
|
|
101
|
+
else if (token.startsWith("/")) {
|
|
102
|
+
targets.push({
|
|
103
|
+
kind: external ? "external" : "internal",
|
|
104
|
+
name: token.slice(1).split(/\s+/)[0],
|
|
105
|
+
});
|
|
106
|
+
} else if (/^(jarvis|node)\s/.test(token)) targets.push({ kind: "cli", name: token });
|
|
107
|
+
else if (token.startsWith("mcp__")) targets.push({ kind: "mcp", name: token });
|
|
108
|
+
// Read, Grep, Glob e texto livre não são chamadas a artefatos: ignorados
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (marker && targets.length === 0 && spans[0]) {
|
|
112
|
+
targets.push({ kind: "unresolved", name: spans[0][1].trim() });
|
|
113
|
+
}
|
|
114
|
+
return { targets, via, marker, selfContained: false };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function parseTable(text, title) {
|
|
118
|
+
const lines = stripFences(text).split("\n");
|
|
119
|
+
const start = lines.findIndex((l) => l.trim() === `### ${title}`);
|
|
120
|
+
if (start === -1) return null;
|
|
121
|
+
const rows = [];
|
|
122
|
+
let i = start + 1;
|
|
123
|
+
while (i < lines.length && !lines[i].startsWith("|")) {
|
|
124
|
+
if (/^#{1,3} /.test(lines[i])) return rows;
|
|
125
|
+
i++;
|
|
126
|
+
}
|
|
127
|
+
i += 2; // cabeçalho e separador
|
|
128
|
+
for (; i < lines.length && lines[i].startsWith("|"); i++) {
|
|
129
|
+
const [passo = "", skill = "", condicao = ""] = splitCells(lines[i]);
|
|
130
|
+
const c = classifyCell(skill);
|
|
131
|
+
rows.push({
|
|
132
|
+
passo,
|
|
133
|
+
condicao,
|
|
134
|
+
targets: c.targets,
|
|
135
|
+
via: c.via,
|
|
136
|
+
marker: c.marker,
|
|
137
|
+
selfContained: c.selfContained,
|
|
138
|
+
available: false,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
return rows;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function section(text, title) {
|
|
145
|
+
const m = text.match(
|
|
146
|
+
new RegExp(`^## ${title}\\s*\\n([\\s\\S]*?)(?=\\n## |\\n---\\s*\\n|(?![\\s\\S]))`, "m")
|
|
147
|
+
);
|
|
148
|
+
return m ? m[1] : null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function parseAgent(text) {
|
|
152
|
+
const clean = stripFences(text);
|
|
153
|
+
const rows = [];
|
|
154
|
+
|
|
155
|
+
const skills = section(clean, "Skills Disponíveis");
|
|
156
|
+
if (skills !== null) {
|
|
157
|
+
for (const block of skills.split(/^### /m).slice(1)) {
|
|
158
|
+
const [heading, ...rest] = block.split("\n");
|
|
159
|
+
// Um bloco pode citar mais de uma skill: uma linha por skill (nome tirado do caminho do SKILL.md).
|
|
160
|
+
const names = [
|
|
161
|
+
...new Set([...block.matchAll(/skills\/([A-Za-z0-9._-]+)\/SKILL\.md/g)].map((m) => m[1])),
|
|
162
|
+
];
|
|
163
|
+
if (names.length === 0) {
|
|
164
|
+
const token = heading.trim().split(/[\s(]/)[0];
|
|
165
|
+
if (/[-.]/.test(token)) names.push(token);
|
|
166
|
+
}
|
|
167
|
+
const context = rest.map((l) => l.trim()).find((l) => l && !l.startsWith("- ")) ?? "";
|
|
168
|
+
for (const name of names) {
|
|
169
|
+
rows.push({
|
|
170
|
+
passo: null,
|
|
171
|
+
condicao: context.replace(/:$/, ""),
|
|
172
|
+
targets: [{ kind: "internal", name }],
|
|
173
|
+
via: null,
|
|
174
|
+
marker: null,
|
|
175
|
+
selfContained: false,
|
|
176
|
+
available: true,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const related = section(clean, "Workflows e Agentes Relacionados");
|
|
183
|
+
if (related !== null) {
|
|
184
|
+
for (const line of related.split("\n")) {
|
|
185
|
+
if (!/^- /.test(line) || /^- Arquivo:/.test(line)) continue;
|
|
186
|
+
const bold = line.match(/\*\*([^*]+)\*\*/);
|
|
187
|
+
const code = line.match(/`([^`]+)`/);
|
|
188
|
+
let name = bold ? bold[1] : code ? code[1] : null;
|
|
189
|
+
if (!name) continue;
|
|
190
|
+
if (name.includes("/")) {
|
|
191
|
+
if (!name.endsWith(".md")) continue;
|
|
192
|
+
name = name.split("/").pop().replace(/\.md$/, "");
|
|
193
|
+
}
|
|
194
|
+
const condicao = line.includes(":") ? line.slice(line.indexOf(":") + 1).trim() : "";
|
|
195
|
+
rows.push({
|
|
196
|
+
passo: null,
|
|
197
|
+
condicao,
|
|
198
|
+
targets: [{ kind: "internal", name }],
|
|
199
|
+
via: null,
|
|
200
|
+
marker: null,
|
|
201
|
+
selfContained: false,
|
|
202
|
+
available: false,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const declared = skills !== null || related !== null;
|
|
208
|
+
return { declared, selfContained: declared && rows.length === 0, rows };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Lê as chamadas declaradas por um artefato.
|
|
213
|
+
* @param {"workflow" | "skill" | "agent"} type
|
|
214
|
+
* @param {string} text
|
|
215
|
+
* @returns {{ declared: boolean, selfContained: boolean, rows: Row[] }}
|
|
216
|
+
*/
|
|
217
|
+
export function parseDeclaration(type, text) {
|
|
218
|
+
if (type === "agent") return parseAgent(text);
|
|
219
|
+
const rows = parseTable(text, type === "workflow" ? WORKFLOW_TITLE : SKILL_TITLE);
|
|
220
|
+
if (rows === null) return { declared: false, selfContained: false, rows: [] };
|
|
221
|
+
const hasTargets = rows.some((r) => r.targets.length > 0);
|
|
222
|
+
return { declared: true, selfContained: rows.some((r) => r.selfContained) && !hasTargets, rows };
|
|
223
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Saída em texto da árvore de chamadas.
|
|
3
|
+
* @module flow-map/render
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const TYPE_LABEL = {
|
|
7
|
+
workflow: "workflow",
|
|
8
|
+
skill: "skill",
|
|
9
|
+
agent: "agente",
|
|
10
|
+
cli: "CLI",
|
|
11
|
+
mcp: "MCP",
|
|
12
|
+
external: "externo",
|
|
13
|
+
broken: "?",
|
|
14
|
+
unresolved: "?",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Resume a coluna `Condição` para uma linha: sem crases, "Se" em minúscula e limite de tamanho.
|
|
19
|
+
* @param {string} text
|
|
20
|
+
* @param {number} [max]
|
|
21
|
+
*/
|
|
22
|
+
export function summarizeCondition(text, max = 90) {
|
|
23
|
+
let t = text.replace(/[`*]/g, "").replace(/\s+/g, " ").trim();
|
|
24
|
+
if (!t || t === "—") return "";
|
|
25
|
+
t = t.charAt(0).toLowerCase() + t.slice(1);
|
|
26
|
+
return t.length > max ? `${t.slice(0, max - 1).trimEnd()}…` : t;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function describe(node) {
|
|
30
|
+
const head = node.kind === "menu" ? node.name : `${node.name} [${TYPE_LABEL[node.kind]}]`;
|
|
31
|
+
const notes = [];
|
|
32
|
+
const condition = summarizeCondition(node.condicao);
|
|
33
|
+
if (node.available) notes.push("disponível");
|
|
34
|
+
if (condition) notes.push(condition);
|
|
35
|
+
if (node.via) notes.push(`via ${node.via}`);
|
|
36
|
+
if (node.kind === "broken" || node.marker === "quebrada") notes.push("referência quebrada");
|
|
37
|
+
else if (node.marker === "a confirmar") notes.push("referência a confirmar");
|
|
38
|
+
if (node.status === "cycle") notes.push("ciclo: já mapeado acima");
|
|
39
|
+
else if (node.status === "seen") notes.push("já mapeado acima");
|
|
40
|
+
else if (node.status === "truncated") notes.push("truncado: use --depth N");
|
|
41
|
+
return notes.length ? `${head} (${notes.join("; ")})` : head;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param {import("./tree.js").TreeNode} root
|
|
46
|
+
* @returns {string[]} linhas da árvore
|
|
47
|
+
*/
|
|
48
|
+
export function renderTree(root) {
|
|
49
|
+
const lines = [describe(root)];
|
|
50
|
+
if (root.status === "selfContained") lines.push("└── (autocontido: não chama nenhum outro item)");
|
|
51
|
+
else if (root.status === "undeclared")
|
|
52
|
+
lines.push("└── (sem declaração de chamadas: o item não segue a tabela padronizada)");
|
|
53
|
+
|
|
54
|
+
function walk(node, prefix) {
|
|
55
|
+
node.children.forEach((child, i) => {
|
|
56
|
+
const last = i === node.children.length - 1;
|
|
57
|
+
lines.push(`${prefix}${last ? "└── " : "├── "}${describe(child)}`);
|
|
58
|
+
walk(child, `${prefix}${last ? " " : "│ "}`);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
walk(root, "");
|
|
62
|
+
return lines;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @param {string} name
|
|
67
|
+
* @param {string} type
|
|
68
|
+
* @param {ReturnType<typeof import("./tree.js").reverseEdges>} edges
|
|
69
|
+
* @returns {string[]}
|
|
70
|
+
*/
|
|
71
|
+
export function renderReverse(name, type, edges) {
|
|
72
|
+
const lines = [`Quem chama ${name} [${TYPE_LABEL[type]}]`];
|
|
73
|
+
if (edges.length === 0) {
|
|
74
|
+
lines.push(`└── nenhum artefato chama ${name} — pode ser um ponto de entrada`);
|
|
75
|
+
return lines;
|
|
76
|
+
}
|
|
77
|
+
edges.forEach((e, i) => {
|
|
78
|
+
const notes = [];
|
|
79
|
+
if (e.passo) notes.push(`passo: ${e.passo}`);
|
|
80
|
+
if (e.available) notes.push("disponível");
|
|
81
|
+
const condition = summarizeCondition(e.condicao);
|
|
82
|
+
if (condition) notes.push(condition);
|
|
83
|
+
if (e.via) notes.push(`via ${e.via}`);
|
|
84
|
+
lines.push(
|
|
85
|
+
`${i === edges.length - 1 ? "└── " : "├── "}${e.from} [${TYPE_LABEL[e.type]}]${notes.length ? ` (${notes.join("; ")})` : ""}`
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
return lines;
|
|
89
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Grafo de chamadas e árvore para frente.
|
|
3
|
+
* @module flow-map/tree
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFileSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { buildIndex } from "./index.js";
|
|
9
|
+
import { parseDeclaration } from "./parse.js";
|
|
10
|
+
|
|
11
|
+
export const DEFAULT_DEPTH = 4;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Lê o índice e as declarações de todos os artefatos.
|
|
15
|
+
* @param {string} root - Raiz do framework
|
|
16
|
+
*/
|
|
17
|
+
export function buildGraph(root) {
|
|
18
|
+
const { items, duplicates } = buildIndex(root);
|
|
19
|
+
const decl = new Map();
|
|
20
|
+
for (const item of items.values()) {
|
|
21
|
+
decl.set(item.name, parseDeclaration(item.type, readFileSync(join(root, item.path), "utf-8")));
|
|
22
|
+
}
|
|
23
|
+
return { items, duplicates, decl };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {Object} TreeNode
|
|
28
|
+
* @property {string} name
|
|
29
|
+
* @property {"workflow"|"skill"|"agent"|"cli"|"mcp"|"external"|"broken"|"unresolved"|"menu"} kind
|
|
30
|
+
* @property {string | null} passo
|
|
31
|
+
* @property {string} condicao
|
|
32
|
+
* @property {string | null} via
|
|
33
|
+
* @property {"quebrada"|"a confirmar"|null} marker
|
|
34
|
+
* @property {boolean} available
|
|
35
|
+
* @property {null|"cycle"|"seen"|"truncated"|"undeclared"|"selfContained"} status
|
|
36
|
+
* @property {TreeNode[]} children
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
function hasCalls(graph, name) {
|
|
40
|
+
const d = graph.decl.get(name);
|
|
41
|
+
return Boolean(d) && d.rows.some((r) => r.targets.length > 0);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Árvore do que o item chama, até `depth` níveis.
|
|
46
|
+
* @param {ReturnType<typeof buildGraph>} graph
|
|
47
|
+
* @param {string} name - Nome de um item do índice
|
|
48
|
+
* @param {{ depth?: number }} [opts]
|
|
49
|
+
* @returns {TreeNode}
|
|
50
|
+
*/
|
|
51
|
+
export function forwardTree(graph, name, opts = {}) {
|
|
52
|
+
const depth = opts.depth ?? DEFAULT_DEPTH;
|
|
53
|
+
const expanded = new Set([name]);
|
|
54
|
+
|
|
55
|
+
function base(partial) {
|
|
56
|
+
return {
|
|
57
|
+
passo: null,
|
|
58
|
+
condicao: "",
|
|
59
|
+
via: null,
|
|
60
|
+
marker: null,
|
|
61
|
+
available: false,
|
|
62
|
+
status: null,
|
|
63
|
+
children: [],
|
|
64
|
+
...partial,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function childrenOf(itemName, level, ancestors) {
|
|
69
|
+
const d = graph.decl.get(itemName);
|
|
70
|
+
const children = [];
|
|
71
|
+
for (const row of d.rows) {
|
|
72
|
+
if (row.targets.length === 0) continue;
|
|
73
|
+
const grouped =
|
|
74
|
+
row.targets.filter((t) => t.kind === "internal" || t.kind === "agent").length > 1;
|
|
75
|
+
// Em menu, a condição fica no nó do menu e não se repete em cada alvo.
|
|
76
|
+
const nodes = row.targets.map((t) =>
|
|
77
|
+
targetNode(t, grouped ? { ...row, condicao: "", via: null } : row, level + 1, ancestors)
|
|
78
|
+
);
|
|
79
|
+
if (grouped) {
|
|
80
|
+
children.push(
|
|
81
|
+
base({
|
|
82
|
+
name: `menu (${nodes.length} opções)`,
|
|
83
|
+
kind: "menu",
|
|
84
|
+
passo: row.passo,
|
|
85
|
+
condicao: row.condicao,
|
|
86
|
+
via: row.via,
|
|
87
|
+
children: nodes,
|
|
88
|
+
})
|
|
89
|
+
);
|
|
90
|
+
} else {
|
|
91
|
+
children.push(...nodes);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return children;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function targetNode(target, row, level, ancestors) {
|
|
98
|
+
const common = {
|
|
99
|
+
passo: row.passo,
|
|
100
|
+
condicao: row.condicao,
|
|
101
|
+
via: row.via,
|
|
102
|
+
marker: row.marker,
|
|
103
|
+
available: row.available,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
if (["cli", "mcp", "external", "unresolved"].includes(target.kind)) {
|
|
107
|
+
return base({ name: target.name, kind: target.kind, ...common });
|
|
108
|
+
}
|
|
109
|
+
const item = graph.items.get(target.name);
|
|
110
|
+
if (!item)
|
|
111
|
+
return base({
|
|
112
|
+
name: target.name,
|
|
113
|
+
kind: "broken",
|
|
114
|
+
...common,
|
|
115
|
+
marker: row.marker ?? "quebrada",
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const node = base({ name: item.name, kind: item.type, ...common });
|
|
119
|
+
if (ancestors.has(item.name)) node.status = "cycle";
|
|
120
|
+
else if (expanded.has(item.name)) node.status = "seen";
|
|
121
|
+
else if (level >= depth) node.status = hasCalls(graph, item.name) ? "truncated" : null;
|
|
122
|
+
else {
|
|
123
|
+
expanded.add(item.name);
|
|
124
|
+
fill(node, level, new Set([...ancestors, item.name]));
|
|
125
|
+
}
|
|
126
|
+
return node;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function fill(node, level, ancestors) {
|
|
130
|
+
const d = graph.decl.get(node.name);
|
|
131
|
+
if (!d.declared) node.status = "undeclared";
|
|
132
|
+
else if (d.selfContained) node.status = "selfContained";
|
|
133
|
+
else node.children = childrenOf(node.name, level, ancestors);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const item = graph.items.get(name);
|
|
137
|
+
const root = base({ name, kind: item.type });
|
|
138
|
+
fill(root, 0, new Set([name]));
|
|
139
|
+
return root;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function levenshtein(a, b) {
|
|
143
|
+
const row = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
144
|
+
for (let i = 1; i <= a.length; i++) {
|
|
145
|
+
let prev = row[0];
|
|
146
|
+
row[0] = i;
|
|
147
|
+
for (let j = 1; j <= b.length; j++) {
|
|
148
|
+
const tmp = row[j];
|
|
149
|
+
row[j] = Math.min(row[j] + 1, row[j - 1] + 1, prev + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
150
|
+
prev = tmp;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return row[b.length];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Até 5 nomes parecidos (prefixo, trecho ou distância de edição de até 1 a cada 4 caracteres).
|
|
158
|
+
* @param {Map<string, unknown>} items
|
|
159
|
+
* @param {string} name
|
|
160
|
+
*/
|
|
161
|
+
export function suggest(items, name) {
|
|
162
|
+
const lower = name.toLowerCase();
|
|
163
|
+
return [...items.keys()]
|
|
164
|
+
.map((candidate) => {
|
|
165
|
+
const c = candidate.toLowerCase();
|
|
166
|
+
const score =
|
|
167
|
+
c.startsWith(lower) || lower.startsWith(c)
|
|
168
|
+
? 0
|
|
169
|
+
: c.includes(lower) || lower.includes(c)
|
|
170
|
+
? 1
|
|
171
|
+
: levenshtein(lower, c) <= Math.max(1, Math.floor(Math.min(lower.length, c.length) / 4))
|
|
172
|
+
? 2
|
|
173
|
+
: null;
|
|
174
|
+
return { candidate, score };
|
|
175
|
+
})
|
|
176
|
+
.filter((s) => s.score !== null)
|
|
177
|
+
.sort((a, b) => a.score - b.score || a.candidate.localeCompare(b.candidate))
|
|
178
|
+
.slice(0, 5)
|
|
179
|
+
.map((s) => s.candidate);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Quem declara chamar o item (arestas invertidas do mesmo grafo).
|
|
184
|
+
* @param {ReturnType<typeof buildGraph>} graph
|
|
185
|
+
* @param {string} name
|
|
186
|
+
* @returns {Array<{ from: string, type: string, passo: string | null, condicao: string, via: string | null, available: boolean }>}
|
|
187
|
+
*/
|
|
188
|
+
export function reverseEdges(graph, name) {
|
|
189
|
+
const edges = [];
|
|
190
|
+
for (const [from, d] of graph.decl) {
|
|
191
|
+
if (from === name) continue;
|
|
192
|
+
for (const row of d.rows) {
|
|
193
|
+
if (
|
|
194
|
+
row.targets.some((t) => (t.kind === "internal" || t.kind === "agent") && t.name === name)
|
|
195
|
+
) {
|
|
196
|
+
edges.push({
|
|
197
|
+
from,
|
|
198
|
+
type: graph.items.get(from).type,
|
|
199
|
+
passo: row.passo,
|
|
200
|
+
condicao: row.condicao,
|
|
201
|
+
via: row.via,
|
|
202
|
+
available: row.available,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return edges.sort((a, b) => a.from.localeCompare(b.from));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Confere os nomes citados nas declarações.
|
|
212
|
+
* - erro: `/nome` ou `nome (agente)` que não existe, e nomes duplicados no índice;
|
|
213
|
+
* - aviso: linha já marcada "referência quebrada" ou "a confirmar", e artefato sem declaração.
|
|
214
|
+
* @param {ReturnType<typeof buildGraph>} graph
|
|
215
|
+
* @returns {{ errors: Array<{ file: string, name: string, reason: string }>, warnings: Array<{ file: string, name: string, reason: string }> }}
|
|
216
|
+
*/
|
|
217
|
+
export function findBrokenReferences(graph) {
|
|
218
|
+
const errors = duplicatesOf(graph);
|
|
219
|
+
const warnings = [];
|
|
220
|
+
for (const [name, d] of graph.decl) {
|
|
221
|
+
const file = graph.items.get(name).path;
|
|
222
|
+
if (!d.declared) warnings.push({ file, name, reason: "sem declaração de chamadas" });
|
|
223
|
+
for (const row of d.rows) {
|
|
224
|
+
for (const t of row.targets) {
|
|
225
|
+
if (t.kind === "unresolved") {
|
|
226
|
+
warnings.push({ file, name: t.name, reason: `referência ${row.marker}` });
|
|
227
|
+
} else if ((t.kind === "internal" || t.kind === "agent") && !graph.items.has(t.name)) {
|
|
228
|
+
if (row.marker) warnings.push({ file, name: t.name, reason: `referência ${row.marker}` });
|
|
229
|
+
else
|
|
230
|
+
errors.push({
|
|
231
|
+
file,
|
|
232
|
+
name: t.name,
|
|
233
|
+
reason: "não existe como workflow, skill ou agente",
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return { errors, warnings };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function duplicatesOf(graph) {
|
|
243
|
+
return graph.duplicates.map((name) => ({
|
|
244
|
+
file: name,
|
|
245
|
+
name,
|
|
246
|
+
reason: "nome repetido entre os artefatos",
|
|
247
|
+
}));
|
|
248
|
+
}
|
package/bin/lib/tasks/comment.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
1
2
|
import { cardUrl } from "../vcs/api.js";
|
|
2
3
|
|
|
3
4
|
function env(name) {
|
|
@@ -13,9 +14,7 @@ async function commentJira(cardKey, message) {
|
|
|
13
14
|
const token = env("TOKEN_TASK_MANAGER");
|
|
14
15
|
const email = env("USER");
|
|
15
16
|
if (!base || !token) {
|
|
16
|
-
throw new Error(
|
|
17
|
-
"Jira: preencha TASK_MANAGER_URL_BASE e TOKEN_TASK_MANAGER no ENV.md",
|
|
18
|
-
);
|
|
17
|
+
throw new Error("Jira: preencha TASK_MANAGER_URL_BASE e TOKEN_TASK_MANAGER no ENV.md");
|
|
19
18
|
}
|
|
20
19
|
const auth = email
|
|
21
20
|
? `Basic ${Buffer.from(`${email}:${token}`).toString("base64")}`
|
|
@@ -68,28 +67,24 @@ async function commentLinear(cardKey, message) {
|
|
|
68
67
|
}
|
|
69
68
|
|
|
70
69
|
async function commentGithub(cardKey, message) {
|
|
71
|
-
const token =
|
|
72
|
-
env("TOKEN_TASK_MANAGER") || env("GITHUB_TOKEN") || env("GH_TOKEN");
|
|
70
|
+
const token = env("TOKEN_TASK_MANAGER") || env("GITHUB_TOKEN") || env("GH_TOKEN");
|
|
73
71
|
const base = env("TASK_MANAGER_URL_BASE").replace(/\/$/, "");
|
|
74
72
|
if (!token || !base) {
|
|
75
73
|
throw new Error(
|
|
76
|
-
"GitHub Issues: TASK_MANAGER_URL_BASE (https://github.com/org/repo) e TOKEN_TASK_MANAGER/GITHUB_TOKEN"
|
|
74
|
+
"GitHub Issues: TASK_MANAGER_URL_BASE (https://github.com/org/repo) e TOKEN_TASK_MANAGER/GITHUB_TOKEN"
|
|
77
75
|
);
|
|
78
76
|
}
|
|
79
77
|
const path = new URL(base).pathname.replace(/^\/+|\/+$/g, "");
|
|
80
78
|
const number = String(cardKey).replace(/^#/, "");
|
|
81
|
-
const res = await fetch(
|
|
82
|
-
|
|
83
|
-
{
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
Accept: "application/vnd.github+json",
|
|
88
|
-
"Content-Type": "application/json",
|
|
89
|
-
},
|
|
90
|
-
body: JSON.stringify({ body: message }),
|
|
79
|
+
const res = await fetch(`https://api.github.com/repos/${path}/issues/${number}/comments`, {
|
|
80
|
+
method: "POST",
|
|
81
|
+
headers: {
|
|
82
|
+
Authorization: `Bearer ${token}`,
|
|
83
|
+
Accept: "application/vnd.github+json",
|
|
84
|
+
"Content-Type": "application/json",
|
|
91
85
|
},
|
|
92
|
-
|
|
86
|
+
body: JSON.stringify({ body: message }),
|
|
87
|
+
});
|
|
93
88
|
if (!res.ok) throw new Error(`GitHub ${res.status}: ${await res.text()}`);
|
|
94
89
|
}
|
|
95
90
|
|
|
@@ -107,25 +102,35 @@ async function commentAsana(cardKey, message) {
|
|
|
107
102
|
if (!res.ok) throw new Error(`Asana ${res.status}: ${await res.text()}`);
|
|
108
103
|
}
|
|
109
104
|
|
|
110
|
-
|
|
111
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Comenta num card do task manager configurado em TASK_MANAGER (jira/linear/github/asana).
|
|
107
|
+
* Usada por `jarvis tasks comment` e pelo skill `eng-global-task-comment`.
|
|
108
|
+
* @param {string} cardKey - Chave/ID do card (ex: `TASK-123`, ou número de issue no GitHub)
|
|
109
|
+
* @param {string} message - Texto do comentário
|
|
110
|
+
*/
|
|
111
|
+
export async function runComment(cardKey, message) {
|
|
112
|
+
if (!cardKey || !message) {
|
|
113
|
+
console.error("Uso: jarvis tasks comment <TASK_MANAGER_KEY> <mensagem>");
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const tm = taskManager();
|
|
118
|
+
try {
|
|
119
|
+
if (tm === "linear") await commentLinear(cardKey, message);
|
|
120
|
+
else if (tm === "github") await commentGithub(cardKey, message);
|
|
121
|
+
else if (tm === "asana") await commentAsana(cardKey, message);
|
|
122
|
+
else await commentJira(cardKey, message);
|
|
112
123
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
124
|
+
const url = cardUrl(tm, env("TASK_MANAGER_URL_BASE"), cardKey);
|
|
125
|
+
console.log(`💬 Comentário adicionado em ${cardKey} (${tm})`);
|
|
126
|
+
if (url !== cardKey) console.log(` ${url}`);
|
|
127
|
+
} catch (err) {
|
|
128
|
+
console.error(`⚠️ Não foi possível comentar no card ${cardKey}: ${err.message}`);
|
|
129
|
+
process.exit(2);
|
|
130
|
+
}
|
|
116
131
|
}
|
|
117
132
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
else if (tm === "github") await commentGithub(cardKey, message);
|
|
122
|
-
else if (tm === "asana") await commentAsana(cardKey, message);
|
|
123
|
-
else await commentJira(cardKey, message);
|
|
124
|
-
|
|
125
|
-
const url = cardUrl(tm, env("TASK_MANAGER_URL_BASE"), cardKey);
|
|
126
|
-
console.log(`💬 Comentário adicionado em ${cardKey} (${tm})`);
|
|
127
|
-
if (url !== cardKey) console.log(` ${url}`);
|
|
128
|
-
} catch (err) {
|
|
129
|
-
console.error(`⚠️ Não foi possível comentar no card ${cardKey}: ${err.message}`);
|
|
130
|
-
process.exit(2);
|
|
133
|
+
// Compatibilidade: continua funcionando como `node bin/lib/tasks/comment.js <key> <mensagem>`
|
|
134
|
+
if (fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
135
|
+
await runComment(process.argv[2], process.argv.slice(3).join(" "));
|
|
131
136
|
}
|