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,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Estimativa do que o framework carrega no início de uma sessão.
|
|
3
|
+
*
|
|
4
|
+
* Mede, por tipo, os arquivos que a IDE lê no início para um perfil (HUB,
|
|
5
|
+
* POSITION, AREA, SQUAD). Tokens são uma **estimativa** (bytes ÷ 4): serve
|
|
6
|
+
* para comparar antes e depois, não para cobrar custo.
|
|
7
|
+
* @module core/token-report
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { listRulesForProfile } from "./profile-filter.js";
|
|
13
|
+
|
|
14
|
+
/** Bytes por token usados na estimativa. */
|
|
15
|
+
export const BYTES_PER_TOKEN = 4;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {number} bytes
|
|
19
|
+
* @returns {number} tokens estimados (bytes ÷ 4, arredondado)
|
|
20
|
+
*/
|
|
21
|
+
export function estimateTokens(bytes) {
|
|
22
|
+
return Math.round(bytes / BYTES_PER_TOKEN);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Tamanho em bytes do bloco de frontmatter (do primeiro `---` ao segundo `---`).
|
|
27
|
+
* @param {string} content
|
|
28
|
+
* @returns {number} 0 quando não há frontmatter
|
|
29
|
+
*/
|
|
30
|
+
export function frontmatterBytes(content) {
|
|
31
|
+
const match = content.match(/^---\s*\n[\s\S]*?\n---/);
|
|
32
|
+
return match ? Buffer.byteLength(match[0], "utf-8") : 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function fileBytes(path) {
|
|
36
|
+
return Buffer.byteLength(readFileSync(path, "utf-8"), "utf-8");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function category(id, label, files, note = "") {
|
|
40
|
+
const sorted = [...files].sort((a, b) => b.bytes - a.bytes || a.path.localeCompare(b.path));
|
|
41
|
+
const bytes = sorted.reduce((sum, f) => sum + f.bytes, 0);
|
|
42
|
+
return { id, label, note, files: sorted, bytes, tokens: estimateTokens(bytes) };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @param {string} root - Raiz do framework (`rules/`, `skills/`, `workflows/`, `AGENTS.md`)
|
|
47
|
+
* @param {import("./profile-filter.js").Profile} profile
|
|
48
|
+
* @param {{ rtkEnabled?: boolean }} [opts]
|
|
49
|
+
*/
|
|
50
|
+
export function buildReport(root, profile, opts = {}) {
|
|
51
|
+
const rtkEnabled = Boolean(opts.rtkEnabled);
|
|
52
|
+
|
|
53
|
+
const rules = listRulesForProfile(join(root, "rules"), profile, { rtkEnabled });
|
|
54
|
+
|
|
55
|
+
const agentsMd = existsSync(join(root, "AGENTS.md"))
|
|
56
|
+
? [{ path: "AGENTS.md", bytes: fileBytes(join(root, "AGENTS.md")) }]
|
|
57
|
+
: [];
|
|
58
|
+
|
|
59
|
+
const skillsDir = join(root, "skills");
|
|
60
|
+
const skills = [];
|
|
61
|
+
if (existsSync(skillsDir)) {
|
|
62
|
+
for (const name of readdirSync(skillsDir)) {
|
|
63
|
+
const skillMd = join(skillsDir, name, "SKILL.md");
|
|
64
|
+
if (!statSync(join(skillsDir, name)).isDirectory() || !existsSync(skillMd)) continue;
|
|
65
|
+
const bytes = frontmatterBytes(readFileSync(skillMd, "utf-8"));
|
|
66
|
+
if (bytes > 0) skills.push({ path: `skills/${name}/SKILL.md`, bytes });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const warmupPath = join(root, "workflows", "warm-up.md");
|
|
71
|
+
const warmup = existsSync(warmupPath)
|
|
72
|
+
? [{ path: "workflows/warm-up.md", bytes: fileBytes(warmupPath) }]
|
|
73
|
+
: [];
|
|
74
|
+
|
|
75
|
+
const categories = [
|
|
76
|
+
category("rules", "Rules", rules, "carregadas no início pelo Claude Code"),
|
|
77
|
+
category("agents-md", "AGENTS.md", agentsMd, "instrução de projeto"),
|
|
78
|
+
category(
|
|
79
|
+
"skills-frontmatter",
|
|
80
|
+
"Frontmatter dos skills",
|
|
81
|
+
skills,
|
|
82
|
+
"sempre visível; o corpo só ao invocar"
|
|
83
|
+
),
|
|
84
|
+
category("warmup", "warm-up", warmup, "só se executado no início da sessão"),
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
const bytes = categories.reduce((sum, c) => sum + c.bytes, 0);
|
|
88
|
+
return {
|
|
89
|
+
profile: {
|
|
90
|
+
HUB: profile.HUB,
|
|
91
|
+
POSITION: profile.POSITION,
|
|
92
|
+
AREA: profile.AREA,
|
|
93
|
+
SQUAD: profile.SQUAD,
|
|
94
|
+
},
|
|
95
|
+
rtkEnabled,
|
|
96
|
+
estimate: `tokens = bytes ÷ ${BYTES_PER_TOKEN} (estimativa)`,
|
|
97
|
+
categories,
|
|
98
|
+
total: { bytes, tokens: estimateTokens(bytes) },
|
|
99
|
+
};
|
|
100
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Busca um arquivo do central-docs (GitLab/GitHub/Bitbucket), com
|
|
3
|
+
* cache Redis best-effort. Substitui `fetch-file.sh` (removido) — mesmo
|
|
4
|
+
* comportamento, sem subprocess: chama `fetchRawFile` de `vcs/api.js` direto.
|
|
5
|
+
* @module docs/fetch-file
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { fetchRawFile } from "../vcs/api.js";
|
|
9
|
+
import { redisGet, redisSetex } from "./redis-cache.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} filePath - Path do arquivo dentro do repo central-docs
|
|
13
|
+
* @param {string} [ref] - Branch/ref (padrão: "main")
|
|
14
|
+
* @param {Object} [opts]
|
|
15
|
+
* @param {string} [opts.repo] - CENTRAL_DOCS_REPO (padrão: `process.env.CENTRAL_DOCS_REPO`)
|
|
16
|
+
* @param {string|number} [opts.cacheTtl] - TTL do cache em segundos (padrão: `process.env.CENTRAL_DOCS_CACHE_TTL` ou 3600)
|
|
17
|
+
* @returns {Promise<string>} conteúdo do arquivo
|
|
18
|
+
* @throws {Error} sem `filePath`, sem `repo`, ou erro do adapter (404/401/403 — ver `fetchRawFile`)
|
|
19
|
+
*/
|
|
20
|
+
export async function fetchFile(filePath, ref = "main", opts = {}) {
|
|
21
|
+
if (!filePath) {
|
|
22
|
+
throw new Error("fetchFile: file-path é obrigatório");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const repo = opts.repo ?? process.env.CENTRAL_DOCS_REPO;
|
|
26
|
+
if (!repo) {
|
|
27
|
+
throw new Error("CENTRAL_DOCS_REPO não definido no ENV.md");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const cacheKey = `docs:${filePath}:${ref}`;
|
|
31
|
+
const cached = redisGet(cacheKey);
|
|
32
|
+
if (cached) return cached;
|
|
33
|
+
|
|
34
|
+
const content = await fetchRawFile(repo, filePath, ref);
|
|
35
|
+
|
|
36
|
+
redisSetex(cacheKey, opts.cacheTtl ?? process.env.CENTRAL_DOCS_CACHE_TTL ?? "3600", content);
|
|
37
|
+
|
|
38
|
+
return content;
|
|
39
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Publica um documento no central-docs (branch + commit + MR/PR).
|
|
3
|
+
* Substitui `publish-file.sh` (removido) — chama os adapters de `vcs/api.js`
|
|
4
|
+
* direto, sem subprocess, sem arquivo temporário para passar JSON/body.
|
|
5
|
+
* @module docs/publish-file
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readFileSync } from "node:fs";
|
|
9
|
+
import { validateFrontmatter, extractMetadata } from "./validate-frontmatter.js";
|
|
10
|
+
import { commitFiles, createMergeRequest } from "../vcs/api.js";
|
|
11
|
+
import { fetchFile } from "./fetch-file.js";
|
|
12
|
+
import { redisDel } from "./redis-cache.js";
|
|
13
|
+
import { logger } from "../utils/logger.js";
|
|
14
|
+
|
|
15
|
+
const VALID_TYPES = ["prd", "frd", "ard", "rfc", "swagger", "qa-report"];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {{ tipo: string, squad: string, workspace: string, feature: string, localFile: string }} p
|
|
19
|
+
* @returns {{ destPath: string, area: "product"|"engineering" }}
|
|
20
|
+
*/
|
|
21
|
+
export function resolveDestPath({ tipo, squad, workspace, feature, localFile }) {
|
|
22
|
+
const base = `${squad}/${workspace}`;
|
|
23
|
+
|
|
24
|
+
if (tipo === "prd" || tipo === "frd") {
|
|
25
|
+
return { destPath: `${base}/product/${tipo}-${feature}.md`, area: "product" };
|
|
26
|
+
}
|
|
27
|
+
if (tipo === "ard") {
|
|
28
|
+
return { destPath: `${base}/engineering/ARD/${tipo}-${feature}.md`, area: "engineering" };
|
|
29
|
+
}
|
|
30
|
+
if (tipo === "rfc") {
|
|
31
|
+
return { destPath: `${base}/engineering/RFC/${tipo}-${feature}.md`, area: "engineering" };
|
|
32
|
+
}
|
|
33
|
+
if (tipo === "swagger") {
|
|
34
|
+
const ext = localFile.split(".").pop().toLowerCase();
|
|
35
|
+
const swaggerExt = ext === "yaml" || ext === "yml" ? ext : ext === "json" ? "json" : null;
|
|
36
|
+
const destPath = swaggerExt
|
|
37
|
+
? `${base}/engineering/swagger/api-${feature}.${swaggerExt}`
|
|
38
|
+
: `${base}/engineering/swagger/swagger-${feature}.md`; // default: .md (wrapper com metadados)
|
|
39
|
+
return { destPath, area: "engineering" };
|
|
40
|
+
}
|
|
41
|
+
if (tipo === "qa-report") {
|
|
42
|
+
return { destPath: `${base}/engineering/qa/qa-report-${feature}.md`, area: "engineering" };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
throw new Error(`Tipo inválido: ${tipo}\nTipos válidos: ${VALID_TYPES.join(", ")}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Insere `newLine` logo após o header `## {area}` — sem nunca descartar uma
|
|
50
|
+
* linha existente. O `publish-file.sh` original usava `getline` do AWK para
|
|
51
|
+
* "pular" a linha seguinte ao header antes de imprimir a nova entrada; essa
|
|
52
|
+
* linha pulada nunca era reimpressa, o que apagava silenciosamente a entrada
|
|
53
|
+
* anterior do índice na segunda publicação numa mesma seção (reproduzido e
|
|
54
|
+
* documentado em `architecture.md` desta sessão, seção 3.4).
|
|
55
|
+
* @param {string} content
|
|
56
|
+
* @param {"product"|"engineering"} area
|
|
57
|
+
* @param {string} newLine
|
|
58
|
+
* @returns {{ content: string, found: boolean }}
|
|
59
|
+
*/
|
|
60
|
+
export function insertIndexLine(content, area, newLine) {
|
|
61
|
+
const lines = content.split("\n");
|
|
62
|
+
const headerPattern = /^## (product|engineering)\b/;
|
|
63
|
+
|
|
64
|
+
for (let i = 0; i < lines.length; i++) {
|
|
65
|
+
const match = lines[i].match(headerPattern);
|
|
66
|
+
if (match && match[1] === area) {
|
|
67
|
+
lines.splice(i + 1, 0, newLine);
|
|
68
|
+
return { content: lines.join("\n"), found: true };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const trimmed = content.replace(/\n+$/, "");
|
|
73
|
+
return { content: `${trimmed}\n\n## ${area}\n${newLine}\n`, found: false };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @param {Object} opts
|
|
78
|
+
* @param {string} opts.localFile - Caminho do arquivo local (ex: `./docs/engineering/ard-x.md`)
|
|
79
|
+
* @param {string} opts.tipo - prd|frd|ard|rfc|swagger|qa-report
|
|
80
|
+
* @param {string} opts.feature - Slug da feature (kebab-case)
|
|
81
|
+
* @param {string} opts.squad - Squad (já resolvido pelo caller: override ou ENV.md)
|
|
82
|
+
* @param {string} opts.workspace - Workspace (já resolvido pelo caller: override ou ENV.md)
|
|
83
|
+
* @param {string} opts.repo - CENTRAL_DOCS_REPO
|
|
84
|
+
* @param {string} [opts.targetBranch] - Branch base do MR/PR (padrão: "dev")
|
|
85
|
+
* @param {string|number} [opts.cacheTtl] - TTL do cache do index.md em segundos (padrão: 3600)
|
|
86
|
+
* @returns {Promise<{ sha: string, mr: { vendor: string, iid: number|string, url: string } }>}
|
|
87
|
+
*/
|
|
88
|
+
export async function publishFile(opts) {
|
|
89
|
+
const {
|
|
90
|
+
localFile,
|
|
91
|
+
tipo,
|
|
92
|
+
feature,
|
|
93
|
+
squad,
|
|
94
|
+
workspace,
|
|
95
|
+
repo,
|
|
96
|
+
targetBranch = "dev",
|
|
97
|
+
cacheTtl = "3600",
|
|
98
|
+
} = opts;
|
|
99
|
+
|
|
100
|
+
if (!repo) {
|
|
101
|
+
throw new Error(
|
|
102
|
+
"CENTRAL_DOCS_REPO não configurado no ENV.md\n\nConfigure a URL do repositório central:\n CENTRAL_DOCS_REPO=https://gitlab.com/org/central-docs.git"
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
logger.info("Validando frontmatter...");
|
|
107
|
+
validateFrontmatter(localFile, tipo);
|
|
108
|
+
logger.info("✅ Frontmatter válido");
|
|
109
|
+
|
|
110
|
+
const metadata = extractMetadata(localFile);
|
|
111
|
+
const version = metadata.version || "1.0.0";
|
|
112
|
+
const status = metadata.status || "draft";
|
|
113
|
+
const jira = metadata.jira || "";
|
|
114
|
+
const docName = metadata.name || "";
|
|
115
|
+
|
|
116
|
+
const { destPath, area } = resolveDestPath({ tipo, squad, workspace, feature, localFile });
|
|
117
|
+
const indexPath = `${squad}/${workspace}/index.md`;
|
|
118
|
+
const branchName = `docs/${squad}/${workspace}/${tipo}-${feature}`;
|
|
119
|
+
|
|
120
|
+
logger.info("");
|
|
121
|
+
logger.info("📋 Resumo da Publicação:");
|
|
122
|
+
logger.info(` Squad: ${squad}`);
|
|
123
|
+
logger.info(` Workspace: ${workspace}`);
|
|
124
|
+
logger.info(` Tipo: ${tipo}`);
|
|
125
|
+
logger.info(` Feature: ${feature}`);
|
|
126
|
+
logger.info(` Destino: ${destPath}`);
|
|
127
|
+
logger.info(` Branch: ${branchName} → ${targetBranch}`);
|
|
128
|
+
logger.info("");
|
|
129
|
+
|
|
130
|
+
const fileContentBase64 = readFileSync(localFile).toString("base64");
|
|
131
|
+
|
|
132
|
+
logger.info("Buscando index.md do workspace...");
|
|
133
|
+
let indexContent = "";
|
|
134
|
+
try {
|
|
135
|
+
indexContent = await fetchFile(indexPath, targetBranch, { repo, cacheTtl });
|
|
136
|
+
} catch {
|
|
137
|
+
indexContent = "";
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
let indexAction;
|
|
141
|
+
if (!indexContent) {
|
|
142
|
+
logger.info("⚠️ index.md não encontrado, será criado");
|
|
143
|
+
indexAction = "create";
|
|
144
|
+
indexContent = `# ${workspace}\n\n**Squad:** ${squad}\n\n## ${area}\n\n`;
|
|
145
|
+
} else {
|
|
146
|
+
indexAction = "update";
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
let newLine = `- [${tipo}-${feature}.md](${area}/${tipo}-${feature}.md) | v${version} | ${status}`;
|
|
150
|
+
if (jira) newLine += ` | jira: ${jira}`;
|
|
151
|
+
if (docName) newLine += ` | ${docName}`;
|
|
152
|
+
|
|
153
|
+
const { content: updatedIndex } = insertIndexLine(indexContent, area, newLine);
|
|
154
|
+
const updatedIndexBase64 = Buffer.from(updatedIndex, "utf-8").toString("base64");
|
|
155
|
+
|
|
156
|
+
logger.info("Criando branch e commit...");
|
|
157
|
+
const commitResult = await commitFiles(repo, {
|
|
158
|
+
branch: branchName,
|
|
159
|
+
startBranch: targetBranch,
|
|
160
|
+
message: `docs(${squad}): add ${tipo} ${feature}`,
|
|
161
|
+
files: [
|
|
162
|
+
{ path: destPath, contentBase64: fileContentBase64, action: "create" },
|
|
163
|
+
{ path: indexPath, contentBase64: updatedIndexBase64, action: indexAction },
|
|
164
|
+
],
|
|
165
|
+
});
|
|
166
|
+
logger.info(`✅ Commit criado: ${commitResult.sha}`);
|
|
167
|
+
|
|
168
|
+
logger.info("Criando Merge/Pull Request...");
|
|
169
|
+
let mrDescription = `## Documento\n\n- **Tipo**: ${tipo}\n- **Workspace**: ${workspace}\n- **Feature**: ${feature}`;
|
|
170
|
+
if (jira) mrDescription += `\n- **Card**: ${jira}`;
|
|
171
|
+
if (docName) mrDescription += `\n- **Nome**: ${docName}`;
|
|
172
|
+
|
|
173
|
+
const mr = await createMergeRequest(repo, {
|
|
174
|
+
source: branchName,
|
|
175
|
+
target: targetBranch,
|
|
176
|
+
title: `docs(${squad}): ${tipo} ${feature}`,
|
|
177
|
+
description: mrDescription,
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
logger.info("");
|
|
181
|
+
logger.info("✅ Merge/Pull Request criado com sucesso!");
|
|
182
|
+
logger.info("");
|
|
183
|
+
logger.info(` #${mr.iid}: ${branchName} → ${targetBranch}`);
|
|
184
|
+
logger.info(` URL: ${mr.url}`);
|
|
185
|
+
logger.info("");
|
|
186
|
+
|
|
187
|
+
redisDel(`docs:${indexPath}:${targetBranch}`);
|
|
188
|
+
redisDel(`docs:${indexPath}:main`);
|
|
189
|
+
logger.info("🔄 Cache Redis invalidado");
|
|
190
|
+
logger.info("");
|
|
191
|
+
logger.info("✅ Publicação concluída!");
|
|
192
|
+
|
|
193
|
+
return { sha: commitResult.sha, mr };
|
|
194
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Cache best-effort via `redis-cli` para fetch-file.js/publish-file.js.
|
|
3
|
+
* Nunca lança — falha de leitura/escrita no cache nunca quebra o fluxo principal
|
|
4
|
+
* (mesmo contrato do `fetch-file.sh`/`publish-file.sh` originais, que faziam
|
|
5
|
+
* `redis-cli ... 2>/dev/null || true`).
|
|
6
|
+
* @module docs/redis-cache
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { execFileSync } from "node:child_process";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} key
|
|
13
|
+
* @returns {string} valor em cache, ou "" se não houver (ou `redis-cli` falhar)
|
|
14
|
+
*/
|
|
15
|
+
export function redisGet(key) {
|
|
16
|
+
try {
|
|
17
|
+
return execFileSync("redis-cli", ["GET", key], {
|
|
18
|
+
encoding: "utf-8",
|
|
19
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
20
|
+
}).trim();
|
|
21
|
+
} catch {
|
|
22
|
+
return "";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {string} key
|
|
28
|
+
* @param {string|number} ttlSeconds
|
|
29
|
+
* @param {string} value
|
|
30
|
+
*/
|
|
31
|
+
export function redisSetex(key, ttlSeconds, value) {
|
|
32
|
+
try {
|
|
33
|
+
execFileSync("redis-cli", ["-x", "SETEX", key, String(ttlSeconds)], {
|
|
34
|
+
input: value,
|
|
35
|
+
stdio: ["pipe", "ignore", "ignore"],
|
|
36
|
+
});
|
|
37
|
+
} catch {
|
|
38
|
+
// best-effort
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** @param {string} key */
|
|
43
|
+
export function redisDel(key) {
|
|
44
|
+
try {
|
|
45
|
+
execFileSync("redis-cli", ["DEL", key], { stdio: "ignore" });
|
|
46
|
+
} catch {
|
|
47
|
+
// best-effort
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs'
|
|
2
|
+
import { parseFrontmatter, extractFrontmatterBlock } from '../utils/frontmatter.js'
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Valida frontmatter YAML de documentos (PRD, FRD, ARD, RFC)
|
|
@@ -10,11 +11,10 @@ import { readFileSync } from 'node:fs'
|
|
|
10
11
|
*/
|
|
11
12
|
export function validateFrontmatter(filePath, tipo) {
|
|
12
13
|
const content = readFileSync(filePath, 'utf-8')
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if (!frontmatterMatch) {
|
|
14
|
+
|
|
15
|
+
const frontmatter = extractFrontmatterBlock(content)
|
|
16
|
+
|
|
17
|
+
if (frontmatter === null) {
|
|
18
18
|
throw new Error(
|
|
19
19
|
`Frontmatter YAML não encontrado em ${filePath}\n` +
|
|
20
20
|
'Formato esperado:\n' +
|
|
@@ -25,8 +25,7 @@ export function validateFrontmatter(filePath, tipo) {
|
|
|
25
25
|
)
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
const
|
|
29
|
-
const metadata = parseFrontmatter(frontmatter)
|
|
28
|
+
const metadata = parseFrontmatter(content)
|
|
30
29
|
|
|
31
30
|
// Validar campos obrigatórios por tipo
|
|
32
31
|
const requiredFields = getRequiredFields(tipo)
|
|
@@ -67,34 +66,6 @@ export function validateFrontmatter(filePath, tipo) {
|
|
|
67
66
|
return metadata
|
|
68
67
|
}
|
|
69
68
|
|
|
70
|
-
/**
|
|
71
|
-
* Parseia frontmatter YAML simples (key: value)
|
|
72
|
-
*
|
|
73
|
-
* @param {string} frontmatter - Conteúdo do frontmatter
|
|
74
|
-
* @returns {Object} Metadados parseados
|
|
75
|
-
*/
|
|
76
|
-
function parseFrontmatter(frontmatter) {
|
|
77
|
-
const metadata = {}
|
|
78
|
-
const lines = frontmatter.split('\n')
|
|
79
|
-
|
|
80
|
-
for (const line of lines) {
|
|
81
|
-
const trimmed = line.trim()
|
|
82
|
-
if (!trimmed || trimmed.startsWith('#')) continue
|
|
83
|
-
|
|
84
|
-
const colonIndex = trimmed.indexOf(':')
|
|
85
|
-
if (colonIndex === -1) continue
|
|
86
|
-
|
|
87
|
-
const key = trimmed.slice(0, colonIndex).trim()
|
|
88
|
-
const value = trimmed.slice(colonIndex + 1).trim()
|
|
89
|
-
|
|
90
|
-
if (key && value) {
|
|
91
|
-
metadata[key] = value
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return metadata
|
|
96
|
-
}
|
|
97
|
-
|
|
98
69
|
/**
|
|
99
70
|
* Retorna campos obrigatórios por tipo de documento
|
|
100
71
|
*
|
|
@@ -121,13 +92,12 @@ function getRequiredFields(tipo) {
|
|
|
121
92
|
*/
|
|
122
93
|
export function extractMetadata(filePath) {
|
|
123
94
|
const content = readFileSync(filePath, 'utf-8')
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (!frontmatterMatch) {
|
|
95
|
+
|
|
96
|
+
if (extractFrontmatterBlock(content) === null) {
|
|
127
97
|
return {}
|
|
128
98
|
}
|
|
129
99
|
|
|
130
|
-
const metadata = parseFrontmatter(
|
|
100
|
+
const metadata = parseFrontmatter(content)
|
|
131
101
|
|
|
132
102
|
// Normalizar campos (suportar variações de nomenclatura)
|
|
133
103
|
const taskLink = metadata.task_link || metadata.jira
|
package/bin/lib/env-loader.js
CHANGED
|
@@ -2,25 +2,7 @@ import { readFileSync, existsSync } from "node:fs";
|
|
|
2
2
|
|
|
3
3
|
import { join, isAbsolute, dirname, basename } from "node:path";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
windsurf: ".windsurf",
|
|
7
|
-
claude: ".claude",
|
|
8
|
-
cursor: ".cursor",
|
|
9
|
-
vscode: ".vscode",
|
|
10
|
-
codex: ".codex",
|
|
11
|
-
opencode: ".opencode",
|
|
12
|
-
gemini: ".gemini",
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const AUTO_DETECT_ORDER = [
|
|
16
|
-
"windsurf",
|
|
17
|
-
"claude",
|
|
18
|
-
"cursor",
|
|
19
|
-
"vscode",
|
|
20
|
-
"codex",
|
|
21
|
-
"opencode",
|
|
22
|
-
"gemini",
|
|
23
|
-
];
|
|
5
|
+
import { getIDEFolder, getIDEValues, isIDESupported } from "./config/ide-config.js";
|
|
24
6
|
|
|
25
7
|
function* walkAncestors(start) {
|
|
26
8
|
let dir = start;
|
|
@@ -33,12 +15,11 @@ function* walkAncestors(start) {
|
|
|
33
15
|
}
|
|
34
16
|
|
|
35
17
|
function envsAt(dir, ideFilter = null) {
|
|
36
|
-
const ides = ideFilter ? [ideFilter] :
|
|
18
|
+
const ides = ideFilter ? [ideFilter] : getIDEValues();
|
|
37
19
|
const found = [];
|
|
38
20
|
for (const ide of ides) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const p = join(dir, folder, "ENV.md");
|
|
21
|
+
if (!isIDESupported(ide)) continue;
|
|
22
|
+
const p = join(dir, `.${getIDEFolder(ide)}`, "ENV.md");
|
|
42
23
|
if (existsSync(p)) found.push({ ide, path: p, root: dir });
|
|
43
24
|
}
|
|
44
25
|
return found;
|
|
@@ -65,13 +46,10 @@ export function deriveWorkspaceName(envPath) {
|
|
|
65
46
|
export function resolveEnvPath(cwd, flags = {}) {
|
|
66
47
|
// 1. --env-file
|
|
67
48
|
if (flags["env-file"]) {
|
|
68
|
-
const p = isAbsolute(flags["env-file"])
|
|
69
|
-
? flags["env-file"]
|
|
70
|
-
: join(cwd, flags["env-file"]);
|
|
49
|
+
const p = isAbsolute(flags["env-file"]) ? flags["env-file"] : join(cwd, flags["env-file"]);
|
|
71
50
|
if (!existsSync(p)) {
|
|
72
51
|
throw new Error(
|
|
73
|
-
`ENV.md não encontrado: ${p}\n` +
|
|
74
|
-
`Dica: verifique o path passado em --env-file`,
|
|
52
|
+
`ENV.md não encontrado: ${p}\n` + `Dica: verifique o path passado em --env-file`
|
|
75
53
|
);
|
|
76
54
|
}
|
|
77
55
|
return { path: p, source: "--env-file", warnings: [], root: dirname(dirname(p)) };
|
|
@@ -79,10 +57,9 @@ export function resolveEnvPath(cwd, flags = {}) {
|
|
|
79
57
|
|
|
80
58
|
// 2. --ide (cwd e ancestrais — abre um repo filho e ainda acha o workspace)
|
|
81
59
|
if (flags.ide) {
|
|
82
|
-
if (!
|
|
60
|
+
if (!isIDESupported(flags.ide)) {
|
|
83
61
|
throw new Error(
|
|
84
|
-
`IDE não suportada: "${flags.ide}"\n` +
|
|
85
|
-
`IDEs válidas: ${Object.keys(IDE_DIRS).join(", ")}`,
|
|
62
|
+
`IDE não suportada: "${flags.ide}"\n` + `IDEs válidas: ${getIDEValues().join(", ")}`
|
|
86
63
|
);
|
|
87
64
|
}
|
|
88
65
|
for (const dir of walkAncestors(cwd)) {
|
|
@@ -98,7 +75,7 @@ export function resolveEnvPath(cwd, flags = {}) {
|
|
|
98
75
|
}
|
|
99
76
|
throw new Error(
|
|
100
77
|
`ENV.md não encontrado para --ide ${flags.ide} a partir de ${cwd}\n` +
|
|
101
|
-
`Dica: rode jarvis init --ide ${flags.ide} na pasta do workspace
|
|
78
|
+
`Dica: rode jarvis init --ide ${flags.ide} na pasta do workspace`
|
|
102
79
|
);
|
|
103
80
|
}
|
|
104
81
|
|
|
@@ -114,7 +91,7 @@ export function resolveEnvPath(cwd, flags = {}) {
|
|
|
114
91
|
}
|
|
115
92
|
|
|
116
93
|
// 4. IDE env var
|
|
117
|
-
if (process.env.IDE &&
|
|
94
|
+
if (process.env.IDE && isIDESupported(process.env.IDE)) {
|
|
118
95
|
for (const dir of walkAncestors(cwd)) {
|
|
119
96
|
const found = envsAt(dir, process.env.IDE);
|
|
120
97
|
if (found.length === 1) {
|
|
@@ -135,7 +112,7 @@ export function resolveEnvPath(cwd, flags = {}) {
|
|
|
135
112
|
const ides = found.map((f) => f.ide).join(" | ");
|
|
136
113
|
throw new Error(
|
|
137
114
|
`Múltiplos ENV.md encontrados em ${dir} (${ides}).\n` +
|
|
138
|
-
`Especifique a IDE ativa. Exemplo: --ide claude
|
|
115
|
+
`Especifique a IDE ativa. Exemplo: --ide claude`
|
|
139
116
|
);
|
|
140
117
|
}
|
|
141
118
|
if (found.length === 1) {
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Índice nome → artefato (workflows, skills e agentes ativos).
|
|
3
|
+
*
|
|
4
|
+
* Montado a cada execução, sem arquivo persistido. O nome identifica o item:
|
|
5
|
+
* - workflow: arquivo `workflows/**\/{nome}.md` (sem README e AGENTS)
|
|
6
|
+
* - skill: pasta `skills/{nome}/SKILL.md`
|
|
7
|
+
* - agente: arquivo `agents/**\/{nome}.md` (fora de `archive/`, sem README e AGENTS)
|
|
8
|
+
* @module flow-map/index
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
12
|
+
import { basename, join, relative, sep } from "node:path";
|
|
13
|
+
|
|
14
|
+
const IGNORED_FILES = new Set(["README.md", "AGENTS.md"]);
|
|
15
|
+
|
|
16
|
+
function walkMarkdown(dir, skipDirs = []) {
|
|
17
|
+
if (!existsSync(dir)) return [];
|
|
18
|
+
return readdirSync(dir).flatMap((name) => {
|
|
19
|
+
const path = join(dir, name);
|
|
20
|
+
if (statSync(path).isDirectory()) {
|
|
21
|
+
return skipDirs.includes(name) ? [] : walkMarkdown(path, skipDirs);
|
|
22
|
+
}
|
|
23
|
+
return name.endsWith(".md") && !IGNORED_FILES.has(name) ? [path] : [];
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {Object} FlowItem
|
|
29
|
+
* @property {string} name
|
|
30
|
+
* @property {"workflow" | "skill" | "agent"} type
|
|
31
|
+
* @property {string} path - Caminho relativo à raiz do framework, com `/`
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {string} root - Raiz do framework (`workflows/`, `skills/`, `agents/`)
|
|
36
|
+
* @returns {{ items: Map<string, FlowItem>, duplicates: string[] }}
|
|
37
|
+
*/
|
|
38
|
+
export function buildIndex(root) {
|
|
39
|
+
const items = new Map();
|
|
40
|
+
const duplicates = [];
|
|
41
|
+
const rel = (path) => relative(root, path).split(sep).join("/");
|
|
42
|
+
|
|
43
|
+
function add(item) {
|
|
44
|
+
if (items.has(item.name)) duplicates.push(item.name);
|
|
45
|
+
else items.set(item.name, item);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
for (const file of walkMarkdown(join(root, "workflows"))) {
|
|
49
|
+
add({ name: basename(file, ".md"), type: "workflow", path: rel(file) });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const skillsDir = join(root, "skills");
|
|
53
|
+
if (existsSync(skillsDir)) {
|
|
54
|
+
for (const dir of readdirSync(skillsDir)) {
|
|
55
|
+
const skillMd = join(skillsDir, dir, "SKILL.md");
|
|
56
|
+
if (existsSync(skillMd)) add({ name: dir, type: "skill", path: rel(skillMd) });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
for (const file of walkMarkdown(join(root, "agents"), ["archive"])) {
|
|
61
|
+
add({ name: basename(file, ".md"), type: "agent", path: rel(file) });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return { items, duplicates };
|
|
65
|
+
}
|