@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.
- package/README.md +433 -0
- package/_opensquad/config/playwright.config.json +11 -0
- package/_opensquad/core/architect.agent.yaml +112 -0
- package/_opensquad/core/best-practices/_catalog.yaml +126 -0
- package/_opensquad/core/best-practices/blog-post.md +132 -0
- package/_opensquad/core/best-practices/blog-seo.md +127 -0
- package/_opensquad/core/best-practices/brand-resolution-checklist.md +172 -0
- package/_opensquad/core/best-practices/copywriting.md +441 -0
- package/_opensquad/core/best-practices/data-analysis.md +401 -0
- package/_opensquad/core/best-practices/email-newsletter.md +118 -0
- package/_opensquad/core/best-practices/email-sales.md +110 -0
- package/_opensquad/core/best-practices/image-design.md +348 -0
- package/_opensquad/core/best-practices/instagram-feed.md +235 -0
- package/_opensquad/core/best-practices/instagram-reels.md +112 -0
- package/_opensquad/core/best-practices/instagram-stories.md +107 -0
- package/_opensquad/core/best-practices/linkedin-article.md +116 -0
- package/_opensquad/core/best-practices/linkedin-post.md +121 -0
- package/_opensquad/core/best-practices/researching.md +349 -0
- package/_opensquad/core/best-practices/review.md +269 -0
- package/_opensquad/core/best-practices/run-recovery.md +61 -0
- package/_opensquad/core/best-practices/social-networks-publishing.md +327 -0
- package/_opensquad/core/best-practices/squad-creation-checklist.md +32 -0
- package/_opensquad/core/best-practices/strategist.md +344 -0
- package/_opensquad/core/best-practices/technical-writing.md +365 -0
- package/_opensquad/core/best-practices/twitter-post.md +105 -0
- package/_opensquad/core/best-practices/twitter-thread.md +122 -0
- package/_opensquad/core/best-practices/whatsapp-broadcast.md +107 -0
- package/_opensquad/core/best-practices/youtube-script.md +122 -0
- package/_opensquad/core/best-practices/youtube-shorts.md +112 -0
- package/_opensquad/core/defaults/youtube-video-assembly.json +84 -0
- package/_opensquad/core/prompts/build.prompt.md +613 -0
- package/_opensquad/core/prompts/design.prompt.md +606 -0
- package/_opensquad/core/prompts/discovery.prompt.md +377 -0
- package/_opensquad/core/prompts/sherlock-instagram.md +123 -0
- package/_opensquad/core/prompts/sherlock-linkedin.md +73 -0
- package/_opensquad/core/prompts/sherlock-shared.md +684 -0
- package/_opensquad/core/prompts/sherlock-twitter.md +78 -0
- package/_opensquad/core/prompts/sherlock-youtube.md +85 -0
- package/_opensquad/core/runner.pipeline.md +743 -0
- package/_opensquad/core/skills.engine.md +384 -0
- package/bin/opensquad.js +108 -0
- package/dashboard/index.html +15 -0
- package/dashboard/package-lock.json +1964 -0
- package/dashboard/package.json +28 -0
- package/dashboard/public/assets/avatars/Female1_1wave.png +0 -0
- package/dashboard/public/assets/avatars/Female1_2wave.png +0 -0
- package/dashboard/public/assets/avatars/Female1_blink.png +0 -0
- package/dashboard/public/assets/avatars/Female1_talk.png +0 -0
- package/dashboard/public/assets/avatars/Female2_1wave.png +0 -0
- package/dashboard/public/assets/avatars/Female2_2wave.png +0 -0
- package/dashboard/public/assets/avatars/Female2_blink.png +0 -0
- package/dashboard/public/assets/avatars/Female2_talk.png +0 -0
- package/dashboard/public/assets/avatars/Female3_blink.png +0 -0
- package/dashboard/public/assets/avatars/Female3_talk.png +0 -0
- package/dashboard/public/assets/avatars/Female3_wave.png +0 -0
- package/dashboard/public/assets/avatars/Female4_blink.png +0 -0
- package/dashboard/public/assets/avatars/Female4_talk.png +0 -0
- package/dashboard/public/assets/avatars/Female4_wave.png +0 -0
- package/dashboard/public/assets/avatars/Female5_blink.png +0 -0
- package/dashboard/public/assets/avatars/Female5_talk.png +0 -0
- package/dashboard/public/assets/avatars/Female5_wave.png +0 -0
- package/dashboard/public/assets/avatars/Female6_blink.png +0 -0
- package/dashboard/public/assets/avatars/Female6_talk.png +0 -0
- package/dashboard/public/assets/avatars/Female6_wave.png +0 -0
- package/dashboard/public/assets/avatars/Male1_1wave.png +0 -0
- package/dashboard/public/assets/avatars/Male1_2wave.png +0 -0
- package/dashboard/public/assets/avatars/Male1_blink.png +0 -0
- package/dashboard/public/assets/avatars/Male1_talk.png +0 -0
- package/dashboard/public/assets/avatars/Male2_1wave.png +0 -0
- package/dashboard/public/assets/avatars/Male2_2wave.png +0 -0
- package/dashboard/public/assets/avatars/Male2_blink.png +0 -0
- package/dashboard/public/assets/avatars/Male2_talk.png +0 -0
- package/dashboard/public/assets/avatars/Male3_blink.png +0 -0
- package/dashboard/public/assets/avatars/Male3_talk.png +0 -0
- package/dashboard/public/assets/avatars/Male3_wave.png +0 -0
- package/dashboard/public/assets/avatars/Male4_blink.png +0 -0
- package/dashboard/public/assets/avatars/Male4_talk.png +0 -0
- package/dashboard/public/assets/avatars/Male4_wave.png +0 -0
- package/dashboard/public/assets/desks/desktop_set_black_down.png +0 -0
- package/dashboard/public/assets/desks/desktop_set_black_down_coding-1.png +0 -0
- package/dashboard/public/assets/desks/desktop_set_black_down_coding.png +0 -0
- package/dashboard/public/assets/desks/desktop_set_black_up.png +0 -0
- package/dashboard/public/assets/desks/desktop_set_white_down.png +0 -0
- package/dashboard/public/assets/desks/desktop_set_white_down_coding-1.png +0 -0
- package/dashboard/public/assets/desks/desktop_set_white_down_coding.png +0 -0
- package/dashboard/public/assets/desks/desktop_set_white_up.png +0 -0
- package/dashboard/public/assets/furniture/armchair_tan.png +0 -0
- package/dashboard/public/assets/furniture/armchair_tan_down.png +0 -0
- package/dashboard/public/assets/furniture/backpack_blue.png +0 -0
- package/dashboard/public/assets/furniture/backpack_red.png +0 -0
- package/dashboard/public/assets/furniture/blinds.png +0 -0
- package/dashboard/public/assets/furniture/blinds_large_closed_white.png +0 -0
- package/dashboard/public/assets/furniture/bookshelf.png +0 -0
- package/dashboard/public/assets/furniture/bookshelf_purple_tall.png +0 -0
- package/dashboard/public/assets/furniture/bulletin_board.png +0 -0
- package/dashboard/public/assets/furniture/clock.png +0 -0
- package/dashboard/public/assets/furniture/coffee_mug.png +0 -0
- package/dashboard/public/assets/furniture/coffee_mug_blue.png +0 -0
- package/dashboard/public/assets/furniture/coffee_table.png +0 -0
- package/dashboard/public/assets/furniture/coffeepot_right.png +0 -0
- package/dashboard/public/assets/furniture/coffeetable_black_horizontal.png +0 -0
- package/dashboard/public/assets/furniture/couch.png +0 -0
- package/dashboard/public/assets/furniture/couch_tan_down.png +0 -0
- package/dashboard/public/assets/furniture/cushion_blue.png +0 -0
- package/dashboard/public/assets/furniture/cushion_tan.png +0 -0
- package/dashboard/public/assets/furniture/desk_wood.png +0 -0
- package/dashboard/public/assets/furniture/fancy_rug.png +0 -0
- package/dashboard/public/assets/furniture/fancy_rug_wide.png +0 -0
- package/dashboard/public/assets/furniture/flowers1.png +0 -0
- package/dashboard/public/assets/furniture/flowers2.png +0 -0
- package/dashboard/public/assets/furniture/lamp_tan.png +0 -0
- package/dashboard/public/assets/furniture/lantern.png +0 -0
- package/dashboard/public/assets/furniture/monstera.png +0 -0
- package/dashboard/public/assets/furniture/monstera_small.png +0 -0
- package/dashboard/public/assets/furniture/picture_frame.png +0 -0
- package/dashboard/public/assets/furniture/plant1.png +0 -0
- package/dashboard/public/assets/furniture/plant2.png +0 -0
- package/dashboard/public/assets/furniture/plant3.png +0 -0
- package/dashboard/public/assets/furniture/plant_poof.png +0 -0
- package/dashboard/public/assets/furniture/plant_spindly.png +0 -0
- package/dashboard/public/assets/furniture/poster_blue.png +0 -0
- package/dashboard/public/assets/furniture/rug.png +0 -0
- package/dashboard/public/assets/furniture/succulent_blue.png +0 -0
- package/dashboard/public/assets/furniture/succulent_green.png +0 -0
- package/dashboard/public/assets/furniture/treasurechest_closed_gold.png +0 -0
- package/dashboard/public/assets/furniture/water_cooler_better.png +0 -0
- package/dashboard/public/assets/furniture/whiteboard.png +0 -0
- package/dashboard/public/assets/furniture/whiteboard_stand_graph.png +0 -0
- package/dashboard/public/assets/furniture/window_blinds_open.png +0 -0
- package/dashboard/src/App.tsx +46 -0
- package/dashboard/src/components/RunDashboardButton.tsx +92 -0
- package/dashboard/src/components/SquadCard.tsx +49 -0
- package/dashboard/src/components/SquadSelector.tsx +67 -0
- package/dashboard/src/components/StatusBadge.tsx +32 -0
- package/dashboard/src/components/StatusBar.tsx +116 -0
- package/dashboard/src/hooks/useSquadSocket.ts +135 -0
- package/dashboard/src/lib/formatTime.ts +16 -0
- package/dashboard/src/lib/normalizeState.ts +25 -0
- package/dashboard/src/main.tsx +10 -0
- package/dashboard/src/office/AgentSprite.ts +241 -0
- package/dashboard/src/office/OfficeScene.ts +153 -0
- package/dashboard/src/office/PhaserGame.tsx +80 -0
- package/dashboard/src/office/RoomBuilder.ts +190 -0
- package/dashboard/src/office/assetKeys.ts +150 -0
- package/dashboard/src/office/palette.ts +32 -0
- package/dashboard/src/plugin/squadWatcher.ts +397 -0
- package/dashboard/src/store/useSquadStore.ts +56 -0
- package/dashboard/src/styles/globals.css +36 -0
- package/dashboard/src/types/state.ts +63 -0
- package/dashboard/src/vite-env.d.ts +1 -0
- package/dashboard/tsconfig.json +24 -0
- package/dashboard/vite.config.ts +13 -0
- package/package.json +59 -0
- package/public/sfx/slide-transition-sfx.mp3 +0 -0
- package/skills/README.md +84 -0
- package/skills/apify/SKILL.md +55 -0
- package/skills/blotato/SKILL.md +63 -0
- package/skills/canva/SKILL.md +60 -0
- package/skills/higgsfield/SKILL.md +147 -0
- package/skills/image-ai-generator/SKILL.md +124 -0
- package/skills/image-ai-generator/scripts/generate.py +175 -0
- package/skills/image-creator/SKILL.md +166 -0
- package/skills/image-creator/editorial-slide-template.js +645 -0
- package/skills/image-fetcher/SKILL.md +91 -0
- package/skills/imgbb-uploader/SKILL.md +73 -0
- package/skills/imgbb-uploader/scripts/upload.js +125 -0
- package/skills/instagram-publisher/README.md +36 -0
- package/skills/instagram-publisher/SKILL.md +231 -0
- package/skills/instagram-publisher/scripts/publish-playwright.js +418 -0
- package/skills/instagram-publisher/scripts/publish.js +521 -0
- package/skills/opensquad-agent-creator/SKILL.md +192 -0
- package/skills/opensquad-skill-creator/SKILL.md +420 -0
- package/skills/opensquad-skill-creator/agents/analyzer.md +274 -0
- package/skills/opensquad-skill-creator/agents/comparator.md +202 -0
- package/skills/opensquad-skill-creator/agents/grader.md +223 -0
- package/skills/opensquad-skill-creator/assets/eval_review.html +146 -0
- package/skills/opensquad-skill-creator/eval-viewer/generate_review.py +471 -0
- package/skills/opensquad-skill-creator/eval-viewer/viewer.html +1325 -0
- package/skills/opensquad-skill-creator/references/schemas.md +430 -0
- package/skills/opensquad-skill-creator/references/skill-format.md +235 -0
- package/skills/opensquad-skill-creator/scripts/__init__.py +0 -0
- package/skills/opensquad-skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/skills/opensquad-skill-creator/scripts/quick_validate.py +103 -0
- package/skills/opensquad-skill-creator/scripts/run_eval.py +310 -0
- package/skills/opensquad-skill-creator/scripts/utils.py +47 -0
- package/skills/pdf-extractor/SKILL.md +57 -0
- package/skills/pdf-extractor/scripts/extract.py +82 -0
- package/skills/resend/SKILL.md +80 -0
- package/skills/run-dashboard/README.md +93 -0
- package/skills/run-dashboard/SKILL.md +173 -0
- package/skills/run-dashboard/scripts/finalize-state.js +273 -0
- package/skills/run-dashboard/scripts/generate.js +1296 -0
- package/skills/run-dashboard/scripts/serve.js +135 -0
- package/skills/run-dashboard/templates/run-dashboard-simple.template.html +191 -0
- package/skills/run-dashboard/templates/run-dashboard.template.html +1164 -0
- package/skills/smtp-sender/SKILL.md +88 -0
- package/skills/smtp-sender/scripts/send.js +478 -0
- package/skills/template-designer/SKILL.md +201 -0
- package/skills/template-designer/base-templates/model-a.html +27 -0
- package/skills/template-designer/base-templates/model-b.html +31 -0
- package/skills/template-designer/base-templates/model-c.html +42 -0
- package/skills/youtube-publisher/SKILL.md +232 -0
- package/skills/youtube-publisher/scripts/publish.js +2078 -0
- package/src/agents-cli.js +158 -0
- package/src/agents.js +134 -0
- package/src/i18n.js +48 -0
- package/src/init.js +442 -0
- package/src/locales/en.json +79 -0
- package/src/locales/es.json +78 -0
- package/src/locales/pt-BR.json +78 -0
- package/src/logger.js +38 -0
- package/src/prompt.js +46 -0
- package/src/readme/README.md +146 -0
- package/src/runs.js +318 -0
- package/src/skills-cli.js +157 -0
- package/src/skills.js +146 -0
- package/src/supabase-cli.js +584 -0
- package/src/update.js +169 -0
- package/templates/_opensquad/.opensquad-version +1 -0
- package/templates/_opensquad/_investigations/.gitkeep +0 -0
- package/templates/ide-templates/antigravity/.agent/rules/opensquad.md +68 -0
- package/templates/ide-templates/antigravity/.agent/workflows/opensquad.md +102 -0
- package/templates/ide-templates/claude-code/.claude/skills/opensquad/SKILL.md +182 -0
- package/templates/ide-templates/claude-code/.mcp.json +8 -0
- package/templates/ide-templates/claude-code/CLAUDE.md +57 -0
- package/templates/ide-templates/codex/.agents/skills/opensquad/SKILL.md +6 -0
- package/templates/ide-templates/codex/AGENTS.md +120 -0
- package/templates/ide-templates/cursor/.cursor/commands/opensquad.md +9 -0
- package/templates/ide-templates/cursor/.cursor/mcp.json +8 -0
- package/templates/ide-templates/cursor/.cursor/rules/opensquad.mdc +62 -0
- package/templates/ide-templates/cursor/.cursorignore +3 -0
- package/templates/ide-templates/gemini-cli/.gemini/settings.json +8 -0
- package/templates/ide-templates/gemini-cli/.gemini/skills/opensquad/SKILL.md +186 -0
- package/templates/ide-templates/gemini-cli/GEMINI.md +57 -0
- package/templates/ide-templates/opencode/.opencode/commands/opensquad.md +9 -0
- package/templates/ide-templates/opencode/AGENTS.md +120 -0
- package/templates/ide-templates/qwen-code/.qwen/settings.json +8 -0
- package/templates/ide-templates/qwen-code/.qwen/skills/opensquad/SKILL.md +182 -0
- package/templates/ide-templates/qwen-code/QWEN.md +57 -0
- package/templates/ide-templates/trae/.trae/mcp.json +8 -0
- package/templates/ide-templates/trae/.trae/rules/opensquad.md +64 -0
- package/templates/ide-templates/vscode-copilot/.github/copilot-instructions.md +59 -0
- package/templates/ide-templates/vscode-copilot/.github/prompts/opensquad.prompt.md +209 -0
- package/templates/ide-templates/vscode-copilot/.vscode/mcp.json +8 -0
- package/templates/ide-templates/vscode-copilot/.vscode/settings.json +3 -0
- package/templates/package.json +8 -0
- 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.
|