jarvis-ai-framework 1.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 (240) hide show
  1. package/AGENTS.md +416 -0
  2. package/LICENSE +21 -0
  3. package/README.md +190 -0
  4. package/agents/AGENTS.md +234 -0
  5. package/agents/README.md +309 -0
  6. package/agents/engineering/data/eng.data-engineer.agent.md +309 -0
  7. package/agents/engineering/eng.agent.md +303 -0
  8. package/agents/engineering/eng.bug-hunter.md +386 -0
  9. package/agents/engineering/eng.cybersecurity.agent.md +503 -0
  10. package/agents/engineering/eng.dev-code-reviewer.md +148 -0
  11. package/agents/engineering/eng.docs-writer.md +152 -0
  12. package/agents/engineering/eng.frontend.agent.md +117 -0
  13. package/agents/engineering/eng.rpa.agent.md +215 -0
  14. package/agents/engineering/eng.tech-analyst.agent.md +102 -0
  15. package/agents/engineering/eng.ux-designer.agent.md +193 -0
  16. package/agents/engineering/qa/eng.qa.cypress-specialist.md +109 -0
  17. package/agents/engineering/qa/eng.qa.quality-champion-task-agent.md +85 -0
  18. package/agents/engineering/qa/eng.qa.quality-strategist.md +111 -0
  19. package/agents/engineering/qa/eng.qa.test-architect.md +400 -0
  20. package/agents/engineering/qa/eng.qa.test-planner.md +477 -0
  21. package/agents/engineering/qa/eng.qa.testing-engineer.md +339 -0
  22. package/agents/product/prod.pm-checker.md +52 -0
  23. package/bin/commands/docs-publish.js +184 -0
  24. package/bin/commands/docs-sync.js +139 -0
  25. package/bin/commands/info.js +87 -0
  26. package/bin/commands/init.js +237 -0
  27. package/bin/commands/install-rtk.js +90 -0
  28. package/bin/commands/list.js +48 -0
  29. package/bin/commands/qa-signoff.js +112 -0
  30. package/bin/commands/whoami.js +43 -0
  31. package/bin/jarvis.js +159 -0
  32. package/bin/lib/auth/session.js +56 -0
  33. package/bin/lib/config/constants.js +123 -0
  34. package/bin/lib/config/ide-config.js +233 -0
  35. package/bin/lib/core/scanner.js +124 -0
  36. package/bin/lib/core/sync-engine.js +551 -0
  37. package/bin/lib/docs/fetch-file.sh +41 -0
  38. package/bin/lib/docs/publish-file.sh +284 -0
  39. package/bin/lib/docs/validate-frontmatter.js +157 -0
  40. package/bin/lib/env-loader.js +198 -0
  41. package/bin/lib/tasks/comment.js +131 -0
  42. package/bin/lib/utils/git-parser.js +145 -0
  43. package/bin/lib/utils/logger.js +104 -0
  44. package/bin/lib/utils/npmrc-parser.js +106 -0
  45. package/bin/lib/utils/paths.js +55 -0
  46. package/bin/lib/utils/ui.js +59 -0
  47. package/bin/lib/vcs/api.js +312 -0
  48. package/bin/lib/vcs/create-issue.js +43 -0
  49. package/bin/lib/vcs/create-merge.js +43 -0
  50. package/bin/lib/vcs/fetch-raw.js +30 -0
  51. package/bin/postinstall.js +41 -0
  52. package/members.md +25 -0
  53. package/package.json +55 -0
  54. package/rules/AGENTS.md +205 -0
  55. package/rules/engineering/data/data-rules.md +200 -0
  56. package/rules/engineering/eng-rules.md +243 -0
  57. package/rules/engineering/eng-security-rules.md +186 -0
  58. package/rules/engineering/eng.breakdown-subtasks-rules.md +585 -0
  59. package/rules/engineering/eng.bump-rules.md +27 -0
  60. package/rules/engineering/eng.docs-scraping-rules.md +64 -0
  61. package/rules/engineering/eng.downstream-flow-rules.md +297 -0
  62. package/rules/engineering/eng.integrations-rules.md +73 -0
  63. package/rules/engineering/eng.plan-rules.md +333 -0
  64. package/rules/engineering/eng.pr-rules.md +359 -0
  65. package/rules/engineering/eng.pre-pr-rules.md +103 -0
  66. package/rules/engineering/eng.start-rules.md +246 -0
  67. package/rules/engineering/eng.tech-spec-rules.md +968 -0
  68. package/rules/engineering/eng.work-rules.md +312 -0
  69. package/rules/engineering/frontend/eng.frontend-rules.md +147 -0
  70. package/rules/engineering/qa/eng.qa.cypress-standards-rules.md +259 -0
  71. package/rules/engineering/qa/eng.qa.exploratory-session-rules.md +137 -0
  72. package/rules/engineering/qa/eng.qa.quality-gate-scoring-rules.md +181 -0
  73. package/rules/engineering/qa/eng.qa.tech-spec-validation-criteria-rules.md +120 -0
  74. package/rules/engineering/rpa/eng.rpa-rules.md +230 -0
  75. package/rules/product/README.md +24 -0
  76. package/rules/product/prod-rules.md +151 -0
  77. package/rules/rtk-rules.md +68 -0
  78. package/skills/AGENTS.md +290 -0
  79. package/skills/SKILLS-ROADMAP.md +333 -0
  80. package/skills/churn-audit/SKILL.md +385 -0
  81. package/skills/context-detect/SKILL.md +399 -0
  82. package/skills/context-detect/assets/context-profile-template.md +127 -0
  83. package/skills/docs-central/README.md +310 -0
  84. package/skills/docs-central/SKILL.md +423 -0
  85. package/skills/docs-index/SKILL.md +377 -0
  86. package/skills/eng-ai-engineer/SKILL.md +296 -0
  87. package/skills/eng-arch-c4/SKILL.md +358 -0
  88. package/skills/eng-arch-c4/assets/example-code.md +189 -0
  89. package/skills/eng-arch-c4/assets/example-component.md +105 -0
  90. package/skills/eng-arch-c4/assets/example-container.md +104 -0
  91. package/skills/eng-arch-c4/assets/example-context.md +81 -0
  92. package/skills/eng-backend/SKILL.md +776 -0
  93. package/skills/eng-browser-extension-builder/SKILL.md +385 -0
  94. package/skills/eng-cybersecurity/SKILL.md +645 -0
  95. package/skills/eng-data-bi/SKILL.md +199 -0
  96. package/skills/eng-data-debug/SKILL.md +307 -0
  97. package/skills/eng-data-engineer/SKILL.md +256 -0
  98. package/skills/eng-data-onboard/SKILL.md +310 -0
  99. package/skills/eng-data-orchestrator/SKILL.md +426 -0
  100. package/skills/eng-design-system/SKILL.md +619 -0
  101. package/skills/eng-docs-write/SKILL.md +312 -0
  102. package/skills/eng-frontend/SKILL.md +913 -0
  103. package/skills/eng-jira-comment/SKILL.md +17 -0
  104. package/skills/eng-microfrontend/SKILL.md +602 -0
  105. package/skills/eng-ms-trace/SKILL.md +469 -0
  106. package/skills/eng-nestjs/SKILL.md +791 -0
  107. package/skills/eng-performance-engineer/SKILL.md +312 -0
  108. package/skills/eng-pr/SKILL.md +339 -0
  109. package/skills/eng-qa-a11y-audit/SKILL.md +269 -0
  110. package/skills/eng-qa-bug-report/SKILL.md +1088 -0
  111. package/skills/eng-qa-bug-report/TASK_MANAGERS.md +138 -0
  112. package/skills/eng-qa-cypress-e2e/SKILL.md +177 -0
  113. package/skills/eng-qa-dev-guide/SKILL.md +164 -0
  114. package/skills/eng-qa-e2e/SKILL.md +400 -0
  115. package/skills/eng-qa-e2e-spec-writer/SKILL.md +322 -0
  116. package/skills/eng-qa-exploratory/SKILL.md +188 -0
  117. package/skills/eng-qa-gate/SKILL.md +370 -0
  118. package/skills/eng-qa-gate/assets/checklist-validacao.md +291 -0
  119. package/skills/eng-qa-graphql-contract/SKILL.md +256 -0
  120. package/skills/eng-qa-quality-report/SKILL.md +412 -0
  121. package/skills/eng-qa-test-plan/SKILL.md +466 -0
  122. package/skills/eng-qa-test-plan/assets/test-coverage-template.md +92 -0
  123. package/skills/eng-qa-test-plan/assets/test-patterns.md +178 -0
  124. package/skills/eng-qa-testsprite/SKILL.md +325 -0
  125. package/skills/eng-qa-testsprite/references/testsprite-mcp.md +224 -0
  126. package/skills/eng-qa-unit-test/SKILL.md +471 -0
  127. package/skills/eng-rabbitmq/SKILL.md +661 -0
  128. package/skills/eng-scraper/SKILL.md +683 -0
  129. package/skills/eng-scraper-robot-builder/SKILL.md +370 -0
  130. package/skills/eng-security-patch/SKILL.md +378 -0
  131. package/skills/eng-security-triage/SKILL.md +266 -0
  132. package/skills/eng-task-comment/SKILL.md +60 -0
  133. package/skills/eng-tech-analyst/SKILL.md +529 -0
  134. package/skills/eng-threat-model/SKILL.md +161 -0
  135. package/skills/init-jarvis/SKILL.md +1304 -0
  136. package/skills/init-jarvis/assets/mcp-configs.md +389 -0
  137. package/skills/init-jarvis/assets/onboarding-checklist.md +104 -0
  138. package/skills/init-jarvis/assets/setup-guide.md +360 -0
  139. package/skills/lovable-prompt-generator/SKILL.md +304 -0
  140. package/skills/prod-roadmap-report/README.md +303 -0
  141. package/skills/prod-roadmap-report/SKILL.md +198 -0
  142. package/skills/prod-roadmap-report/commands/status.compiled.single.team.md +23 -0
  143. package/skills/prod-roadmap-report/commands/status.list.projects.md +17 -0
  144. package/skills/prod-roadmap-report/commands/status.memory.md +192 -0
  145. package/skills/prod-roadmap-report/commands/status.roadmap.preview.md +94 -0
  146. package/skills/prod-roadmap-report/references/detailed-guide.md +236 -0
  147. package/skills/prod-roadmap-report/rules/detailed-guide.md +237 -0
  148. package/skills/prod-roadmap-report/rules/status-report-rules.md +44 -0
  149. package/skills/prod-roadmap-report/templates/template-multiple-teams-compiled-status.md +53 -0
  150. package/skills/prod-roadmap-report/templates/template-projects-list.md +23 -0
  151. package/skills/prod-roadmap-report/templates/template-single-team-compiled-status.md +60 -0
  152. package/skills/prod-roadmap-report/templates/template-single-team-status.md +49 -0
  153. package/skills/prod-specs/SKILL.md +108 -0
  154. package/skills/prod-specs/references/prod.spec.clarify.md +176 -0
  155. package/skills/prod-specs/references/prod.spec.epic.md +107 -0
  156. package/skills/prod-specs/references/prod.spec.frd.md +135 -0
  157. package/skills/prod-specs/references/prod.spec.issue.md +145 -0
  158. package/skills/prod-specs/references/prod.spec.prd.md +118 -0
  159. package/skills/prod-specs/rules/prod-spec-rules.md +186 -0
  160. package/skills/prod-specs/templates/prod-breakdown-template.md +136 -0
  161. package/skills/prod-specs/templates/prod-epic-template.md +76 -0
  162. package/skills/prod-specs/templates/prod-frd-template.md +172 -0
  163. package/skills/prod-specs/templates/prod-issue-template.md +68 -0
  164. package/skills/prod-specs/templates/prod-prd-full-template.md +159 -0
  165. package/skills/prod-specs/templates/prod-prd-template.md +173 -0
  166. package/skills/prod-specs-update/SKILL.md +272 -0
  167. package/skills/report-issue/SKILL.md +156 -0
  168. package/taxonomy.md +270 -0
  169. package/templates/AGENTS.md +189 -0
  170. package/templates/CDD aplicado a Prompts.md +182 -0
  171. package/templates/ENV-template.md +187 -0
  172. package/templates/engineering/AGENTS-template.md +71 -0
  173. package/templates/engineering/ARD-template.md +193 -0
  174. package/templates/engineering/CONTACTS-template.md +135 -0
  175. package/templates/engineering/PR-template.md +40 -0
  176. package/templates/engineering/RFC-Playbook.md +325 -0
  177. package/templates/engineering/RFC-template.md +199 -0
  178. package/templates/engineering/architecture-template.md +277 -0
  179. package/templates/engineering/breakdown-subtasks-template.md +582 -0
  180. package/templates/engineering/c4-model-template.md +516 -0
  181. package/templates/engineering/data-contract-template.md +135 -0
  182. package/templates/engineering/data-pipeline-template.md +163 -0
  183. package/templates/engineering/plan-template.md +255 -0
  184. package/templates/engineering/qa/eng.qa.quality-gate-examples-template.md +311 -0
  185. package/templates/engineering/qa/eng.qa.quality-gate-report-template.md +249 -0
  186. package/templates/engineering/qa/qa.cypress-test-template.md +172 -0
  187. package/templates/engineering/qa/qa.exploratory-session-template.md +148 -0
  188. package/templates/engineering/qa/qa.quality-report-template.md +130 -0
  189. package/templates/engineering/qa/qa.release-signoff-template.md +54 -0
  190. package/templates/engineering/qa/qa.sprint-plan-template.md +49 -0
  191. package/templates/engineering/swagger-template.md +145 -0
  192. package/templates/engineering/tech-spec-template.md +497 -0
  193. package/templates/engineering/work-progress-template.md +155 -0
  194. package/workflows/AGENTS.md +240 -0
  195. package/workflows/README.md +160 -0
  196. package/workflows/all-tools.md +11 -0
  197. package/workflows/engineering/data/data.contract.md +202 -0
  198. package/workflows/engineering/data/data.new-pipeline.md +234 -0
  199. package/workflows/engineering/eng.breakdown-subtasks.md +420 -0
  200. package/workflows/engineering/eng.bug-audit.md +591 -0
  201. package/workflows/engineering/eng.build-tech-spec.md +1116 -0
  202. package/workflows/engineering/eng.create-ard-from-code.md +259 -0
  203. package/workflows/engineering/eng.create-ard.md +382 -0
  204. package/workflows/engineering/eng.create-rfc.md +245 -0
  205. package/workflows/engineering/eng.debug.md +479 -0
  206. package/workflows/engineering/eng.docs.md +40 -0
  207. package/workflows/engineering/eng.light-arch.md +84 -0
  208. package/workflows/engineering/eng.plan.md +213 -0
  209. package/workflows/engineering/eng.pr.md +466 -0
  210. package/workflows/engineering/eng.pre-pr.md +167 -0
  211. package/workflows/engineering/eng.review.md +185 -0
  212. package/workflows/engineering/eng.rpa.robot.md +342 -0
  213. package/workflows/engineering/eng.security-audit.md +312 -0
  214. package/workflows/engineering/eng.security-incident.md +275 -0
  215. package/workflows/engineering/eng.security-pipeline.md +210 -0
  216. package/workflows/engineering/eng.security-review.md +235 -0
  217. package/workflows/engineering/eng.start.md +494 -0
  218. package/workflows/engineering/eng.work.md +558 -0
  219. package/workflows/engineering/frontend/eng.frontend-component.md +190 -0
  220. package/workflows/engineering/frontend/eng.frontend-perf-audit.md +375 -0
  221. package/workflows/engineering/frontend/eng.frontend-review.md +185 -0
  222. package/workflows/engineering/qa/eng.qa-dev-quality-guide.md +51 -0
  223. package/workflows/engineering/qa/eng.qa-e2e-test-generation.md +51 -0
  224. package/workflows/engineering/qa/eng.qa-exploratory-session.md +60 -0
  225. package/workflows/engineering/qa/eng.qa-quality-gate-validation.md +202 -0
  226. package/workflows/engineering/qa/eng.qa-quality-report.md +83 -0
  227. package/workflows/engineering/qa/eng.qa-refinement-entry.md +83 -0
  228. package/workflows/engineering/qa/eng.qa-release-signoff.md +170 -0
  229. package/workflows/engineering/qa/eng.qa-sprint-planning.md +100 -0
  230. package/workflows/engineering/ta/eng.ta.atendimento.md +93 -0
  231. package/workflows/product/prod.roadmap.preview.md +110 -0
  232. package/workflows/product/prod.spec.breakdown.md +163 -0
  233. package/workflows/product/prod.spec.clarify.md +178 -0
  234. package/workflows/product/prod.spec.epic.md +154 -0
  235. package/workflows/product/prod.spec.frd.md +96 -0
  236. package/workflows/product/prod.spec.issue.md +145 -0
  237. package/workflows/product/prod.spec.md +60 -0
  238. package/workflows/product/prod.spec.prd.md +100 -0
  239. package/workflows/taxonomy.md +92 -0
  240. package/workflows/warm-up.md +574 -0
@@ -0,0 +1,551 @@
1
+ import {
2
+ existsSync,
3
+ mkdirSync,
4
+ cpSync,
5
+ readdirSync,
6
+ renameSync,
7
+ readFileSync,
8
+ writeFileSync,
9
+ rmSync,
10
+ } from "node:fs";
11
+ import { execSync } from "node:child_process";
12
+ import { join, relative, basename } from "node:path";
13
+ import { getFrameworkRoot } from "../utils/paths.js";
14
+ import { getIDEFolder } from "../config/ide-config.js";
15
+ import {
16
+ SYNC_DIRS,
17
+ SYNC_ROOT_FILES,
18
+ LOCK_FILE,
19
+ OPENCODE_MODEL_MAP,
20
+ OPENCODE_DEFAULT_PERMISSIONS,
21
+ OPENCODE_STRIP_FIELDS,
22
+ KIRO_STRIP_FIELDS,
23
+ KIRO_HUB_FILE_PATTERNS,
24
+ } from "../config/constants.js";
25
+
26
+ /**
27
+ * Extrai name e description do frontmatter YAML de um SKILL.md
28
+ * @param {string} content - Conteúdo do SKILL.md
29
+ * @returns {{ name: string, description: string } | null}
30
+ */
31
+ function parseSkillFrontmatter(content) {
32
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
33
+ if (!match) return null;
34
+ const fm = match[1];
35
+ const name = (fm.match(/^name:\s*(.+)$/m) || [])[1]?.trim();
36
+ // description pode ser multiline com `>`
37
+ const descMatch = fm.match(/^description:\s*[>|]?\n?([\s\S]*?)(?=\n\S|\n---|\Z)/m);
38
+ const description = descMatch
39
+ ? descMatch[1].replace(/^\s+/gm, "").replace(/\n+/g, " ").trim()
40
+ : "";
41
+ return name ? { name, description } : null;
42
+ }
43
+
44
+ /**
45
+ * Gera openai.yaml para cada skill sincronizado ao Codex.
46
+ * O Codex CLI usa este arquivo para integrar skills no painel TUI.
47
+ * @param {string} skillsDir - Caminho para .codex/skills/
48
+ */
49
+ function generateCodexOpenAIYaml(skillsDir) {
50
+ if (!existsSync(skillsDir)) return;
51
+ for (const entry of readdirSync(skillsDir, { withFileTypes: true })) {
52
+ if (!entry.isDirectory()) continue;
53
+ const skillDir = join(skillsDir, entry.name);
54
+ const skillMdPath = join(skillDir, "SKILL.md");
55
+ const yamlPath = join(skillDir, "openai.yaml");
56
+ if (!existsSync(skillMdPath) || existsSync(yamlPath)) continue;
57
+ const content = readFileSync(skillMdPath, "utf-8");
58
+ const parsed = parseSkillFrontmatter(content);
59
+ if (!parsed) continue;
60
+ const yaml =
61
+ `name: ${parsed.name}\n` +
62
+ `description: "${parsed.description.replace(/"/g, '\\"')}"\n`;
63
+ writeFileSync(yamlPath, yaml, "utf-8");
64
+ }
65
+ }
66
+
67
+ export function syncAssets(targetDir, ide, opts = {}) {
68
+ const frameworkRoot = getFrameworkRoot();
69
+ const ideDir = join(targetDir, `.${getIDEFolder(ide)}`);
70
+ const result = { copied: [], skipped: [], errors: [] };
71
+
72
+ if (!opts.dryRun) {
73
+ mkdirSync(ideDir, { recursive: true });
74
+ }
75
+
76
+ for (const dir of SYNC_DIRS) {
77
+ const src = join(frameworkRoot, dir);
78
+ // Se for "workflows" e a IDE usa outro nome, usa o nome configurado
79
+ const destDirName = (dir === "workflows" && opts.workflowsFolder)
80
+ ? opts.workflowsFolder
81
+ : dir;
82
+ const dest = join(ideDir, destDirName);
83
+
84
+ if (!existsSync(src)) {
85
+ result.skipped.push(`${dir}/ (not found in framework)`);
86
+ continue;
87
+ }
88
+
89
+ try {
90
+ if (!opts.dryRun) {
91
+ cpSync(src, dest, { recursive: true, force: opts.force ?? true });
92
+ }
93
+ result.copied.push(`${destDirName}/`);
94
+ } catch (err) {
95
+ result.errors.push(`${destDirName}/: ${err.message}`);
96
+ }
97
+ }
98
+
99
+ // Copy root files to IDE folder
100
+ for (const file of SYNC_ROOT_FILES) {
101
+ const src = join(frameworkRoot, file);
102
+ const dest = join(ideDir, file);
103
+
104
+ if (!existsSync(src)) continue;
105
+
106
+ if (existsSync(dest) && !opts.force) {
107
+ result.skipped.push(`${file} (already exists)`);
108
+ continue;
109
+ }
110
+
111
+ try {
112
+ if (!opts.dryRun) {
113
+ cpSync(src, dest);
114
+ }
115
+ result.copied.push(file);
116
+ } catch (err) {
117
+ result.errors.push(`${file}: ${err.message}`);
118
+ }
119
+ }
120
+
121
+ // Flatten workflows subfolders — todas as IDEs requerem arquivos na raiz sem subpastas
122
+ const workflowsDest = join(ideDir, opts.workflowsFolder || "workflows");
123
+ if (existsSync(workflowsDest)) {
124
+ const moved = flattenDir(workflowsDest);
125
+ if (moved > 0) result.copied.push(`(flatten) ${moved} workflows movidos para raiz`);
126
+ }
127
+
128
+ // Cleanup leftover metrics skill copies from older installs
129
+ const legacyMetricsDir = join(ideDir, "metrics");
130
+ if (existsSync(legacyMetricsDir) && !opts.dryRun) {
131
+ try {
132
+ rmSync(legacyMetricsDir, { recursive: true, force: true });
133
+ result.copied.push(`(cleanup) .${ide}/metrics/ removed`);
134
+ } catch {}
135
+ }
136
+
137
+ if (!opts.dryRun) {
138
+ writeLockFile(targetDir, ideDir, ide, frameworkRoot);
139
+ }
140
+
141
+ // RTK: detectar e reconciliar RTK_ENABLED + rtk-rules.md
142
+ if (!opts.dryRun) {
143
+ const rtkResult = reconcileRtk(ideDir, targetDir, ide);
144
+ if (rtkResult) result.copied.push(rtkResult);
145
+ }
146
+
147
+ // Codex: gerar openai.yaml por skill para integração no painel TUI
148
+ if (ide === "codex" && !opts.dryRun) {
149
+ const codexSkillsDir = join(ideDir, "skills");
150
+ generateCodexOpenAIYaml(codexSkillsDir);
151
+ result.copied.push("(codex) openai.yaml gerado por skill em skills/*/");
152
+ }
153
+
154
+ // Kiro: gerar steering files (workflows já estão em steering/, processar rules e skills)
155
+ if (ide === "kiro" && !opts.dryRun) {
156
+ const kiroDir = ideDir;
157
+ transformKiroAssets(kiroDir, targetDir);
158
+ result.copied.push("(kiro) steering files gerados para workflows, rules e skills");
159
+
160
+ generateKiroMcpConfig(targetDir, kiroDir);
161
+ result.copied.push("(kiro) .kiro/settings/mcp.json verificado");
162
+ }
163
+
164
+ // OpenCode: transformar frontmatter de agents e commands + gerar opencode.json
165
+ if (ide === "opencode" && !opts.dryRun) {
166
+ const ocAgentsDir = join(ideDir, "agents");
167
+ transformOpenCodeAgents(ocAgentsDir);
168
+ result.copied.push("(opencode) agents transformados para formato OpenCode");
169
+
170
+ const ocCommandsDir = join(ideDir, opts.workflowsFolder || "commands");
171
+ transformOpenCodeCommands(ocCommandsDir);
172
+ result.copied.push("(opencode) commands transformados para formato OpenCode");
173
+
174
+ generateOpenCodeConfig(targetDir);
175
+ result.copied.push("(opencode) opencode.json gerado na raiz");
176
+ }
177
+
178
+ return result;
179
+ }
180
+
181
+ /**
182
+ * Detecta RTK no PATH e reconcilia RTK_ENABLED no ENV.md + rtk-rules.md na IDE.
183
+ * - RTK encontrado → RTK_ENABLED=true no ENV.md, mantém rule
184
+ * - RTK não encontrado → remove rtk-rules.md da IDE, mantém RTK_ENABLED=false
185
+ * @returns {string|null} mensagem de log ou null se nenhuma ação
186
+ */
187
+ function reconcileRtk(ideDir, targetDir, ide) {
188
+ const rtkRulePath = join(ideDir, "rules", "rtk-rules.md");
189
+ const envPath = join(ideDir, "ENV.md");
190
+ const hasRtk = (() => {
191
+ try {
192
+ execSync("which rtk 2>/dev/null", { stdio: "pipe" });
193
+ return true;
194
+ } catch {
195
+ return false;
196
+ }
197
+ })();
198
+
199
+ if (hasRtk) {
200
+ // Ativar RTK_ENABLED no ENV.md se existir e estiver false
201
+ if (existsSync(envPath)) {
202
+ let env = readFileSync(envPath, "utf-8");
203
+ if (env.includes("RTK_ENABLED=false")) {
204
+ env = env.replace("RTK_ENABLED=false", "RTK_ENABLED=true");
205
+ writeFileSync(envPath, env, "utf-8");
206
+ return "(rtk) detectado — RTK_ENABLED=true no ENV.md";
207
+ }
208
+ if (!env.includes("RTK_ENABLED")) {
209
+ env = env.trimEnd() + "\n\n# --- RTK — Token Killer ---\nRTK_ENABLED=true\n";
210
+ writeFileSync(envPath, env, "utf-8");
211
+ return "(rtk) detectado — RTK_ENABLED=true adicionado ao ENV.md";
212
+ }
213
+ }
214
+ return null; // já estava true
215
+ }
216
+
217
+ // RTK não encontrado — remover rule se foi copiada
218
+ if (existsSync(rtkRulePath)) {
219
+ rmSync(rtkRulePath);
220
+ return "(rtk) não detectado — rtk-rules.md removida de rules/";
221
+ }
222
+ return null;
223
+ }
224
+
225
+ /**
226
+ * Resolve o model Jarvis para formato OpenCode (provider/model-id).
227
+ * @param {string} model - Model no formato Jarvis (ex: "opus", "claude-sonnet-4-20250514")
228
+ * @returns {string} Model no formato OpenCode (ex: "anthropic/claude-opus-4-6")
229
+ */
230
+ function resolveOpenCodeModel(model) {
231
+ if (!model) return "anthropic/claude-sonnet-4-6";
232
+ const trimmed = model.trim();
233
+ if (trimmed.includes("/")) return trimmed; // já no formato provider/model
234
+ if (OPENCODE_MODEL_MAP[trimmed]) return OPENCODE_MODEL_MAP[trimmed];
235
+ // Inferir provider pelo prefixo do modelo
236
+ if (trimmed.startsWith("claude")) return `anthropic/${trimmed}`;
237
+ if (trimmed.startsWith("gpt") || trimmed.startsWith("o3") || trimmed.startsWith("o4")) return `openai/${trimmed}`;
238
+ if (trimmed.startsWith("gemini")) return `google/${trimmed}`;
239
+ // Fallback: retornar sem provider — OpenCode resolverá pelo provider padrão
240
+ return trimmed;
241
+ }
242
+
243
+ /**
244
+ * Transforma frontmatter de agents Jarvis para formato OpenCode.
245
+ * Reescreve os .md já copiados em .opencode/agents/.
246
+ * @param {string} agentsDir - Caminho para .opencode/agents/
247
+ */
248
+ function transformOpenCodeAgents(agentsDir) {
249
+ if (!existsSync(agentsDir)) return;
250
+ const files = [];
251
+ collectMdFiles(agentsDir, files);
252
+
253
+ for (const filePath of files) {
254
+ const content = readFileSync(filePath, "utf-8");
255
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
256
+ if (!fmMatch) continue;
257
+
258
+ const fm = fmMatch[1];
259
+ const body = content.slice(fmMatch[0].length).trim();
260
+
261
+ // Extract fields from Jarvis frontmatter
262
+ const description = (fm.match(/^description:\s*(.+)$/m) || [])[1]?.trim() || "";
263
+ const model = (fm.match(/^model:\s*(.+)$/m) || [])[1]?.trim();
264
+ const name = basename(filePath, ".md");
265
+ const isMainAgent = name === "eng.agent" || name === "eng-agent";
266
+ const mode = isMainAgent ? "primary" : "subagent";
267
+
268
+ // Build OpenCode frontmatter
269
+ const lines = [`description: "${description.replace(/"/g, '\\"')}"`];
270
+ lines.push(`mode: ${mode}`);
271
+ lines.push(`model: ${resolveOpenCodeModel(model)}`);
272
+ lines.push("permission:");
273
+ for (const [key, val] of Object.entries(OPENCODE_DEFAULT_PERMISSIONS)) {
274
+ lines.push(` ${key}: ${val}`);
275
+ }
276
+
277
+ const newContent = `---\n${lines.join("\n")}\n---\n\n${body}\n`;
278
+ writeFileSync(filePath, newContent, "utf-8");
279
+ }
280
+ }
281
+
282
+ /**
283
+ * Transforma frontmatter de commands (workflows) Jarvis para formato OpenCode.
284
+ * Remove campos Jarvis-only, converte model e agent path para nome.
285
+ * @param {string} commandsDir - Caminho para .opencode/commands/
286
+ */
287
+ function transformOpenCodeCommands(commandsDir) {
288
+ if (!existsSync(commandsDir)) return;
289
+ const files = [];
290
+ collectMdFiles(commandsDir, files);
291
+
292
+ for (const filePath of files) {
293
+ const content = readFileSync(filePath, "utf-8");
294
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
295
+ if (!fmMatch) continue;
296
+
297
+ const fm = fmMatch[1];
298
+ const body = content.slice(fmMatch[0].length).trim();
299
+
300
+ // Extract fields
301
+ const description = (fm.match(/^description:\s*(.+)$/m) || [])[1]?.trim() || "";
302
+ const model =
303
+ (fm.match(/^recommended_model:\s*(.+)$/m) || [])[1]?.trim() ||
304
+ (fm.match(/^model:\s*(.+)$/m) || [])[1]?.trim();
305
+
306
+ // Convert agent path to agent name: $IDE/agents/engineering/eng.agent.md → eng.agent
307
+ const agentMatch = fm.match(/^agent:\s*["']?(.+?)["']?\s*$/m);
308
+ let agentName = "";
309
+ if (agentMatch) {
310
+ const agentPath = agentMatch[1].trim();
311
+ agentName = basename(agentPath, ".md");
312
+ }
313
+
314
+ // Build OpenCode command frontmatter (only supported fields)
315
+ const lines = [`description: "${description.replace(/"/g, '\\"')}"`];
316
+ if (agentName) lines.push(`agent: ${agentName}`);
317
+ if (model) lines.push(`model: ${resolveOpenCodeModel(model)}`);
318
+
319
+ const newContent = `---\n${lines.join("\n")}\n---\n\n${body}\n`;
320
+ writeFileSync(filePath, newContent, "utf-8");
321
+ }
322
+ }
323
+
324
+ /**
325
+ * Gera opencode.json na raiz do projeto se não existir.
326
+ * @param {string} targetDir - Raiz do projeto alvo
327
+ */
328
+ function generateOpenCodeConfig(targetDir) {
329
+ const configPath = join(targetDir, "opencode.json");
330
+ if (existsSync(configPath)) return; // não sobrescrever config do usuário
331
+
332
+ const config = {
333
+ $schema: "https://opencode.ai/config.json",
334
+ model: "anthropic/claude-sonnet-4-6",
335
+ instructions: [".opencode/AGENTS.md", ".opencode/rules/**/*.md"],
336
+ };
337
+
338
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
339
+ }
340
+
341
+ /**
342
+ * Coleta recursivamente todos os .md de um diretório.
343
+ * @param {string} dir
344
+ * @param {string[]} acc
345
+ */
346
+ function collectMdFiles(dir, acc) {
347
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
348
+ const full = join(dir, entry.name);
349
+ if (entry.isDirectory()) collectMdFiles(full, acc);
350
+ else if (entry.isFile() && entry.name.endsWith(".md")) acc.push(full);
351
+ }
352
+ }
353
+
354
+ function flattenDir(dir, rootDir) {
355
+ const root = rootDir ?? dir;
356
+ let moved = 0;
357
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
358
+ const fullPath = join(dir, entry.name);
359
+ if (entry.isDirectory()) {
360
+ moved += flattenDir(fullPath, root);
361
+ try { rmSync(fullPath, { recursive: true, force: true }); } catch {}
362
+ } else if (entry.isFile() && entry.name.endsWith(".md")) {
363
+ const dest = join(root, basename(entry.name));
364
+ if (fullPath !== dest) {
365
+ try { renameSync(fullPath, dest); moved++; } catch {}
366
+ }
367
+ }
368
+ }
369
+ return moved;
370
+ }
371
+
372
+ /**
373
+ * Converte o frontmatter de um arquivo .md para um steering file Kiro.
374
+ * - Remove campos Jarvis-only (KIRO_STRIP_FIELDS)
375
+ * - Adiciona `inclusion` se não presente
376
+ * - Preserva `name` e `description`
377
+ * @param {string} content - Conteúdo original do .md
378
+ * @param {string} inclusion - Valor de inclusion: "auto" | "manual" | "fileMatch"
379
+ * @param {string} [fileMatchPattern] - Pattern para inclusion: fileMatch
380
+ * @returns {string} Conteúdo transformado
381
+ */
382
+ function toKiroSteeringFile(content, inclusion, fileMatchPattern) {
383
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
384
+ const body = fmMatch ? content.slice(fmMatch[0].length).trimStart() : content;
385
+
386
+ const lines = [`inclusion: ${inclusion}`];
387
+ if (inclusion === "fileMatch" && fileMatchPattern) {
388
+ lines.push(`fileMatchPattern: "${fileMatchPattern}"`);
389
+ }
390
+
391
+ if (fmMatch) {
392
+ const fm = fmMatch[1];
393
+ const description = (fm.match(/^description:\s*[>|]?\s*\n?([\s\S]*?)(?=\n\S|\n---|\Z)/m) || [])[1];
394
+ const descInline = (fm.match(/^description:\s*(.+)$/m) || [])[1];
395
+ const finalDesc = (description || descInline || "").replace(/^\s+/gm, "").replace(/\n+/g, " ").trim();
396
+ if (finalDesc) lines.push(`description: "${finalDesc.replace(/"/g, '\\"')}"`);
397
+ }
398
+
399
+ return `---\n${lines.join("\n")}\n---\n\n${body}`;
400
+ }
401
+
402
+ /**
403
+ * Detecta o HUB de uma rule a partir do bloco "Applies to" no corpo.
404
+ * Retorna o HUB em maiúsculas ou null se não encontrado.
405
+ * @param {string} content
406
+ * @returns {string | null}
407
+ */
408
+ function detectRuleHub(content) {
409
+ const match = content.match(/\*\*Applies to:\*\*[^\n]*HUB:\s*([A-Z-]+)/i);
410
+ if (!match) return null;
411
+ const hub = match[1].trim().toUpperCase();
412
+ return hub === "ALL" ? null : hub;
413
+ }
414
+
415
+ /**
416
+ * Transforma os assets copiados para .kiro/ em steering files.
417
+ * 1. Workflows em steering/ → adiciona inclusion: manual
418
+ * 2. Rules em rules/ → copia para steering/ com inclusion: auto ou fileMatch
419
+ * 3. Skills em skills/*\/SKILL.md → copia para steering/skills/skill-{nome}.md
420
+ * @param {string} kiroDir - Caminho para .kiro/
421
+ * @param {string} targetDir - Raiz do projeto (para buscar MCPs.md)
422
+ */
423
+ function transformKiroAssets(kiroDir, targetDir) {
424
+ const steeringDir = join(kiroDir, "steering");
425
+ mkdirSync(steeringDir, { recursive: true });
426
+
427
+ // 1. Workflows — já estão em steering/ (copiados com workflowsFolder="steering")
428
+ // Adicionar `inclusion: manual` no frontmatter de cada arquivo
429
+ if (existsSync(steeringDir)) {
430
+ for (const entry of readdirSync(steeringDir, { withFileTypes: true })) {
431
+ if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
432
+ const filePath = join(steeringDir, entry.name);
433
+ const content = readFileSync(filePath, "utf-8");
434
+ const transformed = toKiroSteeringFile(content, "manual");
435
+ writeFileSync(filePath, transformed, "utf-8");
436
+ }
437
+ }
438
+
439
+ // 2. Rules — copiar de rules/ para steering/ com inclusion adequada
440
+ const rulesDir = join(kiroDir, "rules");
441
+ if (existsSync(rulesDir)) {
442
+ const ruleFiles = [];
443
+ collectMdFiles(rulesDir, ruleFiles);
444
+
445
+ for (const filePath of ruleFiles) {
446
+ const name = basename(filePath);
447
+ // Ignorar arquivos de índice/meta
448
+ if (name === "AGENTS.md" || name === "README.md") continue;
449
+
450
+ const content = readFileSync(filePath, "utf-8");
451
+ const hub = detectRuleHub(content);
452
+ const pattern = hub ? KIRO_HUB_FILE_PATTERNS[hub] : null;
453
+ const inclusion = pattern ? "fileMatch" : "auto";
454
+ const transformed = toKiroSteeringFile(content, inclusion, pattern);
455
+
456
+ writeFileSync(join(steeringDir, name), transformed, "utf-8");
457
+ }
458
+ }
459
+
460
+ // 3. Skills — copiar SKILL.md de cada skill para steering/skills/skill-{nome}.md
461
+ const skillsDir = join(kiroDir, "skills");
462
+ const steeringSkillsDir = join(steeringDir, "skills");
463
+ if (existsSync(skillsDir)) {
464
+ mkdirSync(steeringSkillsDir, { recursive: true });
465
+
466
+ for (const entry of readdirSync(skillsDir, { withFileTypes: true })) {
467
+ if (!entry.isDirectory()) continue;
468
+ const skillName = entry.name;
469
+ const skillMdPath = join(skillsDir, skillName, "SKILL.md");
470
+ if (!existsSync(skillMdPath)) continue;
471
+
472
+ const content = readFileSync(skillMdPath, "utf-8");
473
+ const transformed = toKiroSteeringFile(content, "manual");
474
+ writeFileSync(join(steeringSkillsDir, `skill-${skillName}.md`), transformed, "utf-8");
475
+ }
476
+ }
477
+ }
478
+
479
+ /**
480
+ * Gera .kiro/settings/mcp.json a partir de MCPs.md do projeto (se existir).
481
+ * Busca em $IDE/MCPs.md (qualquer IDE detectada no targetDir).
482
+ * Não sobrescreve se o arquivo já existir.
483
+ * @param {string} targetDir - Raiz do projeto alvo
484
+ * @param {string} kiroDir - Caminho para .kiro/
485
+ */
486
+ function generateKiroMcpConfig(targetDir, kiroDir) {
487
+ const settingsDir = join(kiroDir, "settings");
488
+ const mcpJsonPath = join(settingsDir, "mcp.json");
489
+ if (existsSync(mcpJsonPath)) return;
490
+
491
+ // Buscar MCPs.md em qualquer pasta de IDE do projeto
492
+ const ideFolders = ["windsurf", "claude", "cursor", "codex", "opencode", "agents"];
493
+ let mcpsContent = null;
494
+ for (const folder of ideFolders) {
495
+ const candidate = join(targetDir, `.${folder}`, "MCPs.md");
496
+ if (existsSync(candidate)) {
497
+ mcpsContent = readFileSync(candidate, "utf-8");
498
+ break;
499
+ }
500
+ }
501
+
502
+ mkdirSync(settingsDir, { recursive: true });
503
+
504
+ if (!mcpsContent) {
505
+ // Gerar estrutura base vazia para o usuário preencher
506
+ const base = { mcpServers: {} };
507
+ writeFileSync(mcpJsonPath, JSON.stringify(base, null, 2) + "\n", "utf-8");
508
+ return;
509
+ }
510
+
511
+ // Extrair blocos de código JSON do MCPs.md (```json ... ```)
512
+ const blocks = [];
513
+ for (const m of mcpsContent.matchAll(/```json\n([\s\S]*?)```/g)) {
514
+ try {
515
+ const parsed = JSON.parse(m[1].trim());
516
+ if (parsed && typeof parsed === "object") blocks.push(parsed);
517
+ } catch {
518
+ // ignorar blocos inválidos
519
+ }
520
+ }
521
+
522
+ const mcpServers = {};
523
+ for (const block of blocks) {
524
+ if (block.mcpServers) Object.assign(mcpServers, block.mcpServers);
525
+ else if (block.name && block.command) mcpServers[block.name] = block;
526
+ }
527
+
528
+ writeFileSync(mcpJsonPath, JSON.stringify({ mcpServers }, null, 2) + "\n", "utf-8");
529
+ }
530
+
531
+ function writeLockFile(targetDir, ideDir, ide, frameworkRoot) {
532
+ const pkgPath = join(frameworkRoot, "package.json");
533
+ const pkg = existsSync(pkgPath)
534
+ ? JSON.parse(readFileSync(pkgPath, "utf-8"))
535
+ : { version: "0.0.0" };
536
+
537
+ const lock = {
538
+ version: pkg.version,
539
+ ide,
540
+ syncedAt: new Date().toISOString(),
541
+ frameworkRoot: relative(targetDir, frameworkRoot) || ".",
542
+ };
543
+
544
+ const lockContent = JSON.stringify(lock, null, 2) + "\n";
545
+
546
+ // Write lock file to project root (for postinstall.js)
547
+ writeFileSync(join(targetDir, LOCK_FILE), lockContent);
548
+
549
+ // Also write lock file to IDE folder
550
+ writeFileSync(join(ideDir, LOCK_FILE), lockContent);
551
+ }
@@ -0,0 +1,41 @@
1
+ #!/bin/bash
2
+ # Busca arquivo do central-docs (GitLab / GitHub / Bitbucket) + cache Redis
3
+
4
+ FILE_PATH="$1"
5
+ REF="${2:-main}"
6
+
7
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
+
9
+ if [ -z "$FILE_PATH" ]; then
10
+ echo "Uso: fetch-file.sh <file-path> [ref]" >&2
11
+ exit 1
12
+ fi
13
+
14
+ if [ -z "$CENTRAL_DOCS_REPO" ]; then
15
+ echo "Erro: CENTRAL_DOCS_REPO não definido no ENV.md" >&2
16
+ exit 1
17
+ fi
18
+
19
+ CACHE_KEY="docs:${FILE_PATH}:${REF}"
20
+ CACHED=$(redis-cli GET "$CACHE_KEY" 2>/dev/null || echo "")
21
+
22
+ if [ -n "$CACHED" ]; then
23
+ echo "$CACHED"
24
+ exit 0
25
+ fi
26
+
27
+ ERR_FILE=$(mktemp)
28
+ RESPONSE=$(node "${SCRIPT_DIR}/../vcs/fetch-raw.js" "$FILE_PATH" "$REF" 2>"$ERR_FILE")
29
+ EXIT_CODE=$?
30
+
31
+ if [ $EXIT_CODE -eq 0 ]; then
32
+ TTL="${CENTRAL_DOCS_CACHE_TTL:-3600}"
33
+ echo "$RESPONSE" | redis-cli -x SETEX "$CACHE_KEY" "$TTL" >/dev/null 2>&1 || true
34
+ rm -f "$ERR_FILE"
35
+ echo "$RESPONSE"
36
+ exit 0
37
+ fi
38
+
39
+ cat "$ERR_FILE" >&2
40
+ rm -f "$ERR_FILE"
41
+ exit $EXIT_CODE