@julioventura/opensquad 0.1.17

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 (247) hide show
  1. package/README.md +433 -0
  2. package/_opensquad/config/playwright.config.json +11 -0
  3. package/_opensquad/core/architect.agent.yaml +112 -0
  4. package/_opensquad/core/best-practices/_catalog.yaml +126 -0
  5. package/_opensquad/core/best-practices/blog-post.md +132 -0
  6. package/_opensquad/core/best-practices/blog-seo.md +127 -0
  7. package/_opensquad/core/best-practices/brand-resolution-checklist.md +172 -0
  8. package/_opensquad/core/best-practices/copywriting.md +441 -0
  9. package/_opensquad/core/best-practices/data-analysis.md +401 -0
  10. package/_opensquad/core/best-practices/email-newsletter.md +118 -0
  11. package/_opensquad/core/best-practices/email-sales.md +110 -0
  12. package/_opensquad/core/best-practices/image-design.md +348 -0
  13. package/_opensquad/core/best-practices/instagram-feed.md +235 -0
  14. package/_opensquad/core/best-practices/instagram-reels.md +112 -0
  15. package/_opensquad/core/best-practices/instagram-stories.md +107 -0
  16. package/_opensquad/core/best-practices/linkedin-article.md +116 -0
  17. package/_opensquad/core/best-practices/linkedin-post.md +121 -0
  18. package/_opensquad/core/best-practices/researching.md +349 -0
  19. package/_opensquad/core/best-practices/review.md +269 -0
  20. package/_opensquad/core/best-practices/run-recovery.md +61 -0
  21. package/_opensquad/core/best-practices/social-networks-publishing.md +327 -0
  22. package/_opensquad/core/best-practices/squad-creation-checklist.md +32 -0
  23. package/_opensquad/core/best-practices/strategist.md +344 -0
  24. package/_opensquad/core/best-practices/technical-writing.md +365 -0
  25. package/_opensquad/core/best-practices/twitter-post.md +105 -0
  26. package/_opensquad/core/best-practices/twitter-thread.md +122 -0
  27. package/_opensquad/core/best-practices/whatsapp-broadcast.md +107 -0
  28. package/_opensquad/core/best-practices/youtube-script.md +122 -0
  29. package/_opensquad/core/best-practices/youtube-shorts.md +112 -0
  30. package/_opensquad/core/defaults/youtube-video-assembly.json +84 -0
  31. package/_opensquad/core/prompts/build.prompt.md +613 -0
  32. package/_opensquad/core/prompts/design.prompt.md +606 -0
  33. package/_opensquad/core/prompts/discovery.prompt.md +377 -0
  34. package/_opensquad/core/prompts/sherlock-instagram.md +123 -0
  35. package/_opensquad/core/prompts/sherlock-linkedin.md +73 -0
  36. package/_opensquad/core/prompts/sherlock-shared.md +684 -0
  37. package/_opensquad/core/prompts/sherlock-twitter.md +78 -0
  38. package/_opensquad/core/prompts/sherlock-youtube.md +85 -0
  39. package/_opensquad/core/runner.pipeline.md +743 -0
  40. package/_opensquad/core/skills.engine.md +384 -0
  41. package/bin/opensquad.js +108 -0
  42. package/dashboard/index.html +15 -0
  43. package/dashboard/package-lock.json +1964 -0
  44. package/dashboard/package.json +28 -0
  45. package/dashboard/public/assets/avatars/Female1_1wave.png +0 -0
  46. package/dashboard/public/assets/avatars/Female1_2wave.png +0 -0
  47. package/dashboard/public/assets/avatars/Female1_blink.png +0 -0
  48. package/dashboard/public/assets/avatars/Female1_talk.png +0 -0
  49. package/dashboard/public/assets/avatars/Female2_1wave.png +0 -0
  50. package/dashboard/public/assets/avatars/Female2_2wave.png +0 -0
  51. package/dashboard/public/assets/avatars/Female2_blink.png +0 -0
  52. package/dashboard/public/assets/avatars/Female2_talk.png +0 -0
  53. package/dashboard/public/assets/avatars/Female3_blink.png +0 -0
  54. package/dashboard/public/assets/avatars/Female3_talk.png +0 -0
  55. package/dashboard/public/assets/avatars/Female3_wave.png +0 -0
  56. package/dashboard/public/assets/avatars/Female4_blink.png +0 -0
  57. package/dashboard/public/assets/avatars/Female4_talk.png +0 -0
  58. package/dashboard/public/assets/avatars/Female4_wave.png +0 -0
  59. package/dashboard/public/assets/avatars/Female5_blink.png +0 -0
  60. package/dashboard/public/assets/avatars/Female5_talk.png +0 -0
  61. package/dashboard/public/assets/avatars/Female5_wave.png +0 -0
  62. package/dashboard/public/assets/avatars/Female6_blink.png +0 -0
  63. package/dashboard/public/assets/avatars/Female6_talk.png +0 -0
  64. package/dashboard/public/assets/avatars/Female6_wave.png +0 -0
  65. package/dashboard/public/assets/avatars/Male1_1wave.png +0 -0
  66. package/dashboard/public/assets/avatars/Male1_2wave.png +0 -0
  67. package/dashboard/public/assets/avatars/Male1_blink.png +0 -0
  68. package/dashboard/public/assets/avatars/Male1_talk.png +0 -0
  69. package/dashboard/public/assets/avatars/Male2_1wave.png +0 -0
  70. package/dashboard/public/assets/avatars/Male2_2wave.png +0 -0
  71. package/dashboard/public/assets/avatars/Male2_blink.png +0 -0
  72. package/dashboard/public/assets/avatars/Male2_talk.png +0 -0
  73. package/dashboard/public/assets/avatars/Male3_blink.png +0 -0
  74. package/dashboard/public/assets/avatars/Male3_talk.png +0 -0
  75. package/dashboard/public/assets/avatars/Male3_wave.png +0 -0
  76. package/dashboard/public/assets/avatars/Male4_blink.png +0 -0
  77. package/dashboard/public/assets/avatars/Male4_talk.png +0 -0
  78. package/dashboard/public/assets/avatars/Male4_wave.png +0 -0
  79. package/dashboard/public/assets/desks/desktop_set_black_down.png +0 -0
  80. package/dashboard/public/assets/desks/desktop_set_black_down_coding-1.png +0 -0
  81. package/dashboard/public/assets/desks/desktop_set_black_down_coding.png +0 -0
  82. package/dashboard/public/assets/desks/desktop_set_black_up.png +0 -0
  83. package/dashboard/public/assets/desks/desktop_set_white_down.png +0 -0
  84. package/dashboard/public/assets/desks/desktop_set_white_down_coding-1.png +0 -0
  85. package/dashboard/public/assets/desks/desktop_set_white_down_coding.png +0 -0
  86. package/dashboard/public/assets/desks/desktop_set_white_up.png +0 -0
  87. package/dashboard/public/assets/furniture/armchair_tan.png +0 -0
  88. package/dashboard/public/assets/furniture/armchair_tan_down.png +0 -0
  89. package/dashboard/public/assets/furniture/backpack_blue.png +0 -0
  90. package/dashboard/public/assets/furniture/backpack_red.png +0 -0
  91. package/dashboard/public/assets/furniture/blinds.png +0 -0
  92. package/dashboard/public/assets/furniture/blinds_large_closed_white.png +0 -0
  93. package/dashboard/public/assets/furniture/bookshelf.png +0 -0
  94. package/dashboard/public/assets/furniture/bookshelf_purple_tall.png +0 -0
  95. package/dashboard/public/assets/furniture/bulletin_board.png +0 -0
  96. package/dashboard/public/assets/furniture/clock.png +0 -0
  97. package/dashboard/public/assets/furniture/coffee_mug.png +0 -0
  98. package/dashboard/public/assets/furniture/coffee_mug_blue.png +0 -0
  99. package/dashboard/public/assets/furniture/coffee_table.png +0 -0
  100. package/dashboard/public/assets/furniture/coffeepot_right.png +0 -0
  101. package/dashboard/public/assets/furniture/coffeetable_black_horizontal.png +0 -0
  102. package/dashboard/public/assets/furniture/couch.png +0 -0
  103. package/dashboard/public/assets/furniture/couch_tan_down.png +0 -0
  104. package/dashboard/public/assets/furniture/cushion_blue.png +0 -0
  105. package/dashboard/public/assets/furniture/cushion_tan.png +0 -0
  106. package/dashboard/public/assets/furniture/desk_wood.png +0 -0
  107. package/dashboard/public/assets/furniture/fancy_rug.png +0 -0
  108. package/dashboard/public/assets/furniture/fancy_rug_wide.png +0 -0
  109. package/dashboard/public/assets/furniture/flowers1.png +0 -0
  110. package/dashboard/public/assets/furniture/flowers2.png +0 -0
  111. package/dashboard/public/assets/furniture/lamp_tan.png +0 -0
  112. package/dashboard/public/assets/furniture/lantern.png +0 -0
  113. package/dashboard/public/assets/furniture/monstera.png +0 -0
  114. package/dashboard/public/assets/furniture/monstera_small.png +0 -0
  115. package/dashboard/public/assets/furniture/picture_frame.png +0 -0
  116. package/dashboard/public/assets/furniture/plant1.png +0 -0
  117. package/dashboard/public/assets/furniture/plant2.png +0 -0
  118. package/dashboard/public/assets/furniture/plant3.png +0 -0
  119. package/dashboard/public/assets/furniture/plant_poof.png +0 -0
  120. package/dashboard/public/assets/furniture/plant_spindly.png +0 -0
  121. package/dashboard/public/assets/furniture/poster_blue.png +0 -0
  122. package/dashboard/public/assets/furniture/rug.png +0 -0
  123. package/dashboard/public/assets/furniture/succulent_blue.png +0 -0
  124. package/dashboard/public/assets/furniture/succulent_green.png +0 -0
  125. package/dashboard/public/assets/furniture/treasurechest_closed_gold.png +0 -0
  126. package/dashboard/public/assets/furniture/water_cooler_better.png +0 -0
  127. package/dashboard/public/assets/furniture/whiteboard.png +0 -0
  128. package/dashboard/public/assets/furniture/whiteboard_stand_graph.png +0 -0
  129. package/dashboard/public/assets/furniture/window_blinds_open.png +0 -0
  130. package/dashboard/src/App.tsx +46 -0
  131. package/dashboard/src/components/RunDashboardButton.tsx +92 -0
  132. package/dashboard/src/components/SquadCard.tsx +49 -0
  133. package/dashboard/src/components/SquadSelector.tsx +67 -0
  134. package/dashboard/src/components/StatusBadge.tsx +32 -0
  135. package/dashboard/src/components/StatusBar.tsx +116 -0
  136. package/dashboard/src/hooks/useSquadSocket.ts +135 -0
  137. package/dashboard/src/lib/formatTime.ts +16 -0
  138. package/dashboard/src/lib/normalizeState.ts +25 -0
  139. package/dashboard/src/main.tsx +10 -0
  140. package/dashboard/src/office/AgentSprite.ts +241 -0
  141. package/dashboard/src/office/OfficeScene.ts +153 -0
  142. package/dashboard/src/office/PhaserGame.tsx +80 -0
  143. package/dashboard/src/office/RoomBuilder.ts +190 -0
  144. package/dashboard/src/office/assetKeys.ts +150 -0
  145. package/dashboard/src/office/palette.ts +32 -0
  146. package/dashboard/src/plugin/squadWatcher.ts +397 -0
  147. package/dashboard/src/store/useSquadStore.ts +56 -0
  148. package/dashboard/src/styles/globals.css +36 -0
  149. package/dashboard/src/types/state.ts +63 -0
  150. package/dashboard/src/vite-env.d.ts +1 -0
  151. package/dashboard/tsconfig.json +24 -0
  152. package/dashboard/vite.config.ts +13 -0
  153. package/package.json +59 -0
  154. package/public/sfx/slide-transition-sfx.mp3 +0 -0
  155. package/skills/README.md +84 -0
  156. package/skills/apify/SKILL.md +55 -0
  157. package/skills/blotato/SKILL.md +63 -0
  158. package/skills/canva/SKILL.md +60 -0
  159. package/skills/higgsfield/SKILL.md +147 -0
  160. package/skills/image-ai-generator/SKILL.md +124 -0
  161. package/skills/image-ai-generator/scripts/generate.py +175 -0
  162. package/skills/image-creator/SKILL.md +166 -0
  163. package/skills/image-creator/editorial-slide-template.js +645 -0
  164. package/skills/image-fetcher/SKILL.md +91 -0
  165. package/skills/imgbb-uploader/SKILL.md +73 -0
  166. package/skills/imgbb-uploader/scripts/upload.js +125 -0
  167. package/skills/instagram-publisher/README.md +36 -0
  168. package/skills/instagram-publisher/SKILL.md +231 -0
  169. package/skills/instagram-publisher/scripts/publish-playwright.js +418 -0
  170. package/skills/instagram-publisher/scripts/publish.js +521 -0
  171. package/skills/opensquad-agent-creator/SKILL.md +192 -0
  172. package/skills/opensquad-skill-creator/SKILL.md +420 -0
  173. package/skills/opensquad-skill-creator/agents/analyzer.md +274 -0
  174. package/skills/opensquad-skill-creator/agents/comparator.md +202 -0
  175. package/skills/opensquad-skill-creator/agents/grader.md +223 -0
  176. package/skills/opensquad-skill-creator/assets/eval_review.html +146 -0
  177. package/skills/opensquad-skill-creator/eval-viewer/generate_review.py +471 -0
  178. package/skills/opensquad-skill-creator/eval-viewer/viewer.html +1325 -0
  179. package/skills/opensquad-skill-creator/references/schemas.md +430 -0
  180. package/skills/opensquad-skill-creator/references/skill-format.md +235 -0
  181. package/skills/opensquad-skill-creator/scripts/__init__.py +0 -0
  182. package/skills/opensquad-skill-creator/scripts/aggregate_benchmark.py +401 -0
  183. package/skills/opensquad-skill-creator/scripts/quick_validate.py +103 -0
  184. package/skills/opensquad-skill-creator/scripts/run_eval.py +310 -0
  185. package/skills/opensquad-skill-creator/scripts/utils.py +47 -0
  186. package/skills/pdf-extractor/SKILL.md +57 -0
  187. package/skills/pdf-extractor/scripts/extract.py +82 -0
  188. package/skills/resend/SKILL.md +80 -0
  189. package/skills/run-dashboard/README.md +93 -0
  190. package/skills/run-dashboard/SKILL.md +173 -0
  191. package/skills/run-dashboard/scripts/finalize-state.js +273 -0
  192. package/skills/run-dashboard/scripts/generate.js +1296 -0
  193. package/skills/run-dashboard/scripts/serve.js +135 -0
  194. package/skills/run-dashboard/templates/run-dashboard-simple.template.html +191 -0
  195. package/skills/run-dashboard/templates/run-dashboard.template.html +1164 -0
  196. package/skills/smtp-sender/SKILL.md +88 -0
  197. package/skills/smtp-sender/scripts/send.js +478 -0
  198. package/skills/template-designer/SKILL.md +201 -0
  199. package/skills/template-designer/base-templates/model-a.html +27 -0
  200. package/skills/template-designer/base-templates/model-b.html +31 -0
  201. package/skills/template-designer/base-templates/model-c.html +42 -0
  202. package/skills/youtube-publisher/SKILL.md +232 -0
  203. package/skills/youtube-publisher/scripts/publish.js +2078 -0
  204. package/src/agents-cli.js +158 -0
  205. package/src/agents.js +134 -0
  206. package/src/i18n.js +48 -0
  207. package/src/init.js +442 -0
  208. package/src/locales/en.json +79 -0
  209. package/src/locales/es.json +78 -0
  210. package/src/locales/pt-BR.json +78 -0
  211. package/src/logger.js +38 -0
  212. package/src/prompt.js +46 -0
  213. package/src/readme/README.md +146 -0
  214. package/src/runs.js +318 -0
  215. package/src/skills-cli.js +157 -0
  216. package/src/skills.js +146 -0
  217. package/src/supabase-cli.js +584 -0
  218. package/src/update.js +169 -0
  219. package/templates/_opensquad/.opensquad-version +1 -0
  220. package/templates/_opensquad/_investigations/.gitkeep +0 -0
  221. package/templates/ide-templates/antigravity/.agent/rules/opensquad.md +68 -0
  222. package/templates/ide-templates/antigravity/.agent/workflows/opensquad.md +102 -0
  223. package/templates/ide-templates/claude-code/.claude/skills/opensquad/SKILL.md +182 -0
  224. package/templates/ide-templates/claude-code/.mcp.json +8 -0
  225. package/templates/ide-templates/claude-code/CLAUDE.md +57 -0
  226. package/templates/ide-templates/codex/.agents/skills/opensquad/SKILL.md +6 -0
  227. package/templates/ide-templates/codex/AGENTS.md +120 -0
  228. package/templates/ide-templates/cursor/.cursor/commands/opensquad.md +9 -0
  229. package/templates/ide-templates/cursor/.cursor/mcp.json +8 -0
  230. package/templates/ide-templates/cursor/.cursor/rules/opensquad.mdc +62 -0
  231. package/templates/ide-templates/cursor/.cursorignore +3 -0
  232. package/templates/ide-templates/gemini-cli/.gemini/settings.json +8 -0
  233. package/templates/ide-templates/gemini-cli/.gemini/skills/opensquad/SKILL.md +186 -0
  234. package/templates/ide-templates/gemini-cli/GEMINI.md +57 -0
  235. package/templates/ide-templates/opencode/.opencode/commands/opensquad.md +9 -0
  236. package/templates/ide-templates/opencode/AGENTS.md +120 -0
  237. package/templates/ide-templates/qwen-code/.qwen/settings.json +8 -0
  238. package/templates/ide-templates/qwen-code/.qwen/skills/opensquad/SKILL.md +182 -0
  239. package/templates/ide-templates/qwen-code/QWEN.md +57 -0
  240. package/templates/ide-templates/trae/.trae/mcp.json +8 -0
  241. package/templates/ide-templates/trae/.trae/rules/opensquad.md +64 -0
  242. package/templates/ide-templates/vscode-copilot/.github/copilot-instructions.md +59 -0
  243. package/templates/ide-templates/vscode-copilot/.github/prompts/opensquad.prompt.md +209 -0
  244. package/templates/ide-templates/vscode-copilot/.vscode/mcp.json +8 -0
  245. package/templates/ide-templates/vscode-copilot/.vscode/settings.json +3 -0
  246. package/templates/package.json +8 -0
  247. package/templates/squads/.gitkeep +0 -0
@@ -0,0 +1,201 @@
1
+ ---
2
+ name: template-designer
3
+ description: Visual template selection for image design agents. Generates template variations, renders them as images for user review, and saves the approved visual identity.
4
+ type: prompt
5
+ version: "2.0.0"
6
+ categories: [design, visual, templates]
7
+ ---
8
+
9
+ # Template Designer
10
+
11
+ Visual template selection and refinement for squad creation and editing.
12
+
13
+ ## When to Use
14
+
15
+ - During squad creation: when the Design phase identifies an image design agent and the user opts to choose a template
16
+ - During squad editing: when the user asks to define, edit, or change the visual identity / template of a design agent
17
+ - Trigger: presence of `image-creator` skill (or similar image-producing skill) in the squad's skill list
18
+
19
+ ## Prerequisites
20
+
21
+ - A squad with a design agent that produces images (uses `image-creator` skill)
22
+ - Squad's `_build/` directory must exist (created during Discovery/Design phases)
23
+ - `image-creator` skill installed (for rendering HTML to JPG)
24
+
25
+ ## How It Works
26
+
27
+ 1. You read context and base templates
28
+ 2. You generate 3 adapted HTML template variations
29
+ 3. You render each as a JPG image using the `image-creator` skill
30
+ 4. You present the image file paths to the user for review
31
+ 5. You iterate with feedback until approval
32
+ 6. You save the approved template as HTML reference + structured style rules
33
+
34
+ ## Generating Templates
35
+
36
+ ### Step 0: Read Design Guidelines (MANDATORY)
37
+
38
+ Before generating any template, read and internalize the design best practices:
39
+ - `_opensquad/core/best-practices/image-design.md` — **REQUIRED reading**. Contains platform-specific minimum font sizes, typography rules, spacing guidelines, color palette constraints, contrast requirements, and layout methodology. Every template you generate MUST comply with these rules.
40
+
41
+ Key rules to always follow:
42
+ - **Font sizes**: Hero 58px, Heading 43px, Body 34px, Caption 24px minimum for Instagram carousel (1080x1440). Absolute minimum 20px for any readable text on any platform.
43
+ - **Font weight**: 500 or higher for body text and above.
44
+ - **Colors**: Maximum 5 colors per design system (primary, secondary, accent, background, text).
45
+ - **Contrast**: WCAG AA minimum 4.5:1 for all text against background.
46
+ - **Layout**: CSS Grid or Flexbox only. No absolute positioning for primary content.
47
+ - **Self-contained HTML**: Inline CSS only. Only Google Fonts @import allowed as external resource.
48
+ - **No slide counters**: Never include "1/7" or similar. Instagram has native navigation.
49
+
50
+ You should also apply general web design best practices: proper white space, visual hierarchy through scale and weight, consistent spacing rhythm, and balanced composition.
51
+
52
+ ### HARD RULES — Dimensions and Typography
53
+
54
+ These rules are NON-NEGOTIABLE. Every template must comply:
55
+
56
+ **Fixed Dimensions (never use height: auto or flexible height):**
57
+ - Instagram Carousel: `width: 1080px; height: 1440px` (3:4 portrait)
58
+ - Instagram Story/Reel: `width: 1080px; height: 1920px` (9:16 portrait)
59
+ - Instagram Post: `width: 1080px; height: 1080px` (1:1 square)
60
+ - LinkedIn Post: `width: 1200px; height: 627px` (1.91:1 horizontal)
61
+
62
+ The root container of every template MUST set explicit `width` and `height` in pixels. The template must render at exactly these dimensions — no overflow, no scrolling, no flexible height.
63
+
64
+ **Minimum Font Sizes (Instagram at 1080px width):**
65
+ - Hero/Title: **58px** minimum
66
+ - Heading: **43px** minimum
67
+ - Body text: **34px** minimum
68
+ - Caption/small text: **24px** minimum
69
+ - Absolute minimum for ANY readable text on ANY platform: **20px**
70
+
71
+ **Font Weight:** 500 or higher for body text and above. Never use font-weight below 400 for any visible text.
72
+
73
+ Templates that violate these rules are rejected — no exceptions.
74
+
75
+ ### Step 1: Read Context
76
+
77
+ Read these files to understand the squad:
78
+ - `squads/{code}/_build/discovery.yaml` — platform, domain, tone, language
79
+ - `squads/{code}/_build/design.yaml` — agents, purpose, skills
80
+ - `squads/{code}/_investigations/consolidated-analysis.md` (if exists) — visual patterns from reference profiles
81
+ - `_opensquad/_memory/company.md` — company name, brand, industry, target audience
82
+ - `_opensquad/_memory/preferences.md` — user preferences (language, style, tone)
83
+
84
+ Use the company context and user preferences to adapt template content: example text should reflect the company's domain and audience, colors should align with brand if available, and language should match the user's Output Language preference.
85
+
86
+ ### Step 2: Read Base Templates
87
+
88
+ Read the 3 base templates from `skills/template-designer/base-templates/`:
89
+ - `model-a.html`
90
+ - `model-b.html`
91
+ - `model-c.html`
92
+
93
+ ### Step 3: Generate Adapted Variations
94
+
95
+ For each base template, create an adapted version:
96
+ - Adjust colors to match the squad's domain/brand (use Sherlock palette if available, company brand colors from company.md if available)
97
+ - Adjust typography following the platform-specific minimum font sizes from `image-design.md`
98
+ - Replace example content with domain-relevant content that reflects the company's industry, audience, and language
99
+ - Set the root container to the exact fixed dimensions from HARD RULES above. Never use percentage heights or auto heights.
100
+ - Add any visual elements that match the squad's personality
101
+ - Apply proper white space, visual hierarchy, and spacing rhythm per `image-design.md` methodology
102
+
103
+ Write each adapted template as a **complete, self-contained HTML file** (with `<!DOCTYPE html>`, inline CSS, and Google Fonts imports if needed).
104
+
105
+ Save to:
106
+ - `squads/{code}/_build/template-a.html`
107
+ - `squads/{code}/_build/template-b.html`
108
+ - `squads/{code}/_build/template-c.html`
109
+
110
+ ### Step 4: Render as Images
111
+
112
+ Use the `image-creator` skill to render each HTML template as a JPG image:
113
+
114
+ 1. Read `skills/image-creator/SKILL.md` for rendering instructions
115
+ 2. Render each template HTML to JPG using the image-creator workflow
116
+ 3. Save rendered images to:
117
+ - `squads/{code}/_build/template-a.jpg`
118
+ - `squads/{code}/_build/template-b.jpg`
119
+ - `squads/{code}/_build/template-c.jpg`
120
+
121
+ ### Step 5: Present to User
122
+
123
+ Present the 3 template options to the user using **clickable markdown links with absolute file paths** so they can open and review:
124
+
125
+ > "Here are 3 template options for your squad's visual identity:
126
+ >
127
+ > - [preview-a.jpg]({absolute_path}/squads/{code}/_build/template-a.jpg) — Template A
128
+ > - [preview-b.jpg]({absolute_path}/squads/{code}/_build/template-b.jpg) — Template B
129
+ > - [preview-c.jpg]({absolute_path}/squads/{code}/_build/template-c.jpg) — Template C
130
+ >
131
+ > Click the links above to open each image. Tell me which one you prefer. I can also mix elements from different templates or adjust colors, fonts, and layout."
132
+
133
+ **Important:** Always use the full absolute path (e.g., `d:\Coding Projects\opensquad\squads\my-squad\_build\template-a.jpg`) inside the markdown link — relative paths are not clickable in the IDE.
134
+
135
+ ## Iteration Loop
136
+
137
+ 1. Present template images with clickable markdown links using absolute file paths
138
+ 2. Wait for user feedback in terminal
139
+ 3. Generate new version based on feedback — save as `template-v2.html`, render as `template-v2.jpg`, etc.
140
+ 4. Present updated image with clickable markdown link using absolute file path
141
+ 5. Repeat until user approves
142
+
143
+ ## Saving the Approved Template
144
+
145
+ When the user approves, create two files:
146
+
147
+ ### 1. Template Reference HTML
148
+
149
+ Save to: `squads/{code}/pipeline/data/template-reference.html`
150
+
151
+ The complete, self-contained HTML/CSS of the approved template at full resolution (e.g., 1080x1440). This is the literal example the design agent will use.
152
+
153
+ ### 2. Visual Identity Rules
154
+
155
+ Save to: `squads/{code}/pipeline/data/visual-identity.md`
156
+
157
+ Extract structured rules from the approved template:
158
+
159
+ ~~~markdown
160
+ # Visual Identity
161
+
162
+ ## Color Palette
163
+ - **Primary:** #HEXCODE — usage description
164
+ - **Secondary:** #HEXCODE — usage description
165
+ - **Background:** #HEXCODE
166
+ - **Text:** #HEXCODE
167
+ - **Accent:** #HEXCODE — usage description
168
+
169
+ ## Typography
170
+ - **Headings:** Font Family, weight, size range
171
+ - **Body:** Font Family, weight, size range
172
+ - **Caption:** Font Family, weight, size range
173
+ - **Minimum sizes:** body 32px, caption 24px, heading 48px
174
+
175
+ ## Layout
176
+ - **Viewport:** WIDTHxHEIGHT px
177
+ - **Padding:** value
178
+ - **Grid:** description
179
+ - **Spacing rules:** description
180
+
181
+ ## Composition Rules
182
+ - Logo/profile placement: description
183
+ - Image treatment: description
184
+ - Visual hierarchy: description
185
+ - Footer/CTA pattern: description
186
+
187
+ ## Adaptation Rules
188
+ - How to handle different viewport sizes
189
+ - What stays fixed vs. what adapts
190
+ - Color usage rules (when to use primary vs accent)
191
+ ~~~
192
+
193
+ ### 3. Update Squad Files
194
+
195
+ If the squad is being created (Build phase hasn't run yet):
196
+ - The design.yaml context now includes the template data — Build will pick it up
197
+
198
+ If the squad already exists (editing flow):
199
+ - Add `pipeline/data/template-reference.html` and `pipeline/data/visual-identity.md` to `squad.yaml` `data:` list
200
+ - Update the design agent's `.agent.md` to reference both files
201
+ - Update the design agent's tasks to include the rule: "always follow visual-identity.md and use template-reference.html as the base model"
@@ -0,0 +1,27 @@
1
+ <!-- Model A: Twitter Editorial — approved 2026-03-28 -->
2
+ <!-- Style: Black background, tweet-style layout with avatar, verified badge, editorial text, contextual image -->
3
+ <!-- Fonts: Inter (body), weight 400-700 -->
4
+ <!-- Colors: #000 bg, #fff text, #1D9BF0 verified, #71767B muted, #2a2a2a borders/avatar, #1a1a2e image gradient -->
5
+ <div style="width:1080px;height:1440px;background:#000;color:#fff;font-family:'Inter',sans-serif;display:flex;flex-direction:column;padding:72px;gap:0;position:relative;overflow:hidden;">
6
+ <!-- Tweet header -->
7
+ <div style="display:flex;gap:24px;align-items:center;">
8
+ <div style="width:80px;height:80px;border-radius:50%;background:#2a2a2a;flex-shrink:0;"></div>
9
+ <div>
10
+ <div style="display:flex;align-items:center;gap:8px;">
11
+ <span style="font-weight:700;font-size:34px;">Nome Sobrenome</span>
12
+ <svg width="28" height="28" viewBox="0 0 24 24" fill="#1D9BF0"><path d="M22.25 12c0-1.43-.88-2.67-2.19-3.34.46-1.39.2-2.9-.81-3.91s-2.52-1.27-3.91-.81c-.66-1.31-1.91-2.19-3.34-2.19s-2.67.88-3.34 2.19c-1.39-.46-2.9-.2-3.91.81s-1.27 2.52-.81 3.91C2.63 9.33 1.75 10.57 1.75 12s.88 2.67 2.19 3.34c-.46 1.39-.2 2.9.81 3.91s2.52 1.27 3.91.81c.66 1.31 1.91 2.19 3.34 2.19s2.67-.88 3.34-2.19c1.39.46 2.9.2 3.91-.81s1.27-2.52.81-3.91c1.31-.67 2.19-1.91 2.19-3.34zm-11.08 4.71c-.22.22-.58.22-.8 0L7.4 13.74c-.22-.22-.22-.58 0-.8.22-.22.58-.22.8 0l2.57 2.57 6.03-6.03c.22-.22.58-.22.8 0 .22.22.22.58 0 .8l-6.43 6.43z"/></svg>
13
+ </div>
14
+ <span style="font-size:28px;color:#71767B;">@username</span>
15
+ </div>
16
+ </div>
17
+ <!-- Tweet text -->
18
+ <div style="font-size:38px;line-height:1.45;font-weight:400;margin-top:48px;">
19
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium.
20
+ <br><br>
21
+ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit. Neque porro quisquam est qui dolorem.
22
+ </div>
23
+ <!-- Image -->
24
+ <div style="width:100%;height:35%;background:linear-gradient(180deg,#1a1a2e,#0d0d0d);border-radius:24px;display:flex;align-items:center;justify-content:center;border:1px solid #2a2a2a;flex-shrink:0;margin-top:auto;">
25
+ <span style="color:#555;font-size:34px;">Imagem contextual</span>
26
+ </div>
27
+ </div>
@@ -0,0 +1,31 @@
1
+ <!-- Model B: Clean Visual — approved 2026-03-28 -->
2
+ <!-- Style: Warm cream background, bold uppercase title, numbered cards with warm gradient progression -->
3
+ <!-- Fonts: Montserrat 900 (title), Inter 500-700 (body) -->
4
+ <!-- Colors: #FDF6EE bg, #E85D2A primary orange, #D4742A/#C05A1A/#A04010 gradient steps, #FFF5E6-#D07030 card backgrounds -->
5
+ <div style="width:1080px;height:1440px;background:#FDF6EE;color:#111;font-family:'Inter',sans-serif;display:flex;flex-direction:column;padding:72px;justify-content:center;gap:48px;position:relative;overflow:hidden;">
6
+ <!-- Title -->
7
+ <div style="text-align:center;">
8
+ <h1 style="font-size:58px;font-weight:900;line-height:1.15;margin:0;text-transform:uppercase;font-family:'Montserrat',sans-serif;">
9
+ <span style="color:#E85D2A;">LOREM IPSUM</span> DOLOR SIT AMET CONSECTETUR
10
+ </h1>
11
+ </div>
12
+ <!-- Items -->
13
+ <div style="display:flex;flex-direction:column;gap:20px;">
14
+ <div style="display:flex;align-items:center;gap:28px;background:#FFF5E6;border-radius:20px;padding:28px 32px;">
15
+ <div style="width:64px;height:64px;border-radius:50%;border:3px solid #E85D2A;display:flex;align-items:center;justify-content:center;font-size:28px;font-weight:800;color:#E85D2A;flex-shrink:0;">1</div>
16
+ <div><div style="font-size:34px;font-weight:700;">Proin tempus vehicula</div><div style="font-size:26px;color:#888;font-weight:500;">Sed ut perspiciatis unde omnis iste natus error.</div></div>
17
+ </div>
18
+ <div style="display:flex;align-items:center;gap:28px;background:linear-gradient(90deg,#FDEBD0,#F5C89A);border-radius:20px;padding:28px 32px;">
19
+ <div style="width:64px;height:64px;border-radius:50%;border:3px solid #D4742A;display:flex;align-items:center;justify-content:center;font-size:28px;font-weight:800;color:#D4742A;flex-shrink:0;">2</div>
20
+ <div><div style="font-size:34px;font-weight:700;">Vestibulum ante ipsum</div><div style="font-size:26px;color:#666;font-weight:500;">Nemo enim ipsam voluptatem quia voluptas sit.</div></div>
21
+ </div>
22
+ <div style="display:flex;align-items:center;gap:28px;background:linear-gradient(90deg,#F5C89A,#E8A065);border-radius:20px;padding:28px 32px;">
23
+ <div style="width:64px;height:64px;border-radius:50%;border:3px solid #C05A1A;display:flex;align-items:center;justify-content:center;font-size:28px;font-weight:800;color:#C05A1A;flex-shrink:0;">3</div>
24
+ <div><div style="font-size:34px;font-weight:700;">Maecenas dignissim justo</div><div style="font-size:26px;color:#555;font-weight:500;">Ut enim ad minima veniam nostrum exercitationem.</div></div>
25
+ </div>
26
+ <div style="display:flex;align-items:center;gap:28px;background:linear-gradient(90deg,#E8A065,#D07030);border-radius:20px;padding:28px 32px;">
27
+ <div style="width:64px;height:64px;border-radius:50%;border:3px solid #A04010;display:flex;align-items:center;justify-content:center;font-size:28px;font-weight:800;color:#A04010;flex-shrink:0;">4</div>
28
+ <div><div style="font-size:34px;font-weight:700;color:#fff;">Curabitur pretium tincidunt</div><div style="font-size:26px;color:#ffffffcc;font-weight:500;">Quis autem vel eum iure reprehenderit voluptate.</div></div>
29
+ </div>
30
+ </div>
31
+ </div>
@@ -0,0 +1,42 @@
1
+ <!-- Model C: Data Dashboard — approved 2026-03-28 -->
2
+ <!-- Style: Purple-dark gradient, data metric cards, decorative glow effects -->
3
+ <!-- Fonts: Montserrat 900 (title, metrics), Inter 500 (body, labels) -->
4
+ <!-- Colors: gradient #0f0326-#1a0a3e-#0d0d0d bg, #F59E0B yellow, #EC4899 pink, #7C3AED purple, #4ADE80 green accents -->
5
+ <div style="width:1080px;height:1440px;background:linear-gradient(160deg,#0f0326,#1a0a3e 40%,#0d0d0d);color:#fff;font-family:'Inter',sans-serif;display:flex;flex-direction:column;padding:72px;position:relative;overflow:hidden;">
6
+ <!-- Glow effects -->
7
+ <div style="position:absolute;top:-120px;right:-120px;width:480px;height:480px;background:radial-gradient(circle,#7C3AED33,transparent 70%);pointer-events:none;"></div>
8
+ <div style="position:absolute;bottom:-100px;left:-100px;width:400px;height:400px;background:radial-gradient(circle,#F59E0B22,transparent 70%);pointer-events:none;"></div>
9
+ <!-- Tag + Title -->
10
+ <div style="position:relative;z-index:1;">
11
+ <div style="display:inline-block;background:#F59E0B33;border-radius:8px;padding:8px 20px;font-size:24px;color:#F59E0B;font-weight:800;text-transform:uppercase;letter-spacing:3px;margin-bottom:28px;">Lorem Ipsum</div>
12
+ <h1 style="font-size:52px;font-weight:900;line-height:1.2;margin:0 0 24px 0;font-family:'Montserrat',sans-serif;">
13
+ Sed ut perspiciatis <span style="background:linear-gradient(90deg,#F59E0B,#EC4899);-webkit-background-clip:text;-webkit-text-fill-color:transparent;">unde omnis</span> iste natus error sit
14
+ </h1>
15
+ <p style="font-size:34px;color:#bbb;line-height:1.55;margin:0;font-weight:500;letter-spacing:0.01em;">Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores.</p>
16
+ </div>
17
+ <!-- Data cards -->
18
+ <div style="position:relative;z-index:1;display:flex;flex-direction:column;gap:16px;margin:40px 0;flex:1;justify-content:center;">
19
+ <div style="background:#ffffff08;border:1px solid #ffffff10;border-radius:16px;padding:28px 32px;display:flex;justify-content:space-between;align-items:center;">
20
+ <span style="font-size:28px;color:#888;font-weight:500;">Lorem ipsum</span>
21
+ <span style="font-size:40px;font-weight:800;color:#F59E0B;font-family:'Montserrat',sans-serif;">U$3.4B</span>
22
+ </div>
23
+ <div style="background:#ffffff08;border:1px solid #ffffff10;border-radius:16px;padding:28px 32px;display:flex;justify-content:space-between;align-items:center;">
24
+ <span style="font-size:28px;color:#888;font-weight:500;">Dolor sit amet</span>
25
+ <span style="font-size:40px;font-weight:800;color:#EC4899;font-family:'Montserrat',sans-serif;">-U$5B</span>
26
+ </div>
27
+ <div style="background:#ffffff08;border:1px solid #ffffff10;border-radius:16px;padding:28px 32px;display:flex;justify-content:space-between;align-items:center;">
28
+ <span style="font-size:28px;color:#888;font-weight:500;">Consectetur</span>
29
+ <span style="font-size:40px;font-weight:800;color:#7C3AED;font-family:'Montserrat',sans-serif;">U$157B</span>
30
+ </div>
31
+ <div style="background:#ffffff08;border:1px solid #ffffff10;border-radius:16px;padding:28px 32px;display:flex;justify-content:space-between;align-items:center;">
32
+ <span style="font-size:28px;color:#888;font-weight:500;">Adipiscing elit</span>
33
+ <span style="font-size:40px;font-weight:800;color:#4ADE80;font-family:'Montserrat',sans-serif;">3.500+</span>
34
+ </div>
35
+ </div>
36
+ <!-- Footer -->
37
+ <div style="position:relative;z-index:1;display:flex;align-items:center;gap:20px;padding-top:24px;border-top:1px solid #ffffff12;">
38
+ <div style="width:48px;height:48px;border-radius:50%;background:linear-gradient(135deg,#F59E0B,#EC4899);"></div>
39
+ <span style="font-size:24px;color:#777;">@username</span>
40
+ <span style="margin-left:auto;font-size:24px;color:#555;">ARRASTE →</span>
41
+ </div>
42
+ </div>
@@ -0,0 +1,232 @@
1
+ ---
2
+ name: youtube-publisher
3
+ description: >
4
+ Publishes simple YouTube videos from a local image sequence.
5
+ Builds an MP4 slideshow with ffmpeg using 10-second still intervals,
6
+ applies the repository background soundtrack by default when available,
7
+ validates OAuth credentials, uploads via the YouTube Data API,
8
+ and can set a custom thumbnail plus richer video metadata.
9
+ description_pt-BR: >
10
+ Publica videos simples no YouTube a partir de uma sequencia local de imagens.
11
+ Monta um slideshow MP4 com ffmpeg usando 10 segundos por imagem,
12
+ aplica por padrao a trilha de fundo do repositorio quando disponivel,
13
+ valida as credenciais OAuth, faz upload pela YouTube Data API
14
+ e pode definir thumbnail customizada e metadados mais completos.
15
+ description_es: >
16
+ Publica videos simples en YouTube a partir de una secuencia local de imagenes.
17
+ Genera un slideshow MP4 con ffmpeg usando 10 segundos por imagen,
18
+ aplica por defecto la pista de fondo del repositorio cuando esta disponible,
19
+ valida credenciales OAuth y sube el video con la YouTube Data API.
20
+ type: script
21
+ version: "1.0.0"
22
+ script:
23
+ path: scripts/publish.js
24
+ runtime: node
25
+ invoke: "node --env-file=.env {skill_path}/scripts/publish.js --images \"{images}\" --title \"{title}\" --description \"{description}\""
26
+ env:
27
+ - YOUTUBE_CLIENT_ID
28
+ - YOUTUBE_CLIENT_SECRET
29
+ - YOUTUBE_REFRESH_TOKEN
30
+ - YOUTUBE_CHANNEL_ID
31
+ categories: [social-media, publishing, youtube, video]
32
+ ---
33
+
34
+ # YouTube Publisher
35
+
36
+ ## When to use
37
+
38
+ Use the YouTube Publisher when you need an initial proof of publishing to YouTube from content already rendered as local images. This version turns ordered JPG/JPEG or PNG slides into a simple MP4 slideshow with **10 seconds per image**, applies the repository soundtrack from `public/Trilha sonora de slides.mp3` when that file exists, validates OAuth access, uploads the video to the target YouTube channel, and can optionally apply a custom thumbnail plus richer video metadata.
39
+
40
+ ## Instructions
41
+
42
+ ### Workflow
43
+
44
+ 1. List JPG/JPEG or PNG files in the squad output folder and confirm their order with the user.
45
+ 2. Build the YouTube metadata package for the upload:
46
+ - title and description
47
+ - tags and category
48
+ - privacy status (default: `public`; use `private` only for approved scheduling flows)
49
+ - optional thumbnail, otherwise auto-generate one from slide 2 in 16:9 when available
50
+ - optional scheduling/language/recording fields
51
+ - normalize wrapped communication-channel values from `content-package.md` before composing the description; strip surrounding `< >`, quotes, or backticks so YouTube receives plain-text URLs/emails only
52
+ - for Opensquad slide/news runs, ensure the description ends with a `FONTES VERIFICADAS` section listing the main verified sources with source name, valid URL, and consultation date
53
+ 3. Run a **dry-run first** to validate:
54
+ - local image files exist
55
+ - `ffmpeg` is resolvable either from `FFMPEG_PATH`/`FFMPEG_BIN`, the current `PATH`, or the validated local WinGet install on Windows
56
+ - the default background soundtrack is detected, or a custom `--audio-track` path resolves correctly when provided
57
+ - OAuth refresh token can be exchanged for an access token
58
+ - authenticated account can access the target channel
59
+ - slideshow MP4 can be generated successfully
60
+ - if the refresh-token exchange fails with `invalid_grant`, stop the live upload path and refresh the OAuth grant before retrying
61
+ 4. Present the preview and wait for explicit user confirmation before a live upload.
62
+ 5. On live upload, call the script without `--dry-run`, then save the resulting YouTube video ID, URL, thumbnail status, and effective metadata.
63
+ 6. If this upload belongs to an Opensquad run, keep the run-dashboard in the closing checklist. If `jornal-matutino` is not pending, regenerate the run dashboard immediately after the last selected output completes. If `jornal-matutino` is still pending, defer dashboard regeneration until that complementary step finishes. When static publish is enabled in squad config, publishing the dashboard is automatic and does not require a second confirmation prompt.
64
+
65
+ ### OAuth recovery when `invalid_grant` appears
66
+
67
+ If Google returns `invalid_grant` during the refresh-token exchange, treat the stored refresh token as expired or revoked.
68
+
69
+ Recovery procedure:
70
+
71
+ 1. Generate a fresh consent URL for the brand's desktop OAuth client with `access_type=offline` and `prompt=consent`.
72
+ 2. Open the URL in the browser and complete the Google approval flow with the correct brand account.
73
+ 3. If the browser lands on `http://127.0.0.1:3000/?code=...` and shows `ERR_CONNECTION_REFUSED`, do not treat that page as a blocker. The `code` query parameter is still valid.
74
+ 4. Exchange that authorization code manually against `https://oauth2.googleapis.com/token` using the same desktop client id, client secret, and redirect URI.
75
+ 5. Update the squad-specific refresh token in `.env`, rerun the YouTube dry-run, and only then proceed to the live upload.
76
+
77
+ ### Command
78
+
79
+ ```
80
+ node --env-file=.env {skill_path}/scripts/publish.js \
81
+ --images "slide-01.jpg,slide-02.png,slide-03.jpg" \
82
+ --title "Teste inicial do pipeline no YouTube" \
83
+ --description "Video de validacao gerado automaticamente a partir das imagens do post." \
84
+ [--thumbnail "squads/{squad}/output/thumbs/youtube-thumb.jpg"] \
85
+ [--privacy-status "public"] \
86
+ [--tags "opensquad,teste,youtube"] \
87
+ [--category-id "22"] \
88
+ [--made-for-kids "false"] \
89
+ [--embeddable "true"] \
90
+ [--public-stats-viewable "true"] \
91
+ [--license "youtube"] \
92
+ [--publish-at "2026-05-18T15:00:00Z"] \
93
+ [--default-language "pt-BR"] \
94
+ [--default-audio-language "pt-BR"] \
95
+ [--recording-date "2026-05-17"] \
96
+ [--playlist-name "Posts automáticos"] \
97
+ [--playlist-privacy "private"] \
98
+ [--video-output "squads/{squad}/output/videos/youtube-validation.mp4"] \
99
+ [--audio-track "public/Trilha sonora de slides.mp3"] \
100
+ [--youtube-client-id-env "CUSTOM_YOUTUBE_CLIENT_ID"] \
101
+ [--youtube-client-secret-env "CUSTOM_YOUTUBE_CLIENT_SECRET"] \
102
+ [--youtube-refresh-token-env "CUSTOM_YOUTUBE_REFRESH_TOKEN"] \
103
+ [--youtube-channel-id-env "CUSTOM_YOUTUBE_CHANNEL_ID"] \
104
+ [--no-narration] \
105
+ [--dry-run]
106
+ ```
107
+
108
+ ### Per-squad credential routing
109
+
110
+ If the workspace operates multiple brands, prefer per-squad environment aliases instead of a single shared YouTube credential set.
111
+
112
+ Example:
113
+
114
+ ```
115
+ node --env-file=.env {skill_path}/scripts/publish.js \
116
+ --images "slide-01.jpg,slide-02.png" \
117
+ --title "MusicPlay Club - teste de upload" \
118
+ --description "Validacao do fluxo de YouTube" \
119
+ --youtube-client-id-env "MUSICPLAY_YOUTUBE_CLIENT_ID" \
120
+ --youtube-client-secret-env "MUSICPLAY_YOUTUBE_CLIENT_SECRET" \
121
+ --youtube-refresh-token-env "MUSICPLAY_YOUTUBE_REFRESH_TOKEN" \
122
+ --youtube-channel-id-env "MUSICPLAY_YOUTUBE_CHANNEL_ID" \
123
+ --dry-run
124
+ ```
125
+
126
+ If these flags are omitted, the script uses the default env keys:
127
+
128
+ - `YOUTUBE_CLIENT_ID`
129
+ - `YOUTUBE_CLIENT_SECRET`
130
+ - `YOUTUBE_REFRESH_TOKEN`
131
+ - `YOUTUBE_CHANNEL_ID`
132
+
133
+ ### Constraints
134
+
135
+ - Input images: JPG, JPEG, or PNG
136
+ - Thumbnail: JPG, JPEG, or PNG, max 2MB
137
+ - If `--thumbnail` is omitted, the script auto-generates a 1280x720 thumbnail from slide 2 when available, otherwise from slide 1
138
+ - If the channel is not eligible for custom thumbnails yet, the video upload still succeeds and the run reports the thumbnail upload error separately
139
+ - Minimum image count: 1
140
+ - Slide duration: determined dynamically based on slide narration length. The narrated default is 1s pause at the start of each slide + Title voice + 1s pause + Text voice + 2s pause at the end of the slide, plus 1 extra second of dwell before the transition timing begins. The visual transition must start only after that full narrated block ends; it must never overlap the slide narration itself. If narration is disabled via `--no-narration`, slide duration defaults to a fixed 10 seconds per image.
141
+ - Background audio: mixed automatically with narration. Background soundtrack volume is `0.12` when voice is present (otherwise `0.3`), narration volume is `1.2`, with a 2-second fade-in and 5-second fade-out. **Audio mix uses `amix` with `normalize=0`** — obrigatório para evitar aumento de volume no último slide quando o SFX de transição termina antes da narração e música.
142
+ - Slide transitions: by default, consecutive slides use a `fade-out-to-black` + `fade-in-from-black` transition (`fadeblack`) lasting 2 seconds.
143
+ - Transition SFX: by default, each slide change uses the shared asset `public/sfx/slide-transition-sfx.mp3` (warm whoosh, canonical since 2026-05). If that shared asset is unavailable, the publisher falls back to Eleven Labs generation. **Never recriar o arquivo a cada run** — reuse the shared copy. This is the standard transition sound for all squads unless explicitly overridden.
144
+ - Video ending: the final slide remains visible to the end while the soundtrack fades out during the last 5 seconds.
145
+ - Video-only progress indicator: only active and displayed for fixed-duration slideshows (when `--no-narration` is used). It overlays a circular progress marker in a corner of the MP4 that loops every 10 seconds. For dynamic voice-synchronized slideshows, the progress ring is hidden to ensure a clean design.
146
+ - Narration: Enabled by default and uses Eleven Labs with the "Dani" voice (`PznTnBc8X6pvixs9UkQm`) for both title (expressive parameters) and text description. Can be disabled via `--no-narration` (which falls back to a fixed 10s duration and silence narration track).
147
+ - Narration source discipline: for every slide, synthesize only the text that is actually visible on the slide canvas and relevant to the slide itself, prioritizing the visible title plus the primary body copy. Do not let narration spill into off-slide editorial support text, QR/link instructions, channel lists, source appendices, or any longer `content-package.md` context that is not on the card.
148
+ - Last-slide guardrail: when `content-package.md` continues with sections such as `## Legenda`, `## Hashtags`, `## Saiba mais`, or compliance notes after the final `## Slide N`, the narration parser must stop at the next markdown heading. Never let the final slide absorb trailing document sections just because it is the last slide in the file.
149
+ - Description sources: for Opensquad slide/news runs, the YouTube description should carry a final `FONTES VERIFICADAS` block with the verified source entries actually used in the story, each one with a valid URL and consultation date.
150
+ - Description sanitation: communication-channel values imported from `content-package.md` must be emitted as plain text. Remove decorative wrappers such as `<https://...>` or `<email@brand.com>` before upload.
151
+ - Video format: MP4 (`libx264`, `yuv420p`) rendered in 1280x720 landscape to avoid Shorts-style square output
152
+ - `ffmpeg` must be installed and resolvable by the shared publisher logic (`FFMPEG_PATH`/`FFMPEG_BIN`, `PATH`, or the local WinGet install on Windows)
153
+ - On Windows, prefer the full WinGet build from `Gyan.FFmpeg`; incomplete bundled builds may miss filters required by the slideshow fallback
154
+ - OAuth scopes required:
155
+ - `https://www.googleapis.com/auth/youtube.upload`
156
+ - `https://www.googleapis.com/auth/youtube.force-ssl`
157
+ - `https://www.googleapis.com/auth/youtube.readonly`
158
+ - `--publish-at` requires `--privacy-status private`
159
+
160
+ ### Setup (first-time)
161
+
162
+ 1. Create a dedicated Google account for the brand/channel. Prefer one Gmail per brand.
163
+ 2. Create or choose a YouTube channel owned by that Google account.
164
+ 3. Create a Google Cloud project and enable **YouTube Data API v3**.
165
+ 4. Create an **OAuth Client ID** of type **Desktop app**.
166
+ 5. Obtain a refresh token with the scopes `https://www.googleapis.com/auth/youtube.upload`, `https://www.googleapis.com/auth/youtube.force-ssl`, and `https://www.googleapis.com/auth/youtube.readonly`.
167
+ 6. Fill `.env` with the generated values.
168
+ 7. Install `ffmpeg` locally and confirm `where.exe ffmpeg` plus `ffmpeg -version` work in a fresh terminal. On Windows, the validated install command is `winget install --id Gyan.FFmpeg --silent --accept-package-agreements --accept-source-agreements`. If corporate shell policy prevents global PATH propagation, set `FFMPEG_PATH` to the resolved `ffmpeg.exe` as a stable fallback.
169
+
170
+ Detailed authorization steps are documented in `DOC_YOUTUBE.md`.
171
+
172
+ ### Operational note
173
+
174
+ Do not rely on an `ffmpeg.exe` inherited from another workspace, a third-party `node_modules` folder, or a temporary `PATH` change in one shell only. The preferred operating state is that a new terminal can resolve `ffmpeg`, but the shared publisher also supports an explicit `FFMPEG_PATH` or the validated local WinGet install as a recovery path.
175
+
176
+ ## Available operations
177
+
178
+ - **Build slideshow video** -- Converts ordered images into a 1280x720 landscape MP4 slideshow with 10 seconds per image
179
+ - **Dry Run** -- Validates files, thumbnail constraints, ffmpeg, OAuth token exchange, channel access, and video generation without uploading
180
+ - **Upload Video** -- Performs resumable upload to YouTube and returns the video ID and URL
181
+ - **Upload Thumbnail** -- Applies a custom YouTube thumbnail after the video is created
182
+ - **Playlist routing** -- Creates or reuses a target playlist and adds the uploaded video to it
183
+ - **Rich Metadata** -- Sets privacy, category, tags, kids setting, embeddability, visibility of stats, license, scheduling, language, and recording date
184
+ - **Per-squad env routing** -- Reads credentials from default keys or custom env-variable aliases
185
+
186
+ ## Output expectations
187
+
188
+ Successful live runs should report:
189
+
190
+ - `Status: published`
191
+ - `YouTube Video ID`
192
+ - `YouTube URL`
193
+ - effective channel title/id
194
+ - generated local MP4 path
195
+ - `Thumbnail Uploaded`
196
+ - `Thumbnail Upload Error` when the video upload succeeds but the channel rejects custom thumbnails
197
+ - metadata summary (privacy, category, tags, kids flag, schedule/language when present)
198
+
199
+ Dry-runs should report:
200
+
201
+ - `Status: dry-run`
202
+ - validated channel title/id
203
+ - generated local MP4 path
204
+ - validated thumbnail path when provided
205
+ - normalized metadata summary
206
+ - confirmation that upload was skipped intentionally
207
+
208
+ ## Pre-publish checklist (mandatory for all squads)
209
+
210
+ Before executing a YouTube publish command for any squad, the pipeline runner or agent must verify every item below. This checklist applies globally unless the squad's local step file explicitly overrides a specific item.
211
+
212
+ - [ ] `ELEVEN_LABS_API` is set in `.env` and valid (narration is **enabled by default**)
213
+ - [ ] Narration uses the Dani voice (`PznTnBc8X6pvixs9UkQm`) via Eleven Labs for title + body text of each slide
214
+ - [ ] `--no-narration` is **not used** unless the user or the squad's local pipeline step explicitly requests silent mode
215
+ - [ ] Background soundtrack (`public/Trilha sonora de slides.mp3`) is present and mixed at volume `0.12` (with voice) or `0.3` (without voice)
216
+ - [ ] Slide transition SFX (`public/sfx/slide-transition-sfx.mp3`) is present and reused (not regenerated per run)
217
+ - [ ] `ffmpeg` is resolvable (`where.exe ffmpeg` or `FFMPEG_PATH`)
218
+ - [ ] OAuth refresh token exchange succeeds (dry-run validates this)
219
+ - [ ] Thumbnail is auto-generated from slide 2 (or slide 1 if slide 2 is unavailable), unless a custom thumbnail is approved
220
+ - [ ] Title is short, clear, and aligned with the editorial theme
221
+ - [ ] Description ends with `FONTES VERIFICADAS` block (for Opensquad news/slide runs)
222
+ - [ ] Hashtags are reviewed and content-coherent
223
+ - [ ] Content is in the squad's target language with proper accentation
224
+ - [ ] Slide order matches the approved editorial sequence
225
+ - [ ] Video output is 1280x720 landscape MP4, not square/vertical (to avoid Shorts misclassification)
226
+
227
+ > **Why narration is mandatory by default:** The Dani voice narration is a core brand differentiator for Opensquad video content. Publishing a slideshow without narration produces a silent video that feels incomplete and harms engagement metrics. Any agent that skips narration without explicit user approval is committing a pipeline violation.
228
+
229
+ ## Default visibility policy
230
+
231
+ - Default live visibility is `public`.
232
+ - Use `private` only when the user explicitly wants scheduling or a restricted pre-release flow.