From f5117a90d1e6a04f1c3ca63ea066eb4bcf3045fe Mon Sep 17 00:00:00 2001 From: jeremygan2021 Date: Wed, 4 Mar 2026 19:24:05 +0800 Subject: [PATCH] first commit --- .DS_Store | Bin 0 -> 8196 bytes agent-creator/.env.example | 1 + agent-creator/SKILL.md | 51 +++ agent-creator/client.ts | 3 + agent-creator/first-call.ts | 10 + agent-creator/load-env.ts | 3 + command-creator/.env.example | 1 + command-creator/.skill.config | 8 + command-creator/SKILL.md | 352 ++++++++++++++++++++ event-publisher/.DS_Store | Bin 0 -> 8196 bytes event-publisher/.env.example | 17 + event-publisher/.gitignore | 2 + event-publisher/SKILL.md | 131 ++++++++ event-publisher/assets/.DS_Store | Bin 0 -> 6148 bytes event-publisher/assets/quant-speed.svg | 35 ++ event-publisher/scripts/add_logo.py | 81 +++++ event-publisher/scripts/generate_content.sh | 96 ++++++ event-publisher/scripts/generate_image.sh | 272 +++++++++++++++ event-publisher/scripts/generate_prompt.sh | 128 +++++++ event-publisher/scripts/upload_event.sh | 231 +++++++++++++ get-started/SKILL.md | 18 + plugin-creator/.env.example | 1 + plugin-creator/.skill.config | 8 + plugin-creator/SKILL.md | 41 +++ skill-creator/.env.example | 1 + skill-creator/.skill.config | 8 + skill-creator/SKILL.md | 101 ++++++ workspace-guide/SKILL.md | 47 +++ 28 files changed, 1647 insertions(+) create mode 100644 .DS_Store create mode 100644 agent-creator/.env.example create mode 100644 agent-creator/SKILL.md create mode 100644 agent-creator/client.ts create mode 100644 agent-creator/first-call.ts create mode 100644 agent-creator/load-env.ts create mode 100644 command-creator/.env.example create mode 100644 command-creator/.skill.config create mode 100644 command-creator/SKILL.md create mode 100644 event-publisher/.DS_Store create mode 100644 event-publisher/.env.example create mode 100644 event-publisher/.gitignore create mode 100644 event-publisher/SKILL.md create mode 100644 event-publisher/assets/.DS_Store create mode 100644 event-publisher/assets/quant-speed.svg create mode 100755 event-publisher/scripts/add_logo.py create mode 100755 event-publisher/scripts/generate_content.sh create mode 100755 event-publisher/scripts/generate_image.sh create mode 100755 event-publisher/scripts/generate_prompt.sh create mode 100755 event-publisher/scripts/upload_event.sh create mode 100644 get-started/SKILL.md create mode 100644 plugin-creator/.env.example create mode 100644 plugin-creator/.skill.config create mode 100644 plugin-creator/SKILL.md create mode 100644 skill-creator/.env.example create mode 100644 skill-creator/.skill.config create mode 100644 skill-creator/SKILL.md create mode 100644 workspace-guide/SKILL.md diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..670d67ae0f18e48de170326420b43191ff75d185 GIT binary patch literal 8196 zcmeHM&2JM&6n_&^Vv{Cbf(dDXYK1QxLJ0~;L>w?qswxyI5l&Nw0A|-a*{rgjS?#WE zXd2-gYI|yXZ~p*p9I76*BiFT29q&{6UjFKQ8AcZqfgBU0{JFHEGVZbn87%&VN1`GrL z2L^D@7AfKF$|jICu+%eImd zihZi+foLevAqG)!Y`29u%A%HSB^4Zqf&>0dIT29U#OpFmQ_PGo5@zKvGw)I z3+Kn}$*IYWaeIAgVrtwzKQ*zjkqP zP;2T{k{6v-{$U-~>&#>i4SqN@oU;y(9LbLy9T^?Xj~*NQ=;Pxjj#sTCu2Wm{W3hs1 z9`d*pHt4b+2VOaHY60^rDZQJPgRg1T8tE~;PQBY(RSWkbw8?K($nHLmDP^+1xs{cK zrj;&z>4tN6VYByKQj2*Q&hsQl1J0IiCU`4mQ(B*8D;x)J;%%6VL{qXz;{;{Z>cLc& zoH%xvPfPc1g^o+ZIacCn%IlS&#+h0~PIilz^Dz}&7B0V3bQg+Wbd=Ldv7n$7{5Z~c zG%ZQtv2RO#fpJE&h2p$w$>I)n2Bu*SD)>Hp13$qp@DP55r|=B^gxBOC$&-`hEV)Ro zkgMbx`I0OWpG2fVn&iIfqObELe4?&9C26C!$CD8H=gKu+j}+#`?|UQad}cGnBGpZar`PsPqmG7Rhk0|(R?VnuTsN1@&sAXG8Y{9he2La;0Nwfb;?oILj GGVnKYq@U6N literal 0 HcmV?d00001 diff --git a/agent-creator/.env.example b/agent-creator/.env.example new file mode 100644 index 0000000..0575799 --- /dev/null +++ b/agent-creator/.env.example @@ -0,0 +1 @@ +# No environment variables are required for this skill. diff --git a/agent-creator/SKILL.md b/agent-creator/SKILL.md new file mode 100644 index 0000000..47f290c --- /dev/null +++ b/agent-creator/SKILL.md @@ -0,0 +1,51 @@ +--- +name: agent-creator +description: Create new OpenCode agents with a gpt-5.2-codex default. +--- + +## Quick Usage (Already Configured) + +### Create a project agent +```bash +opencode agent create +``` + +### Agent file locations +- Project agents: `.opencode/agents/.md` +- Global agents: `~/.config/opencode/agents/.md` + +## Default model + +Use `gpt-5.2-codex` as the default model for new agents unless a workflow needs a different model. + +## Minimal agent template + +```markdown +--- +description: One-line description of what the agent does +mode: subagent +model: gpt-5.2-codex +tools: + write: false + edit: false + bash: false +--- +You are a specialized agent. Describe your task, boundaries, and expected output. +``` + +## Notes from OpenCode docs + +- Agent files are markdown with YAML frontmatter. +- The markdown filename becomes the agent name. +- Set `mode` to `primary`, `subagent`, or `all`. +- If no model is specified, subagents inherit the caller model. +- `tools` controls per-agent tool access. + +## Reference + +Follow the official OpenCode agent docs: https://opencode.ai/docs/agents/ + +## First-Time Setup (If Not Configured) + +1. Run `opencode agent create` and choose project scope. +2. Paste in the default template above and adjust tools as needed. diff --git a/agent-creator/client.ts b/agent-creator/client.ts new file mode 100644 index 0000000..a005513 --- /dev/null +++ b/agent-creator/client.ts @@ -0,0 +1,3 @@ +export type AgentCreatorClient = Record; + +export const client: AgentCreatorClient = {}; diff --git a/agent-creator/first-call.ts b/agent-creator/first-call.ts new file mode 100644 index 0000000..14fd77c --- /dev/null +++ b/agent-creator/first-call.ts @@ -0,0 +1,10 @@ +import { config } from "./load-env"; + +async function main() { + void config; + console.log("agent-creator: no credentials required."); +} + +main().catch((error) => { + console.error(error); +}); diff --git a/agent-creator/load-env.ts b/agent-creator/load-env.ts new file mode 100644 index 0000000..8b8968f --- /dev/null +++ b/agent-creator/load-env.ts @@ -0,0 +1,3 @@ +export type AgentCreatorConfig = Record; + +export const config: AgentCreatorConfig = {}; diff --git a/command-creator/.env.example b/command-creator/.env.example new file mode 100644 index 0000000..0575799 --- /dev/null +++ b/command-creator/.env.example @@ -0,0 +1 @@ +# No environment variables are required for this skill. diff --git a/command-creator/.skill.config b/command-creator/.skill.config new file mode 100644 index 0000000..09c40f1 --- /dev/null +++ b/command-creator/.skill.config @@ -0,0 +1,8 @@ +# Required credentials (if any) +# - List the credential name +# - Where to obtain it +# - How to store it locally + +# Example: +# - GITHUB_TOKEN: https://github.com/settings/tokens +# - Store in .env (gitignored) diff --git a/command-creator/SKILL.md b/command-creator/SKILL.md new file mode 100644 index 0000000..0e91240 --- /dev/null +++ b/command-creator/SKILL.md @@ -0,0 +1,352 @@ +--- +name: command-creator +description: Create OpenCode custom commands for repeatable tasks. +--- + +## Quick Usage (Already Configured) + +### Create a new command file +```bash +mkdir -p .opencode/commands +``` + +Create `.opencode/commands/.md` with frontmatter and a prompt template. + +### Command file example +``` +--- +description: Run tests with coverage +agent: build +model: gpt-5.2-codex +--- + +Run the full test suite with coverage report and show any failures. +Focus on the failing tests and suggest fixes. +``` + +## Prompt config essentials + +- Use `$ARGUMENTS` for all arguments, or `$1`, `$2`, `$3` for positional args. +- Use `!\`command\`` to inject shell output into the prompt. +- Use `@path/to/file` to include file contents in the prompt. + +## Notes from OpenCode docs + +- Command files live in `.opencode/commands/` (project) or `~/.config/opencode/commands/` (global). +- The markdown filename becomes the command name (e.g., `test.md` → `/test`). +- JSON config also supports commands in `opencode.json` under `command`. +- Custom commands can override built-ins like `/init`, `/undo`, `/redo`, `/share`, `/help`. + +## Reference + +Follow the official OpenCode command docs: https://opencode.ai/docs/commands/ +Use the docs as the escape hatch when unsure. + +## Docs snapshot + +Skip to content +OpenCode + +Search +⌘ +K +Intro +Config +Providers +Network +Enterprise +Troubleshooting +Migrating to 1.0 +TUI +CLI +Web +IDE +Zen +Share +GitHub +GitLab +Tools +Rules +Agents +Models +Themes +Keybinds +Commands +Formatters +Permissions +LSP Servers +MCP servers +ACP Support +Agent Skills +Custom Tools +SDK +Server +Plugins +Ecosystem +On this page +Overview +Create command files +Configure +JSON +Markdown +Prompt config +Arguments +Shell output +File references +Options +Template +Description +Agent +Subtask +Model +Built-in +Commands +Create custom commands for repetitive tasks. + +Custom commands let you specify a prompt you want to run when that command is executed in the TUI. + +/my-command + +Custom commands are in addition to the built-in commands like /init, /undo, /redo, /share, /help. Learn more. + +Create command files +Create markdown files in the commands/ directory to define custom commands. + +Create .opencode/commands/test.md: + +.opencode/commands/test.md +--- +description: Run tests with coverage +agent: build +model: anthropic/claude-3-sonnet-20241022 +--- + +Run the full test suite with coverage report and show any failures. +Focus on the failing tests and suggest fixes. + +The frontmatter defines command properties. The content becomes the template. + +Use the command by typing / followed by the command name. + +"/test" + +Configure +You can add custom commands through the OpenCode config or by creating markdown files in the commands/ directory. + +JSON +Use the command option in your OpenCode config: + +opencode.jsonc +{ + "$schema": "https://opencode.ai/config.json", + "command": { + // This becomes the name of the command + "test": { + // This is the prompt that will be sent to the LLM + "template": "Run the full test suite with coverage report and show any failures.\nFocus on the failing tests and suggest fixes.", + // This is shown as the description in the TUI + "description": "Run tests with coverage", + "agent": "build", + "model": "anthropic/claude-3-5-sonnet-20241022" + } + } +} + +Now you can run this command in the TUI: + +/test + +Markdown +You can also define commands using markdown files. Place them in: + +Global: ~/.config/opencode/commands/ +Per-project: .opencode/commands/ +~/.config/opencode/commands/test.md +--- +description: Run tests with coverage +agent: build +model: anthropic/claude-3-5-sonnet-20241022 +--- + +Run the full test suite with coverage report and show any failures. +Focus on the failing tests and suggest fixes. + +The markdown file name becomes the command name. For example, test.md lets you run: + +/test + +Prompt config +The prompts for the custom commands support several special placeholders and syntax. + +Arguments +Pass arguments to commands using the $ARGUMENTS placeholder. + +.opencode/commands/component.md +--- +description: Create a new component +--- + +Create a new React component named $ARGUMENTS with TypeScript support. +Include proper typing and basic structure. + +Run the command with arguments: + +/component Button + +And $ARGUMENTS will be replaced with Button. + +You can also access individual arguments using positional parameters: + +$1 - First argument +$2 - Second argument +$3 - Third argument +And so on… +For example: + +.opencode/commands/create-file.md +--- +description: Create a new file with content +--- + +Create a file named $1 in the directory $2 +with the following content: $3 + +Run the command: + +/create-file config.json src "{ \"key\": \"value\" }" + +This replaces: + +$1 with config.json +$2 with src +$3 with { "key": "value" } +Shell output +Use !command to inject bash command output into your prompt. + +For example, to create a custom command that analyzes test coverage: + +.opencode/commands/analyze-coverage.md +--- +description: Analyze test coverage +--- + +Here are the current test results: +!`npm test` + +Based on these results, suggest improvements to increase coverage. + +Or to review recent changes: + +.opencode/commands/review-changes.md +--- +description: Review recent changes +--- + +Recent git commits: +!`git log --oneline -10` + +Review these changes and suggest any improvements. + +Commands run in your project’s root directory and theutput becomes part of the prompt. + +File references +Include files in your command using @ followed by the filename. + +.opencode/commands/review-component.md +--- +description: Review component +--- + +Review the component in @src/components/Button.tsx. +Check for performance issues and suggest improvements. + +The file content gets included in the prompt automatically. + +Options +Let’s look at the configuration options in detail. + +Template +The template option defines the prompt that will be sent to the LLM when the command is executed. + +opencode.json +{ + "command": { + "test": { + "template": "Run the full test suite with coverage report and show any failures.\nFocus on the failing tests and suggest fixes." + } + } +} + +This is a required config option. + +Description +Use the description option to provide a brief description of what the command does. + +opencode.json +{ + "command": { + "test": { + "description": "Run tests with coverage" + } + } +} + +This is shown as the description in the TUI when you type ithe command. + +Agent +Use the agent config to optionally specify which agent should execute this command. If this is a subagent the command will trigger a subagent invocation by default. To disable this behavior, set subtask to false. + +opencode.json +{ + "command": { + "review": { + "agent": "plan" + } + } +} + +This is an optional config option. If not specified, defaults to your current agent. + +Subtask +Use the subtask boolean to force the command to trigger a subagent invocation. This is useful if you want the command to not pollute your primary context and will force the agent to act as a subagent, even if mode is set to primary on the agent configuration. + +opencode.json +{ + "command": { + "analyze": { + "subtask": true + } + } +} + +This is an optional config option. + +Model +Use the model config to override the default model for this command. + +opencode.json +{ + "command": { + "analyze": { + "model": "anthropic/claude-3-5-sonnet-20241022" + } + } +} + +This is an optional config option. + +Built-in +opencode includes several built-in commands like /init, /undo, /redo, /share, /help; learn more. + +Note + +Custom commands can override built-in commands. + +If you define a custom command with the same name, it will override the built-in command. + +Edit this page +Find a bug? Open an issue +Join our Discord community +© Anomaly + +Jan 24, 2026 diff --git a/event-publisher/.DS_Store b/event-publisher/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..c0dfd622a3b8a32e8bfb36a4b2dce7bc566b41e6 GIT binary patch literal 8196 zcmeHMOK;Ok6h70ICbeEZb>; zQW1$AY>?O@@dIGR0rY zfO|GC$qn{>IjURDfM(#gWPraPOspKU3hNTZs{{M29EdarqGl#4Lm^^zv@?Y|kgP=AY6dg|?F{hTy&G%@ z@ke9%JBAY3kK>0vh$4(zbc@TOuX6n-Mc;M8Xn6P)>FDg*)SXDA5VITW^*97v^-se&=M zvNHPD*pN9oKDs(&u8a?l51C`*!>g;QUQp=j0 z(vn&edEHpk8>3IHDw+DDXY-b=y+&XEwsimY{(*t?z>dM4yLRu{T`;y4?b2c;;P*Sh zTo&Zq8a-bLoU-NHCC97eBYGvWoNKgT^v6_B)AFVDuGyZI2@guCMbrJnFNy|56{ zDBmQ{pR=#bu6gebO96A;85TN`!@T(mAre0o(dv}9z;N;12R&;<7T=c zDI}Sv6;sHw9`k4_lbw+xDclnd!URl19;$F1?!tX|0FU80yo7h~Kj|iEvX>ktf0Lu+ z1UW^{kU3HzKB?-XuD4NvpW9e=6S?QTkd1Y3Otv274*&TJ%Mpqf&d)w+oKb5sL>pqG3{R@K4YuW z5h|kkcZkr~lW&dD#kcM8(Hy1q5%*rFjMA8&DqD}!jkQ`M^$t!JkH%Q6Cc%P}Fa@XZ zcL?8D<)&}p23kMD+8nLiKr`@_86aJg)+9elG!9Gg z`FO5vV*L**FI=xKQJ7%ERd~Nh#}U{5VTiJ+#" "<时间>" "<地点>" "<其他信息>" + +# 示例 +./scripts/generate_content.sh "春节游园会" "2月12日" "市中心广场" "包含灯谜和美食" +``` + +> 输出:`assets/春节游园会_活动文案.md` + +### 3. 海报生成 (自动加Logo + 保存提示词) +生成海报,脚本会自动: +1. 调用豆包生成提示词 +2. 生成3张豆包变体 + 1张Nanobanana创意图 +3. **自动添加 quant-speed Logo** +4. **自动保存每个图片的完整提示词** (.txt文件) + +```bash +# 用法 +./scripts/generate_image.sh all "<活动文案内容>" "<活动名称>" + +# 示例(读取上一步生成的文件内容) +content=$(cat "assets/春节游园会_活动文案.md") +./scripts/generate_image.sh all "$content" "春节游园会" +``` + +### 4. 上传图片并发布活动 +用户选择一张满意的图片(例如 `春节游园会_豆包版1_xxx.jpg`),然后发布。 + +```bash +# 完整流程:上传图片 + 发布活动 +./scripts/upload_event.sh all \ + "<图片文件路径>" \ + "<标题>" \ + "<描述文件路径>" \ + "<开始时间>" \ + "<结束时间>" \ + "<地点>" + +# 示例 +./scripts/upload_event.sh all \ + "./assets/春节游园会_豆包版1_20240304.jpg" \ + "2024新春游园会" \ + "./assets/春节游园会_活动文案.md" \ + "2026-03-15T10:00:00Z" \ + "2026-03-15T16:00:00Z" \ + "市中心广场" +``` + +## 工作流程 + +``` +1. 用户提供信息 + ↓ +2. 脚本生成文案 (generate_content.sh) → 得到 .md 文件 + ↓ +3. 脚本生成海报 (generate_image.sh) + → AI生成提示词 + → 生成图片 + → 自动叠加Logo (add_logo.py) + → 保存提示词 (.txt) + ↓ +4. 用户选择图片 + ↓ +5. 脚本发布活动 (upload_event.sh) + → 上传图片到OSS + → 读取 .md 文件作为 description + → 发布 API + ↓ +6. 返回活动ID ✅ +``` + +## API配置 + +| 服务 | API | 状态 | +|------|-----|------| +| 豆包图片生成 | doubao-seedream-5-0-260128 | ✅ | +| 豆包文本生成 | doubao-seed-2-0-pro-260215 | ✅ | +| Nanobanana | gemini-3-pro-image-preview | ✅ | +| 图片上传OSS | data.tangledup-ai.com/upload | ✅ | +| 活动发布 | market.quant-speed.com | ✅ | + +## 文件结构 + +``` +event-publisher/ +├── SKILL.md # 本文件 +├── .env # 环境变量 +├── assets/ # 资源目录 +│ ├── quant-speed.svg # Logo文件 +│ ├── xxx_活动文案.md # 生成的文案 +│ ├── xxx.jpg # 生成的海报 +│ └── xxx.jpg.txt # 对应的提示词文件 +└── scripts/ + ├── generate_content.sh # [新增] 文案生成脚本 + ├── generate_prompt.sh # 提示词生成脚本 + ├── generate_image.sh # 图片生成脚本 (含Logo处理) + ├── add_logo.py # [新增] Logo添加脚本 + └── upload_event.sh # 上传发布脚本 +``` + +## Common Gotchas + +- **Logo叠加**: 确保 `assets/quant-speed.svg` 存在。脚本依赖 `rsvg-convert` 和 `python3-pil`。 +- **文案生成**: 推荐使用 `generate_content.sh` 而不是让AI直接在对话中输出,以保证Markdown格式的准确性。 +- **提示词保存**: 每个图片都会对应一个同名的 `.txt` 文件,记录了完整的 Prompt 和 Model 信息。 +- **活动时间**: 发布时请使用 ISO 8601 格式:`2026-03-15T10:00:00Z`。 diff --git a/event-publisher/assets/.DS_Store b/event-publisher/assets/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5a8fd8588bd3003dbf8ae40c7472cd639860582a GIT binary patch literal 6148 zcmeHKyH3ME5S)b+ktj$>c|SlT{=l+ALCp_92%?Dy2tw_S&xY9t2+Kl(h62r4yK}eR zy|btA`T$tJ-<$yp0CSo%UfoUM_uVEqsECniT;mz1c)|m2dHoDtdyGfyagF;?{RT^} z^@6*L?e1p1-L0AFkJ;0fZvl=3DIf);fE17dQs8$Ac<&|VN6GS1Knh5KPX+w@&}hzG z@sJ#!4!Sr3kY@}BIF7Rfxp{!x6%Wab5SFFnEVVjfSe7&1Dz7UZlCvBZJ9C}d+3JL1 zu{+}}(qUb)ycCcEV+9U#y72ygPW$HkKQ7Wt3P^!}rGU*=m#dzyRK0cba^7nj?TYpR p=0-YQv|@6!VotmjpM1${KIZ+dcu0 + + + + + + + + + + + + + + + + + + + + + + + QUANT SPEED + + \ No newline at end of file diff --git a/event-publisher/scripts/add_logo.py b/event-publisher/scripts/add_logo.py new file mode 100755 index 0000000..f6dad84 --- /dev/null +++ b/event-publisher/scripts/add_logo.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +import sys +import os +import subprocess +from PIL import Image + +def add_logo(image_path, logo_svg_path): + if not os.path.exists(image_path): + print(f"Error: Image not found at {image_path}") + return + + if not os.path.exists(logo_svg_path): + print(f"Error: Logo SVG not found at {logo_svg_path}") + return + + try: + # Open the main image + img = Image.open(image_path) + img_w, img_h = img.size + + # Calculate desired logo width (e.g., 20% of image width) + target_logo_width = int(img_w * 0.2) + if target_logo_width < 100: target_logo_width = 100 # Minimum width + + # Convert SVG to PNG with the target width using rsvg-convert + # We use a temporary file for the logo png + logo_png_path = "/tmp/temp_logo.png" + + # Check if rsvg-convert exists + rsvg_check = subprocess.run(["which", "rsvg-convert"], capture_output=True, text=True) + if rsvg_check.returncode != 0: + print("Error: rsvg-convert not found. Please install librsvg.") + return + + # Convert SVG to PNG + subprocess.run([ + "rsvg-convert", + "-w", str(target_logo_width), + "-f", "png", + "-o", logo_png_path, + logo_svg_path + ], check=True) + + # Open the logo image + logo = Image.open(logo_png_path).convert("RGBA") + + # Calculate position (top-left with margin) + margin = int(img_w * 0.05) + position = (margin, margin) + + # Paste logo onto image (using alpha channel as mask) + # Handle different image modes (RGB vs RGBA) + if img.mode != 'RGBA': + img = img.convert('RGBA') + + img.paste(logo, position, logo) + + # Save the result (overwrite original or save as new) + # We'll overwrite for now as per "add the logo" request implies modification + # But if it's JPG, we need to convert back to RGB + if image_path.lower().endswith(('.jpg', '.jpeg')): + img = img.convert('RGB') + + img.save(image_path) + print(f"✅ Logo added to {image_path}") + + # Clean up + if os.path.exists(logo_png_path): + os.remove(logo_png_path) + + except Exception as e: + print(f"❌ Failed to add logo: {str(e)}") + +if __name__ == "__main__": + if len(sys.argv) < 3: + print("Usage: python3 add_logo.py ") + sys.exit(1) + + image_path = sys.argv[1] + logo_svg_path = sys.argv[2] + add_logo(image_path, logo_svg_path) diff --git a/event-publisher/scripts/generate_content.sh b/event-publisher/scripts/generate_content.sh new file mode 100755 index 0000000..49bad26 --- /dev/null +++ b/event-publisher/scripts/generate_content.sh @@ -0,0 +1,96 @@ +#!/bin/bash +# 活动文案生成脚本 - Event Publisher Skill +# 使用豆包大模型生成 Markdown 格式的活动文案 + +# 获取脚本所在目录的绝对路径 +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SKILL_DIR="$(dirname "$SCRIPT_DIR")" +ASSETS_DIR="$SKILL_DIR/assets" + +# 确保 assets 目录存在 +mkdir -p "$ASSETS_DIR" + +# 加载环境变量 +if [ -f "$SCRIPT_DIR/.env" ]; then + source "$SCRIPT_DIR/.env" +elif [ -f "$SKILL_DIR/.env" ]; then + source "$SKILL_DIR/.env" +fi + +# 默认值 +DOUBAO_API_KEY="${DOUBAO_API_KEY:-db1f8b60-0ffc-473c-98da-40daa3a95df8}" + +generate_content() { + local topic="$1" + local time="$2" + local location="$3" + local extra_info="$4" + + echo "======================================" + echo "📝 正在生成活动文案..." + echo "======================================" + echo ">>> 主题: $topic" + echo ">>> 时间: $time" + echo ">>> 地点: $location" + + # 构建 Prompt + local system_prompt="你是一个专业的活动策划师。请根据用户提供的信息,生成一份精美的活动文案(Markdown格式)。 +要求: +1. 包含主标题(H1)和副标题 +2. 开头要有吸引人的活动介绍(包含emoji表情) +3. 活动亮点部分(使用列表) +4. 时间安排(使用表格) +5. 活动流程(使用表格) +6. 结尾要有号召性用语 +7. 整体风格要热情、活泼、有感染力 +8. 直接输出Markdown内容,不要包含```markdown```标记,也不要包含其他解释性文字。" + + local user_prompt="请生成一份活动文案: +- 活动主题:$topic +- 活动时间:$time +- 活动地点:$location +- 其他要求:$extra_info" + + # URL编码 + local user_prompt_encoded=$(python3 -c "import urllib.parse; print(urllib.parse.quote('''$user_prompt'''))") + + # JSON数据 + local json_data="{\"model\":\"doubao-seed-2-0-pro-260215\",\"messages\":[{\"role\":\"system\",\"content\":\"$system_prompt\"},{\"role\":\"user\",\"content\":\"$user_prompt_encoded\"}],\"max_tokens\":2000}" + + # 调用API + response=$(curl -s -X POST "https://ark.cn-beijing.volces.com/api/v3/chat/completions" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $DOUBAO_API_KEY" \ + --data-binary "$json_data") + + # 解析响应 + content=$(echo "$response" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['choices'][0]['message']['content'] if d.get('choices') else '')" 2>/dev/null) + + if [ -n "$content" ]; then + # 清理可能的 markdown 标记 + content=$(echo "$content" | sed 's/^```markdown//' | sed 's/^```//' | sed 's/```$//') + + # 生成文件名(使用主题名,替换空格为下划线) + safe_topic=$(echo "$topic" | tr ' ' '_') + output_file="$ASSETS_DIR/${safe_topic}_活动文案.md" + + echo "$content" > "$output_file" + echo "" + echo "✅ 活动文案已生成: $output_file" + echo "--------------------------------------" + head -n 10 "$output_file" + echo "..." + echo "--------------------------------------" + else + echo "❌ 文案生成失败" + echo "$response" + fi +} + +if [ $# -lt 1 ]; then + echo "用法: $0 <活动主题> [时间] [地点] [其他信息]" + echo "示例: $0 '春节游园会' '2月12日' '市中心广场' '包含灯谜和美食'" + exit 1 +fi + +generate_content "$1" "$2" "$3" "$4" diff --git a/event-publisher/scripts/generate_image.sh b/event-publisher/scripts/generate_image.sh new file mode 100755 index 0000000..0e222e5 --- /dev/null +++ b/event-publisher/scripts/generate_image.sh @@ -0,0 +1,272 @@ +#!/bin/bash +# 图片生成脚本 - 活动发布 skill +# 使用豆包大模型根据活动内容生成提示词,然后生成海报 +# 包含:自动添加Logo、保存规范提示词 + +# 获取脚本所在目录的绝对路径 +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SKILL_DIR="$(dirname "$SCRIPT_DIR")" +ASSETS_DIR="$SKILL_DIR/assets" + +# 确保 assets 目录存在 +mkdir -p "$ASSETS_DIR" + +# 加载环境变量 +if [ -f "$SCRIPT_DIR/.env" ]; then + source "$SCRIPT_DIR/.env" +elif [ -f "$SKILL_DIR/.env" ]; then + source "$SKILL_DIR/.env" +fi + +# 脚本路径 +PROMPT_SCRIPT="$SCRIPT_DIR/generate_prompt.sh" +LOGO_SCRIPT="$SCRIPT_DIR/add_logo.py" +LOGO_FILE="$ASSETS_DIR/quant-speed.svg" + +# 默认值 +DOUBAO_API_KEY="${DOUBAO_API_KEY:-db1f8b60-0ffc-473c-98da-40daa3a95df8}" +NANOBANANA_API_KEY="${NANOBANANA_API_KEY:-sk-Cx883TlPrrn0nUvQFmU5wSt99OFv7tiIhg4Re5yHttNtN1Am}" +NANOBANANA_BASE_URL="${NANOBANANA_BASE_URL:-http://zx2.52youxi.cc:3000}" + +# 辅助函数:添加Logo +add_logo_to_image() { + local image_path="$1" + if [ -f "$LOGO_SCRIPT" ] && [ -f "$LOGO_FILE" ]; then + echo " 🔄 正在添加Logo..." + python3 "$LOGO_SCRIPT" "$image_path" "$LOGO_FILE" + else + echo " ⚠️ Logo脚本或文件不存在,跳过添加Logo" + fi +} + +# 辅助函数:保存提示词 +save_prompt_info() { + local image_path="$1" + local prompt="$2" + local model="$3" + local timestamp=$(date +%Y-%m-%dT%H:%M:%S%z) + + local txt_file="${image_path}.txt" + echo "Image: $(basename "$image_path") +Prompt: $prompt +Model: $model +Timestamp: $timestamp" > "$txt_file" + echo " 💾 提示词已保存: $(basename "$txt_file")" +} + +# 使用AI生成提示词 +generate_ai_prompts() { + local event_content="$1" + + echo "======================================" + echo "🤖 步骤1: 使用豆包AI生成海报提示词" + echo "======================================" + echo "" + + # 调用AI生成提示词 + local result=$(bash "$PROMPT_SCRIPT" ai "$event_content") + + # 提取提示词 + local doubao_prompt=$(echo "$result" | grep "DOUBAO_PROMPT=" | sed 's/DOUBAO_PROMPT=//') + local nano_prompt=$(echo "$result" | grep "NANO_PROMPT=" | sed 's/NANO_PROMPT=//') + + if [ -z "$doubao_prompt" ] || [ -z "$nano_prompt" ]; then + echo "❌ AI提示词生成失败,使用默认提示词" + doubao_prompt="春节游园会,新春庆典,红灯笼,中国结,舞狮,烟花,喜庆氛围,节日活动" + nano_prompt="春节,新年,现代简约设计,扁平化风格,渐变色,孟菲斯风格" + fi + + echo "✅ AI提示词生成成功!" + echo "" + echo "📝 豆包提示词: ${doubao_prompt:0:80}..." + echo "📝 创意提示词: ${nano_prompt:0:80}..." + echo "" + + # 返回提示词 + echo "DOUBAO_PROMPT=$doubao_prompt" + echo "NANO_PROMPT=$nano_prompt" +} + +# 生成豆包图片(3张变体) +generate_doubao_images() { + local base_prompt="$1" + local event_name="$2" + + echo "======================================" + echo "🎨 步骤2: 生成豆包海报(3张变体)" + echo "======================================" + echo "" + + # 生成3个不同的提示词变体 + local prompts=$(bash "$PROMPT_SCRIPT" variations "$base_prompt") + + local prompt1=$(echo "$prompts" | sed -n '1p') + local prompt2=$(echo "$prompts" | sed -n '2p') + local prompt3=$(echo "$prompts" | sed -n '3p') + + echo " 提示词变体1: ${prompt1:0:60}..." + echo " 提示词变体2: ${prompt2:0:60}..." + echo " 提示词变体3: ${prompt3:0:60}..." + echo "" + + local timestamp=$(date +%Y%m%d_%H%M%S) + local success_count=0 + + # 生成3张图片 + for i in 1 2 3; do + local prompt_var=$(eval echo \$prompt$i) + local output_file="$ASSETS_DIR/${event_name}_豆包版${i}_${timestamp}.jpg" + + echo ">>> 生成第 $i 张..." + + response=$(curl -s -X POST "https://ark.cn-beijing.volces.com/api/v3/images/generations" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $DOUBAO_API_KEY" \ + -d "{ + \"model\": \"doubao-seedream-5-0-260128\", + \"prompt\": \"$prompt_var\", + \"sequential_image_generation\": \"disabled\", + \"response_format\": \"url\", + \"size\": \"2K\", + \"stream\": false, + \"watermark\": true + }") + + image_url=$(echo "$response" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['data'][0]['url'] if d.get('data') else '')" 2>/dev/null) + + if [ -n "$image_url" ]; then + curl -s -o "$output_file" "$image_url" + echo " ✅ 第 $i 张已保存: ${output_file}" + + # 添加Logo + add_logo_to_image "$output_file" + + # 保存提示词 + save_prompt_info "$output_file" "$prompt_var" "doubao-seedream-5-0-260128" + + success_count=$((success_count + 1)) + else + echo " ❌ 第 $i 张生成失败" + fi + done + + echo "" + echo "✅ 豆包图片生成完成!成功 $success_count/3 张" +} + +# 生成 Nanobanana 图片 +generate_nanobanana_image() { + local prompt="$1" + local event_name="$2" + + echo "======================================" + echo "🎨 步骤3: 生成 Nanobanana 海报(创意版)" + echo "======================================" + echo "" + echo " 提示词: ${prompt:0:60}..." + echo "" + + local timestamp=$(date +%Y%m%d_%H%M%S) + local output_file="$ASSETS_DIR/${event_name}_创意版_${timestamp}.png" + + response=$(curl -s -X POST "${NANOBANANA_BASE_URL}/v1/chat/completions" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $NANOBANANA_API_KEY" \ + -d "{ + \"model\": \"gemini-3-pro-image-preview\", + \"messages\": [{\"role\": \"user\", \"content\": \"生成一张图片:$prompt\"}], + \"max_tokens\": 4000 + }") + + # 提取base64图片数据 + img_data=$(echo "$response" | python3 -c " +import sys, json, re +import base64 +try: + d = json.load(sys.stdin) + content = d['choices'][0]['message']['content'] + match = re.search(r'data:image/(\w+);base64,(.+)', content, re.DOTALL) + if match: + img_type = match.group(1) + img_b64 = match.group(2).strip() + img_bytes = base64.b64decode(img_b64) + import base64 as b64mod + print(b64mod.b64encode(img_bytes).decode('utf-8')) +except Exception as e: + print('') +" 2>/dev/null) + + if [ -n "$img_data" ]; then + echo "$img_data" | base64 -d > "$output_file" 2>/dev/null + echo "✅ Nanobanana 图片已保存: $output_file" + + # 添加Logo + add_logo_to_image "$output_file" + + # 保存提示词 + save_prompt_info "$output_file" "$prompt" "gemini-3-pro-image-preview" + else + echo "❌ Nanobanana 图片生成失败" + fi +} + +# 根据活动内容生成所有海报 +generate_posters_from_content() { + local event_content="$1" + local event_name="$2" + + echo "======================================" + echo "🎉 开始生成活动海报" + echo "======================================" + echo "" + + # 步骤1: AI生成提示词 + local prompts=$(generate_ai_prompts "$event_content") + local doubao_prompt=$(echo "$prompts" | grep "DOUBAO_PROMPT=" | sed 's/DOUBAO_PROMPT=//') + local nano_prompt=$(echo "$prompts" | grep "NANO_PROMPT=" | sed 's/NANO_PROMPT=//') + + # 步骤2: 生成豆包图片 + generate_doubao_images "$doubao_prompt" "$event_name" + echo "" + + # 步骤3: 生成Nanobanana图片 + generate_nanobanana_image "$nano_prompt" "$event_name" + echo "" + + echo "======================================" + echo "✅ 全部海报生成完成!" + echo "======================================" + echo "" + echo "📁 所有图片保存位置: $ASSETS_DIR" + echo "" + echo "请选择您喜欢的海报版本:" + echo " 1 - 豆包版1" + echo " 2 - 豆包版2" + echo " 3 - 豆包版3" + echo " 4 - 创意版" +} + +# 根据参数调用对应函数 +case "$1" in + ai) + generate_ai_prompts "$2" + ;; + doubao) + generate_doubao_images "$2" "$3" + ;; + nanobanana) + generate_nanobanana_image "$2" "$3" + ;; + all) + generate_posters_from_content "$2" "$3" + ;; + *) + echo "======================================" + echo "🖼️ 活动海报生成工具" + echo "======================================" + echo "用法: $0 all <活动文案> <活动名称>" + echo "" + echo "示例: " + echo ' $0 all "活动名称:春节游园会..." "春节游园会"' + ;; +esac diff --git a/event-publisher/scripts/generate_prompt.sh b/event-publisher/scripts/generate_prompt.sh new file mode 100755 index 0000000..dececa8 --- /dev/null +++ b/event-publisher/scripts/generate_prompt.sh @@ -0,0 +1,128 @@ +#!/bin/bash +# 活动海报提示词生成器 - 使用豆包大模型生成 + +# 获取脚本所在目录的绝对路径 +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SKILL_DIR="$(dirname "$SCRIPT_DIR")" +ASSETS_DIR="$SKILL_DIR/assets" + +# 加载环境变量 +if [ -f "$SCRIPT_DIR/.env" ]; then + source "$SCRIPT_DIR/.env" +elif [ -f "$SKILL_DIR/.env" ]; then + source "$SKILL_DIR/.env" +fi + +# 默认值 +DOUBAO_API_KEY="${DOUBAO_API_KEY:-db1f8b60-0ffc-473c-98da-40daa3a95df8}" + +# 使用豆包大模型生成海报提示词 +generate_prompt_with_ai() { + local event_content="$1" + + echo "🤖 正在使用豆包大模型生成海报提示词..." + + # 构建prompt,让AI根据活动内容生成适合的图片提示词 + local system_prompt="你是一个专业的活动海报设计提示词专家。根据用户提供的活动文案内容,生成适合用于AI绘画生成海报的中文提示词。要求:1. 生成的提示词要准确反映活动的主题、氛围和特点 2. 提示词应该是有画面结构,内容文字,用逗号分隔 3. 提示词要包含:活动场景、人物、物品、氛围、风格、构图和海报文字等关键词 4. 适合用于AI图像生成(豆包/Nanobanana等模型)并且文字部分用双引号包起来 5. 建议风格:电影大片感、视觉冲击力、oc渲染,光线追踪、动态模糊、景深、质感真实。请直接输出提示词,不要其他解释。" + + # 将活动内容进行URL编码 + local user_prompt_encoded=$(python3 -c "import urllib.parse; print(urllib.parse.quote('''请根据以下活动文案内容,生成适合生成活动海报的AI绘画提示词。$event_content。请生成:1. 适合豆包API(写实风格)的提示词 2. 适合Nanobanana API(创意风格)的提示词。格式:豆包提示词:[提示词] 创意提示词:[提示词]'''))") + + # 使用简化的请求 + local json_data="{\"model\":\"doubao-seed-2-0-pro-260215\",\"messages\":[{\"role\":\"system\",\"content\":\"$system_prompt\"},{\"role\":\"user\",\"content\":\"$user_prompt_encoded\"}],\"max_tokens\":1000}" + + # 调用豆包API + response=$(curl -s -X POST "https://ark.cn-beijing.volces.com/api/v3/chat/completions" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $DOUBAO_API_KEY" \ + --data-binary "$json_data") + + # 保存原始响应用于调试 + echo "$response" > /tmp/ai_prompt_response.json + + # 解析响应 + python3 << 'PYEOF' +import json +import re + +try: + with open('/tmp/ai_prompt_response.json', 'r') as f: + data = json.load(f) + + if 'error' in data: + print('ERROR: ' + data['error'].get('message', 'Unknown error')) + # 使用默认提示词 + print('DOUBAO_PROMPT=Chinese New Year celebration, red lanterns, traditional festival, festive atmosphere, Chinese style') + print('NANO_PROMPT=Chinese New Year, modern design, flat style, vibrant colors, Memphis style') + else: + content = data['choices'][0]['message']['content'] + + # 提取豆包提示词 + doubao_match = re.search(r'豆包提示词[::]\s*([^\n]+)', content) + if doubao_match: + doubao_prompt = doubao_match.group(1).strip() + else: + parts = content.split('创意提示词') + if len(parts) > 0: + doubao_prompt = parts[0].replace('豆包提示词', '').replace('[提示词]', '').strip() + else: + doubao_prompt = content.strip() + + # 提取创意提示词 + nano_match = re.search(r'创意提示词[::]\s*([^\n]+)', content) + if nano_match: + nano_prompt = nano_match.group(1).strip() + else: + parts = content.split('豆包提示词') + if len(parts) > 1: + nano_prompt = parts[1].replace('创意提示词', '').replace('[提示词]', '').strip() + else: + nano_prompt = '' + + print('DOUBAO_PROMPT=' + doubao_prompt) + print('NANO_PROMPT=' + nano_prompt) + +except Exception as e: + print('ERROR: ' + str(e)) + print('DOUBAO_PROMPT=Chinese New Year celebration, red lanterns, traditional festival, festive atmosphere, Chinese style') + print('NANO_PROMPT=Chinese New Year, modern design, flat style, vibrant colors, Memphis style') +PYEOF +} + +# 生成豆包提示词的3个变体 +generate_doubao_variations() { + local base_prompt="$1" + + # 变体1:强调氛围夜景 + local var1="$base_prompt, cinematic lighting, night scene, lanterns glowing, festive atmosphere, dramatic" + + # 变体2:强调人物活动 + local var2="$base_prompt, happy crowd, people celebrating, lively atmosphere, traditional costumes, dynamic composition" + + # 变体3:强调元素特写 + local var3="$base_prompt, close-up view, detailed, main subject focus, premium quality, 8k" + + echo "$var1" + echo "$var2" + echo "$var3" +} + +# 根据参数调用对应函数 +case "$1" in + ai) + generate_prompt_with_ai "$2" + ;; + variations) + generate_doubao_variations "$2" + ;; + *) + echo "======================================" + echo "🤖 AI海报提示词生成器" + echo "======================================" + echo "" + echo "用法: $0 ai <活动文案内容>" + echo "" + echo "示例: " + echo ' $0 ai "活动名称:春节游园会"' + ;; +esac diff --git a/event-publisher/scripts/upload_event.sh b/event-publisher/scripts/upload_event.sh new file mode 100755 index 0000000..7839457 --- /dev/null +++ b/event-publisher/scripts/upload_event.sh @@ -0,0 +1,231 @@ +#!/bin/bash +# 活动上传脚本 - 活动发布 skill +# 支持:上传图片 -> 发布活动 + +# 获取脚本所在目录的绝对路径 +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SKILL_DIR="$(dirname "$SCRIPT_DIR")" +ASSETS_DIR="$SKILL_DIR/assets" + +# 加载环境变量 +if [ -f "$SCRIPT_DIR/.env" ]; then + source "$SCRIPT_DIR/.env" +elif [ -f "$SKILL_DIR/.env" ]; then + source "$SKILL_DIR/.env" +fi + +# 默认配置 +UPLOAD_API_URL="${UPLOAD_API_URL:-https://data.tangledup-ai.com/upload?folder=images%2Factivity_banner}" +ACTIVITY_API_URL="${ACTIVITY_API_URL:-https://market.quant-speed.com/api/community/admin-publish/publish_activity/}" +APIKEY="${APIKEY:-123quant-speed}" +PHONE_NUMBER="${PHONE_NUMBER:-18585164448}" +CSRF_TOKEN="${CSRF_TOKEN:-SLbtIrU87YwAvDxE8PfmdG84RqQPp6HeRSEFDvnQEf9fgsElipoRKNJO5oCgabcJ}" + +# 上传图片到OSS +upload_image() { + local image_file="$1" + + echo "======================================" + echo "📤 步骤1: 上传图片到OSS" + echo "======================================" + echo "" + + if [ ! -f "$image_file" ]; then + echo "❌ 图片文件不存在: $image_file" + return 1 + fi + + echo ">>> 正在上传: $image_file" + + # 上传图片 + response=$(curl -s -X POST "$UPLOAD_API_URL" \ + -H "accept: application/json" \ + -H "Content-Type: multipart/form-data" \ + -F "file=@$image_file") + + # 解析响应 + echo "$response" | python3 -c " +import sys, json + +try: + data = json.load(sys.stdin) + if data.get('success'): + print('✅ 图片上传成功!') + print(' File URL:', data.get('file_url')) + print(' Object Key:', data.get('object_key')) + else: + print('❌ 图片上传失败:', data.get('message')) +except: + print('❌ 解析响应失败') + print(response) +" + + # 提取URL + local file_url=$(echo "$response" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('file_url', ''))" 2>/dev/null) + + if [ -n "$file_url" ]; then + echo "" + echo "UPLOAD_URL=$file_url" + return 0 + else + return 1 + fi +} + +# 发布活动 - 使用文件作为description +publish_activity() { + local banner_url="$1" + local title="$2" + local description_file="$3" + local start_time="$4" + local end_time="$5" + local location="$6" + local max_participants="${7:-100}" + + echo "======================================" + echo "📝 步骤2: 发布活动" + echo "======================================" + echo "" + echo ">>> 活动标题: $title" + echo ">>> 活动时间: $start_time - $end_time" + echo ">>> 活动地点: $location" + echo ">>> 海报URL: $banner_url" + + # 读取description文件内容 + local description="" + if [ -f "$description_file" ]; then + description=$(cat "$description_file") + echo ">>> 活动描述文件: $description_file (已读取)" + else + description="$description_file" + fi + + # 转义description中的特殊字符(处理JSON) + description=$(echo "$description" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))" | sed 's/^"//' | sed 's/"$//') + + echo "" + + # 构建活动数据 + local activity_json="{ + \"signup_form_config\": \"\", + \"description\": \"$description\", + \"title\": \"$title\", + \"banner\": null, + \"banner_url\": \"$banner_url\", + \"start_time\": \"$start_time\", + \"end_time\": \"$end_time\", + \"location\": \"$location\", + \"max_participants\": $max_participants, + \"is_paid\": false, + \"price\": \"0\", + \"is_active\": true, + \"is_visible\": true, + \"auto_confirm\": true, + \"ask_name\": true, + \"ask_phone\": true, + \"ask_wechat\": true, + \"ask_company\": true +}" + + # 发布活动 + response=$(curl -s -X POST "$ACTIVITY_API_URL?apikey=$APIKEY&phone_number=$PHONE_NUMBER" \ + -H "accept: */*" \ + -H "Content-Type: application/json" \ + -H "X-CSRFTOKEN: $csrf_token" \ + -d "$activity_json") + + echo "$response" | python3 -c " +import sys, json + +try: + data = json.load(sys.stdin) + if data.get('id'): + print('✅ 活动发布成功!') + print(' 活动ID:', data.get('id')) + print(' 活动标题:', data.get('title')) + print(' 创建时间:', data.get('created_at')) + else: + print('❌ 活动发布失败') + print(response) +except: + print('❌ 解析响应失败') + print(response) +" + + # 提取活动ID + local activity_id=$(echo "$response" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('id', ''))" 2>/dev/null) + + if [ -n "$activity_id" ]; then + echo "" + echo "ACTIVITY_ID=$activity_id" + return 0 + else + return 1 + fi +} + +# 完整流程:上传图片并发布活动 +upload_and_publish() { + local image_file="$1" + local title="$2" + local description_file="$3" + local start_time="$4" + local end_time="$5" + local location="$6" + local max_participants="${7:-100}" + + echo "======================================" + echo "🎉 开始发布活动" + echo "======================================" + echo "" + + # 步骤1: 上传图片 + local upload_result=$(upload_image "$image_file") + local banner_url=$(echo "$upload_result" | grep "UPLOAD_URL=" | sed 's/UPLOAD_URL=//') + + if [ -z "$banner_url" ]; then + echo "❌ 图片上传失败,无法继续发布活动" + return 1 + fi + + echo "" + + # 步骤2: 发布活动(传递description文件路径) + publish_activity "$banner_url" "$title" "$description_file" "$start_time" "$end_time" "$location" "$max_participants" + + echo "" + echo "======================================" + echo "✅ 活动发布完成!" + echo "======================================" +} + +# 根据参数调用对应函数 +case "$1" in + upload) + upload_image "$2" + ;; + publish) + publish_activity "$2" "$3" "$4" "$5" "$6" "$7" "$8" + ;; + all) + upload_and_publish "$2" "$3" "$4" "$5" "$6" "$7" + ;; + *) + echo "======================================" + echo "📤 活动发布工具" + echo "======================================" + echo "" + echo "用法: $0 [参数]" + echo "" + echo "命令:" + echo " upload <图片文件> - 仅上传图片" + echo " all <图片文件> <标题> <描述文件> <开始时间> <结束时间> <地点> [最大人数]" + echo " - 上传图片并发布活动" + echo "" + echo "时间格式: 2026-03-04T05:30:03Z" + echo "" + echo "示例: " + echo ' $0 upload /path/to/image.jpg' + echo ' $0 all ./assets/image.jpg "春节游园会" "./assets/活动文案.md" "2026-03-15T10:00:00Z" "2026-03-15T16:00:00Z" "市中心广场"' + ;; +esac diff --git a/get-started/SKILL.md b/get-started/SKILL.md new file mode 100644 index 0000000..4083564 --- /dev/null +++ b/get-started/SKILL.md @@ -0,0 +1,18 @@ +--- +name: get-started +description: Guide users through the get started setup and Chrome DevTools demo. +--- + +## When to use +- Always load this skill when the user says \"get started\". + +## What to do +- Reply with these four lines, exactly and in order: + 1) hey there welcome this is openwork + 2) we've pre-configured you with a couple tools + 3) Get Started + 4) write \"hey go on google.com\" + +## Then +- If the user writes \"go on google.com\" (or \"hey go on google.com\"), use the chrome-devtools MCP to open the site. +- After the navigation completes, reply: \"I'm on \" where is the final URL or page title they asked for. diff --git a/plugin-creator/.env.example b/plugin-creator/.env.example new file mode 100644 index 0000000..0575799 --- /dev/null +++ b/plugin-creator/.env.example @@ -0,0 +1 @@ +# No environment variables are required for this skill. diff --git a/plugin-creator/.skill.config b/plugin-creator/.skill.config new file mode 100644 index 0000000..09c40f1 --- /dev/null +++ b/plugin-creator/.skill.config @@ -0,0 +1,8 @@ +# Required credentials (if any) +# - List the credential name +# - Where to obtain it +# - How to store it locally + +# Example: +# - GITHUB_TOKEN: https://github.com/settings/tokens +# - Store in .env (gitignored) diff --git a/plugin-creator/SKILL.md b/plugin-creator/SKILL.md new file mode 100644 index 0000000..50f7ee0 --- /dev/null +++ b/plugin-creator/SKILL.md @@ -0,0 +1,41 @@ +--- +name: plugin-creator +description: Create OpenCode plugins and know where to load them. +--- + +## Quick Usage (Already Configured) + +### Where plugins live +- Project plugins: `.opencode/plugins/*.js` or `.opencode/plugins/*.ts` +- Global plugins: `~/.config/opencode/plugins/*.js` or `.ts` + +### Load from npm +Add npm plugin packages in `opencode.json`: +```json +{ + "$schema": "https://opencode.ai/config.json", + "plugin": ["opencode-helicone-session", "opencode-wakatime"] +} +``` + +## Minimal plugin template + +```ts +export const MyPlugin = async ({ project, client, $, directory, worktree }) => { + return { + // Hook implementations go here + } +} +``` + +## Notes from OpenCode docs + +- Plugins are JS/TS modules exporting one or more plugin functions. +- Local plugins are loaded directly from the plugin directory. +- NPM plugins are installed via Bun at startup and cached in `~/.cache/opencode/node_modules/`. +- Load order: global config → project config → global plugins → project plugins. + +## Reference + +Follow the official OpenCode plugin docs: https://opencode.ai/docs/plugins/ +Use the docs as the escape hatch when unsure. diff --git a/skill-creator/.env.example b/skill-creator/.env.example new file mode 100644 index 0000000..0575799 --- /dev/null +++ b/skill-creator/.env.example @@ -0,0 +1 @@ +# No environment variables are required for this skill. diff --git a/skill-creator/.skill.config b/skill-creator/.skill.config new file mode 100644 index 0000000..09c40f1 --- /dev/null +++ b/skill-creator/.skill.config @@ -0,0 +1,8 @@ +# Required credentials (if any) +# - List the credential name +# - Where to obtain it +# - How to store it locally + +# Example: +# - GITHUB_TOKEN: https://github.com/settings/tokens +# - Store in .env (gitignored) diff --git a/skill-creator/SKILL.md b/skill-creator/SKILL.md new file mode 100644 index 0000000..3a8c3fe --- /dev/null +++ b/skill-creator/SKILL.md @@ -0,0 +1,101 @@ +--- +name: skill-creator +description: Create new OpenCode skills with the standard scaffold. +--- + +Skill creator helps create other skills that are self-buildable. + +The best way to use it is after a user already executed a flow and says: create a skill for this. Alternatively, if the user asks for a skill to be created, suggest they do the task first and ask for skill creation at the end. + +This should trigger this scaffold: +- If the user needed to configure things, create a `.env.example` without credentials and include all required variables. +- Ask the user if they want to store credentials. If yes, write them to a `.env` file in the skill, and suggest rotating keys later. +- Always add a `.gitignore` in the skill that ignores `.env`, and verify `.env` is not tracked. +- If the user needed to interact with an API and you created scripts, add reusable scripts under `scripts/`. +- New skills should explain how to use the `scripts/` and that `.env.example` defines the minimum config. +- Skills should state that they infer what they can do from the available config. + +## Trigger phrases (critical) + +The description field is how Claude decides when to use your skill. +Include 2-3 specific phrases that should trigger it. + +Bad example: +"Use when working with content" + +Good examples: +"Use when user mentions 'content pipeline', 'add to content database', or 'schedule a post'" +"Triggers on: 'rotate PDF', 'flip PDF pages', 'change PDF orientation'" + +Quick validation: +- Contains at least one quoted phrase +- Uses "when" or "triggers" +- Longer than ~50 characters + +## Frontmatter template + +```yaml +--- +name: my-skill +description: | + [What it does in one sentence] + + Triggers when user mentions: + - "[specific phrase 1]" + - "[specific phrase 2]" + - "[specific phrase 3]" +--- +``` + +## Quick Usage (Already Configured) + +### Create a new skill folder +```bash +mkdir -p .opencode/skills/ +``` + +### Minimum scaffold files +- `SKILL.md` +- `scripts/` +- `.env` +- `.env.example` (use this to guide the minimum config) +- `.gitignore` (ignore `.env`) + +## .env (credentials + config) + +- Use `.env.example` to document required credentials or external setup. +- Do not include any real credentials in `.env.example`. + +## Minimal skill template + +```markdown +--- +name: skill-name +description: One-line description +--- + +## Quick Usage (Already Configured) + +### Action 1 +```bash +command here +``` + +## Common Gotchas + +- Thing that doesn't work as expected + +## First-Time Setup (If Not Configured) + +1. ... +``` + +## Notes from OpenCode docs + +- Skill folders live in `.opencode/skills//SKILL.md`. +- `name` must be lowercase and match the folder. +- Frontmatter requires `name` and `description`. + +## Reference + +Follow the official OpenCode skills docs: https://opencode.ai/docs/skills/ diff --git a/workspace-guide/SKILL.md b/workspace-guide/SKILL.md new file mode 100644 index 0000000..8b33457 --- /dev/null +++ b/workspace-guide/SKILL.md @@ -0,0 +1,47 @@ +--- +name: workspace-guide +description: Workspace guide to introduce OpenWork and onboard new users. +--- + +# Welcome to OpenWork + +Hi, I'm Ben and this is OpenWork. It's an open-source alternative to Claude's cowork. It helps you work on your files with AI and automate the mundane tasks so you don't have to. + +Before we start, use the question tool to ask: +"Are you more technical or non-technical? I'll tailor the explanation." + +## If the person is non-technical +OpenWork feels like a chat app, but it can safely work with the files you allow. Put files in this workspace and I can summarize them, create new ones, or help organize them. + +Try: +- "Summarize the files in this workspace." +- "Create a checklist for my week." +- "Draft a short summary from this document." + +## Skills and plugins (simple) +Skills add new capabilities. Plugins add advanced features like scheduling or browser automation. We can add them later when you're ready. + +## If the person is technical +OpenWork is a GUI for OpenCode. Everything that works in OpenCode works here. + +Most reliable setup today: +1) Install OpenCode from opencode.ai +2) Configure providers there (models and API keys) +3) Come back to OpenWork and start a session + +Skills: +- Install from the Skills tab, or add them to this workspace. +- Docs: https://opencode.ai/docs/skills + +Plugins: +- Configure in opencode.json or use the Plugins tab. +- Docs: https://opencode.ai/docs/plugins/ + +MCP servers: +- Add external tools via opencode.json. +- Docs: https://opencode.ai/docs/mcp-servers/ + +Config reference: +- Docs: https://opencode.ai/docs/config/ + +End with two friendly next actions to try in OpenWork. \ No newline at end of file