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.
Files changed (303) hide show
  1. package/AGENTS.md +22 -122
  2. package/agents/AGENTS.md +20 -24
  3. package/agents/README.md +25 -129
  4. package/agents/engineering/data/eng.data-engineer.agent.md +12 -35
  5. package/agents/engineering/eng.agent.md +18 -30
  6. package/agents/engineering/eng.bug-hunter.md +9 -12
  7. package/agents/engineering/eng.dev-code-reviewer.md +5 -1
  8. package/agents/engineering/eng.docs-writer.md +3 -3
  9. package/agents/engineering/eng.frontend.agent.md +13 -8
  10. package/agents/engineering/eng.rpa.agent.md +5 -13
  11. package/agents/engineering/eng.ux-designer.agent.md +4 -0
  12. package/agents/engineering/qa/eng.qa.quality-champion-task-agent.md +4 -0
  13. package/agents/engineering/qa/eng.qa.quality-strategist.md +13 -8
  14. package/agents/engineering/qa/eng.qa.test-architect.md +16 -15
  15. package/agents/engineering/qa/eng.qa.test-planner.md +22 -17
  16. package/agents/engineering/qa/eng.qa.testing-engineer.md +99 -15
  17. package/agents/product/prod.discovery-interviewer.md +22 -3
  18. package/agents/product/prod.pm-checker.md +4 -0
  19. package/bin/README.md +142 -0
  20. package/bin/commands/docs-publish.js +105 -141
  21. package/bin/commands/docs-sync.js +66 -104
  22. package/bin/commands/info.js +7 -27
  23. package/bin/commands/init.js +23 -51
  24. package/bin/commands/install-rtk.js +9 -3
  25. package/bin/commands/list.js +5 -18
  26. package/bin/commands/map.js +58 -0
  27. package/bin/commands/qa-signoff.js +58 -55
  28. package/bin/commands/tokens.js +90 -0
  29. package/bin/commands/whoami.js +30 -26
  30. package/bin/jarvis.js +9 -153
  31. package/bin/lib/auth/session.js +20 -20
  32. package/bin/lib/cli/program.js +219 -0
  33. package/bin/lib/cli/shared-options.js +29 -0
  34. package/bin/lib/config/constants.js +70 -2
  35. package/bin/lib/config/ide-config.js +4 -8
  36. package/bin/lib/core/profile-filter.js +116 -0
  37. package/bin/lib/core/scanner.js +5 -31
  38. package/bin/lib/core/sync-engine.js +21 -14
  39. package/bin/lib/core/token-report.js +100 -0
  40. package/bin/lib/docs/fetch-file.js +39 -0
  41. package/bin/lib/docs/publish-file.js +194 -0
  42. package/bin/lib/docs/redis-cache.js +49 -0
  43. package/bin/lib/docs/validate-frontmatter.js +9 -39
  44. package/bin/lib/env-loader.js +11 -34
  45. package/bin/lib/flow-map/index.js +65 -0
  46. package/bin/lib/flow-map/parse.js +223 -0
  47. package/bin/lib/flow-map/render.js +89 -0
  48. package/bin/lib/flow-map/tree.js +248 -0
  49. package/bin/lib/tasks/comment.js +40 -35
  50. package/bin/lib/utils/frontmatter.js +57 -0
  51. package/bin/lib/utils/git-parser.js +4 -6
  52. package/bin/lib/utils/npmrc-parser.js +6 -13
  53. package/bin/lib/utils/paths.js +2 -4
  54. package/bin/lib/utils/ui.js +3 -9
  55. package/bin/lib/vcs/api.js +25 -41
  56. package/bin/lib/vcs/create-issue.js +49 -33
  57. package/bin/lib/vcs/create-merge.js +47 -31
  58. package/bin/lib/vcs/fetch-raw.js +35 -23
  59. package/members.md +1 -1
  60. package/package.json +15 -4
  61. package/rules/AGENTS.md +18 -7
  62. package/rules/engineering/eng-rules.md +3 -13
  63. package/rules/engineering/eng-security-rules.md +19 -18
  64. package/rules/engineering/eng.bump-rules.md +3 -2
  65. package/rules/engineering/eng.docs-scraping-rules.md +4 -4
  66. package/rules/engineering/eng.integrations-rules.md +3 -7
  67. package/rules/engineering/eng.skills-rules.md +1 -5
  68. package/rules/engineering/eng.specializations-rules.md +18 -8
  69. package/rules/engineering/frontend/eng.frontend-rules.md +1 -4
  70. package/rules/engineering/qa/eng.qa.cypress-standards-rules.md +1 -1
  71. package/rules/engineering/rpa/eng.rpa-rules.md +4 -9
  72. package/rules/product/README.md +1 -0
  73. package/rules/product/prod-rules.md +1 -4
  74. package/rules/product/prod.spec-rules.md +0 -4
  75. package/rules/rtk-rules.md +0 -1
  76. package/{rules → rules-on-demand}/engineering/eng.breakdown-subtasks-rules.md +0 -4
  77. package/{rules → rules-on-demand}/engineering/eng.plan-rules.md +0 -4
  78. package/{rules → rules-on-demand}/engineering/eng.pr-rules.md +0 -4
  79. package/{rules → rules-on-demand}/engineering/eng.pre-pr-rules.md +0 -4
  80. package/{rules → rules-on-demand}/engineering/eng.start-rules.md +0 -4
  81. package/{rules → rules-on-demand}/engineering/eng.tech-spec-rules.md +1 -2
  82. package/{rules → rules-on-demand}/engineering/eng.work-rules.md +0 -4
  83. package/skills/AGENTS.md +69 -106
  84. package/skills/SKILLS-ROADMAP.md +47 -132
  85. package/skills/eng-ai/SKILL.md +270 -0
  86. package/skills/eng-ai/references/1-fundamentos-ml.md +24 -0
  87. package/skills/eng-ai/references/10-arquitetura-solucoes-ia.md +24 -0
  88. package/skills/eng-ai/references/11-governanca-ia.md +24 -0
  89. package/skills/eng-ai/references/12-seguranca-ia-responsavel.md +25 -0
  90. package/skills/eng-ai/references/2-matematica-estatistica.md +24 -0
  91. package/skills/eng-ai/references/3-dados-para-ia.md +24 -0
  92. package/skills/eng-ai/references/4-deep-learning-fundacionais.md +24 -0
  93. package/skills/eng-ai/references/5-llms-chatbots-agentes-rag.md +25 -0
  94. package/skills/eng-ai/references/6-avaliacao-experimentacao.md +24 -0
  95. package/skills/eng-ai/references/7-engenharia-sistemas-ia.md +24 -0
  96. package/skills/eng-ai/references/8-mlops-plataforma.md +24 -0
  97. package/skills/eng-ai/references/9-produto-ia.md +24 -0
  98. package/skills/eng-ai/references/especializacoes.md +17 -0
  99. package/skills/eng-automation/SKILL.md +290 -0
  100. package/skills/eng-automation/references/1-fundamentos-automacao.md +25 -0
  101. package/skills/eng-automation/references/10-ingestao-dados.md +25 -0
  102. package/skills/eng-automation/references/11-captcha-antibot.md +25 -0
  103. package/skills/eng-automation/references/12-limites-taxa-sessoes.md +25 -0
  104. package/skills/eng-automation/references/13-agendamento-orquestracao.md +24 -0
  105. package/skills/eng-automation/references/14-retentativa-erros.md +25 -0
  106. package/skills/eng-automation/references/15-autenticacao-credenciais.md +24 -0
  107. package/skills/eng-automation/references/16-observabilidade-confiabilidade.md +24 -0
  108. package/skills/eng-automation/references/2-aquisicao-dados.md +25 -0
  109. package/skills/eng-automation/references/3-automacao-navegador.md +24 -0
  110. package/skills/eng-automation/references/4-scraping-crawling.md +25 -0
  111. package/skills/eng-automation/references/5-apis-rest-graphql-soap.md +26 -0
  112. package/skills/eng-automation/references/6-webhooks-integracao.md +25 -0
  113. package/skills/eng-automation/references/7-rpa.md +25 -0
  114. package/skills/eng-automation/references/8-extracao-estruturada.md +25 -0
  115. package/skills/eng-automation/references/9-extracao-documentos.md +25 -0
  116. package/skills/eng-automation/references/especializacoes.md +18 -0
  117. package/skills/eng-backend/SKILL.md +6 -0
  118. package/skills/eng-backend-arch-c4/SKILL.md +10 -4
  119. package/skills/eng-backend-microservices-trace/SKILL.md +6 -0
  120. package/skills/eng-backend-nestjs/SKILL.md +17 -462
  121. package/skills/eng-backend-nestjs/references/1-arquitetura-modulos.md +54 -0
  122. package/skills/eng-backend-nestjs/references/2-guards-autenticacao.md +118 -0
  123. package/skills/eng-backend-nestjs/references/3-interceptors-pipes-filters.md +113 -0
  124. package/skills/eng-backend-nestjs/references/4-config-logging.md +61 -0
  125. package/skills/eng-backend-nestjs/references/5-testes.md +83 -0
  126. package/skills/eng-backend-nestjs/references/6-problemas-comuns.md +53 -0
  127. package/skills/eng-backend-rabbitmq/SKILL.md +15 -298
  128. package/skills/eng-backend-rabbitmq/references/1-referencia-endpoints.md +28 -0
  129. package/skills/eng-backend-rabbitmq/references/2-codigo-consumer-producer.md +90 -0
  130. package/skills/eng-backend-rabbitmq/references/3-arquiteturas-comuns.md +101 -0
  131. package/skills/eng-backend-rabbitmq/references/4-troubleshooting.md +93 -0
  132. package/skills/eng-data/SKILL.md +271 -0
  133. package/skills/eng-data/references/1-fundamentos-dados.md +24 -0
  134. package/skills/eng-data/references/10-ciencia-dados.md +24 -0
  135. package/skills/eng-data/references/11-dataops-plataforma.md +24 -0
  136. package/skills/eng-data/references/12-seguranca-privacidade.md +24 -0
  137. package/skills/eng-data/references/2-modelagem-dados.md +24 -0
  138. package/skills/eng-data/references/3-bancos-dados.md +24 -0
  139. package/skills/eng-data/references/4-arquitetura-dados.md +24 -0
  140. package/skills/eng-data/references/5-pipelines-integracao.md +24 -0
  141. package/skills/eng-data/references/6-qualidade-dados.md +24 -0
  142. package/skills/eng-data/references/7-governanca-metadados.md +24 -0
  143. package/skills/eng-data/references/8-estatistica-analise.md +24 -0
  144. package/skills/eng-data/references/9-analytics-bi.md +24 -0
  145. package/skills/eng-data/references/especializacoes.md +20 -0
  146. package/skills/eng-frontend/SKILL.md +18 -5
  147. package/skills/eng-frontend/references/13-design-system.md +48 -0
  148. package/skills/eng-frontend/references/14-microfrontend.md +46 -0
  149. package/skills/eng-global-docs-write/SKILL.md +10 -5
  150. package/skills/eng-global-pr/SKILL.md +7 -1
  151. package/skills/eng-global-task-comment/SKILL.md +8 -2
  152. package/skills/eng-platform/SKILL.md +331 -0
  153. package/skills/eng-platform/references/1-fundamentos-sistemas.md +23 -0
  154. package/skills/eng-platform/references/10-resiliencia-arquitetura.md +24 -0
  155. package/skills/eng-platform/references/11-plataforma-devex.md +24 -0
  156. package/skills/eng-platform/references/12-finops-custos.md +24 -0
  157. package/skills/eng-platform/references/13-seguranca-fundamentos.md +26 -0
  158. package/skills/eng-platform/references/14-profiling-performance.md +23 -0
  159. package/skills/eng-platform/references/15-testes-carga-chaos.md +23 -0
  160. package/skills/eng-platform/references/16-cache-multicamada.md +22 -0
  161. package/skills/eng-platform/references/2-redes.md +23 -0
  162. package/skills/eng-platform/references/3-cloud.md +24 -0
  163. package/skills/eng-platform/references/4-iac-automacao.md +24 -0
  164. package/skills/eng-platform/references/5-containers-orquestracao.md +25 -0
  165. package/skills/eng-platform/references/6-cicd-entrega.md +24 -0
  166. package/skills/eng-platform/references/7-observabilidade.md +24 -0
  167. package/skills/eng-platform/references/8-confiabilidade-sre.md +24 -0
  168. package/skills/eng-platform/references/9-seguranca-infra.md +26 -0
  169. package/skills/eng-platform/references/especializacoes.md +34 -0
  170. package/skills/eng-qa/SKILL.md +283 -0
  171. package/skills/eng-qa/references/1-fundamentos-qualidade.md +25 -0
  172. package/skills/eng-qa/references/10-confiabilidade.md +25 -0
  173. package/skills/eng-qa/references/11-seguranca.md +24 -0
  174. package/skills/eng-qa/references/12-acessibilidade.md +24 -0
  175. package/skills/eng-qa/references/13-dados-testes.md +24 -0
  176. package/skills/eng-qa/references/14-gestao-testes-defeitos.md +25 -0
  177. package/skills/eng-qa/references/15-arquitetura-qa.md +25 -0
  178. package/skills/eng-qa/references/2-estrategia-planejamento.md +25 -0
  179. package/skills/eng-qa/references/3-design-testes.md +25 -0
  180. package/skills/eng-qa/references/4-manual-exploratorio.md +24 -0
  181. package/skills/eng-qa/references/5-automacao-testes.md +25 -0
  182. package/skills/eng-qa/references/6-api-integracao.md +25 -0
  183. package/skills/eng-qa/references/7-contrato.md +24 -0
  184. package/skills/eng-qa/references/8-interface-compat-mobile.md +24 -0
  185. package/skills/eng-qa/references/9-performance.md +24 -0
  186. package/skills/eng-qa/references/especializacoes.md +29 -0
  187. package/skills/eng-qa-planner/SKILL.md +264 -0
  188. package/skills/jarvis-context-detect/SKILL.md +7 -1
  189. package/skills/jarvis-context-detect/assets/context-profile-template.md +1 -1
  190. package/skills/jarvis-create-specialization/SKILL.md +14 -0
  191. package/skills/jarvis-docs-central/README.md +31 -28
  192. package/skills/jarvis-docs-central/SKILL.md +11 -5
  193. package/skills/jarvis-docs-index/SKILL.md +10 -2
  194. package/skills/jarvis-evolution-architect/SKILL.md +6 -0
  195. package/skills/jarvis-init/SKILL.md +22 -15
  196. package/skills/jarvis-init/assets/passo-adicionar-stacks.md +47 -18
  197. package/skills/jarvis-list-specializations/SKILL.md +36 -17
  198. package/skills/jarvis-report-issue/SKILL.md +7 -1
  199. package/skills/product-roadmap-report/SKILL.md +8 -0
  200. package/skills/product-specs/SKILL.md +9 -0
  201. package/skills/product-specs-update/SKILL.md +6 -0
  202. package/taxonomy.md +16 -28
  203. package/templates/AGENTS.md +2 -0
  204. package/templates/ENV-template.md +10 -2
  205. package/templates/engineering/qa/qa.sprint-plan-template.md +1 -1
  206. package/templates/engineering/tech-spec-delivery-template.md +179 -0
  207. package/workflows/AGENTS.md +14 -2
  208. package/workflows/README.md +8 -16
  209. package/workflows/all-tools.md +7 -1
  210. package/workflows/engineering/data/data.contract.md +8 -0
  211. package/workflows/engineering/data/data.new-pipeline.md +15 -4
  212. package/workflows/engineering/eng.breakdown-subtasks.md +9 -0
  213. package/workflows/engineering/eng.bug-audit.md +10 -13
  214. package/workflows/engineering/eng.build-tech-spec.md +17 -146
  215. package/workflows/engineering/eng.create-ard-from-code.md +8 -0
  216. package/workflows/engineering/eng.create-ard.md +8 -0
  217. package/workflows/engineering/eng.create-rfc.md +7 -0
  218. package/workflows/engineering/eng.debug.md +5 -21
  219. package/workflows/engineering/eng.docs.md +9 -0
  220. package/workflows/engineering/eng.light-arch.md +7 -0
  221. package/workflows/engineering/eng.plan.md +15 -2
  222. package/workflows/engineering/eng.pr.md +24 -30
  223. package/workflows/engineering/eng.pre-pr.md +33 -15
  224. package/workflows/engineering/eng.review.md +18 -11
  225. package/workflows/engineering/eng.rpa.robot.md +17 -7
  226. package/workflows/engineering/eng.start.md +33 -12
  227. package/workflows/engineering/eng.work.md +25 -17
  228. package/workflows/engineering/frontend/eng.frontend-component.md +7 -0
  229. package/workflows/engineering/frontend/eng.frontend-perf-audit.md +8 -0
  230. package/workflows/engineering/frontend/eng.frontend-review.md +8 -0
  231. package/workflows/engineering/qa/eng.qa-refinement-entry.md +7 -0
  232. package/workflows/engineering/qa/eng.qa-release-signoff.md +6 -0
  233. package/workflows/engineering/qa/eng.qa-sprint-planning.md +7 -1
  234. package/workflows/product/prod.roadmap.preview.md +9 -0
  235. package/workflows/product/prod.spec.ard.md +97 -0
  236. package/workflows/product/prod.spec.breakdown.md +10 -0
  237. package/workflows/product/prod.spec.clarify.md +8 -2
  238. package/workflows/product/prod.spec.discovery.md +11 -1
  239. package/workflows/product/prod.spec.epic.md +8 -0
  240. package/workflows/product/prod.spec.frd.md +7 -0
  241. package/workflows/product/prod.spec.issue.md +6 -0
  242. package/workflows/product/prod.spec.md +15 -1
  243. package/workflows/product/prod.spec.prd.md +7 -0
  244. package/workflows/taxonomy.md +58 -6
  245. package/workflows/warm-up.md +47 -60
  246. package/agents/engineering/eng.cybersecurity.agent.md +0 -488
  247. package/agents/engineering/qa/eng.qa.cypress-specialist.md +0 -109
  248. package/bin/lib/docs/fetch-file.sh +0 -41
  249. package/bin/lib/docs/publish-file.sh +0 -284
  250. package/rules/engineering/eng.downstream-flow-rules.md +0 -297
  251. package/rules/engineering/qa/eng.qa.exploratory-session-rules.md +0 -137
  252. package/rules/engineering/qa/eng.qa.quality-gate-scoring-rules.md +0 -181
  253. package/rules/engineering/qa/eng.qa.tech-spec-validation-criteria-rules.md +0 -120
  254. package/skills/eng-ai-engineer/SKILL.md +0 -297
  255. package/skills/eng-data-bi/SKILL.md +0 -200
  256. package/skills/eng-data-debug/SKILL.md +0 -308
  257. package/skills/eng-data-engineer/SKILL.md +0 -257
  258. package/skills/eng-data-onboard/SKILL.md +0 -311
  259. package/skills/eng-data-orchestrator/SKILL.md +0 -427
  260. package/skills/eng-devops-performance-engineer/SKILL.md +0 -313
  261. package/skills/eng-frontend-design-system/SKILL.md +0 -621
  262. package/skills/eng-frontend-microfrontend/SKILL.md +0 -604
  263. package/skills/eng-global-browser-extension-builder/SKILL.md +0 -386
  264. package/skills/eng-global-jira-comment/SKILL.md +0 -21
  265. package/skills/eng-qa-a11y-audit/SKILL.md +0 -270
  266. package/skills/eng-qa-bug-report/SKILL.md +0 -1089
  267. package/skills/eng-qa-bug-report/TASK_MANAGERS.md +0 -138
  268. package/skills/eng-qa-cypress-e2e/SKILL.md +0 -178
  269. package/skills/eng-qa-dev-guide/SKILL.md +0 -165
  270. package/skills/eng-qa-e2e/SKILL.md +0 -401
  271. package/skills/eng-qa-e2e-spec-writer/SKILL.md +0 -323
  272. package/skills/eng-qa-exploratory/SKILL.md +0 -189
  273. package/skills/eng-qa-gate/SKILL.md +0 -371
  274. package/skills/eng-qa-gate/assets/checklist-validacao.md +0 -291
  275. package/skills/eng-qa-graphql-contract/SKILL.md +0 -257
  276. package/skills/eng-qa-quality-report/SKILL.md +0 -413
  277. package/skills/eng-qa-test-plan/SKILL.md +0 -467
  278. package/skills/eng-qa-testsprite/SKILL.md +0 -326
  279. package/skills/eng-qa-testsprite/references/testsprite-mcp.md +0 -224
  280. package/skills/eng-qa-unit-test/SKILL.md +0 -472
  281. package/skills/eng-scraper/SKILL.md +0 -684
  282. package/skills/eng-scraper-robot-builder/SKILL.md +0 -371
  283. package/skills/eng-security-cybersecurity/SKILL.md +0 -646
  284. package/skills/eng-security-patch/SKILL.md +0 -382
  285. package/skills/eng-security-threat-model/SKILL.md +0 -165
  286. package/skills/eng-security-triage/SKILL.md +0 -270
  287. package/templates/engineering/qa/eng.qa.quality-gate-examples-template.md +0 -311
  288. package/templates/engineering/qa/eng.qa.quality-gate-report-template.md +0 -249
  289. package/templates/engineering/qa/qa.cypress-test-template.md +0 -172
  290. package/templates/engineering/qa/qa.exploratory-session-template.md +0 -148
  291. package/templates/engineering/qa/qa.quality-report-template.md +0 -130
  292. package/workflows/engineering/eng.security-audit.md +0 -312
  293. package/workflows/engineering/eng.security-incident.md +0 -275
  294. package/workflows/engineering/eng.security-pipeline.md +0 -210
  295. package/workflows/engineering/eng.security-review.md +0 -235
  296. package/workflows/engineering/qa/eng.qa-dev-quality-guide.md +0 -51
  297. package/workflows/engineering/qa/eng.qa-e2e-test-generation.md +0 -51
  298. package/workflows/engineering/qa/eng.qa-exploratory-session.md +0 -60
  299. package/workflows/engineering/qa/eng.qa-quality-gate-validation.md +0 -202
  300. package/workflows/engineering/qa/eng.qa-quality-report.md +0 -78
  301. /package/skills/{eng-qa-test-plan → eng-qa-planner}/assets/test-coverage-template.md +0 -0
  302. /package/skills/{eng-qa-test-plan → eng-qa-planner}/assets/test-patterns.md +0 -0
  303. /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
+ }
@@ -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
- `https://api.github.com/repos/${path}/issues/${number}/comments`,
83
- {
84
- method: "POST",
85
- headers: {
86
- Authorization: `Bearer ${token}`,
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
- const cardKey = process.argv[2];
111
- const message = process.argv.slice(3).join(" ");
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
- if (!cardKey || !message) {
114
- console.error("Uso: comment.js <TASK_MANAGER_KEY> <mensagem>");
115
- process.exit(1);
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
- const tm = taskManager();
119
- try {
120
- if (tm === "linear") await commentLinear(cardKey, message);
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
  }