diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 2b5d8e9..9fa12cb 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,38 +1,17 @@ -## Submission Type +## New Entry Submission -- [ ] Plugin -- [ ] Project -- [ ] Theme -- [ ] Agent -- [ ] Resource +**Category:** (plugins / themes / agents / projects / resources) -## Details +**File added:** `data/{category}/your-entry.yaml` -**Name:** -**Repository:** -**Description:** +### Checklist -## Checklist +- [ ] YAML file is in the correct category folder +- [ ] Filename is kebab-case (e.g., `my-plugin.yaml`) +- [ ] All required fields included (name, repo, description, full_description) +- [ ] Repository is public and maintained +- [ ] Entry is relevant to OpenCode -- [ ] Relevant to OpenCode -- [ ] Repository is public and accessible -- [ ] Actively maintained -- [ ] Not a duplicate -- [ ] Entry added in alphabetical order +### Description -## Entry Format - -Use this format in the appropriate README section: - -```html -
- Name - Short description -
- Full description. -

- πŸ”— View Repository -
-
-``` - -See [contributing.md](contributing.md) for full instructions. +Brief description of what this entry does and why it should be included. diff --git a/.github/workflows/generate-readme.yml b/.github/workflows/generate-readme.yml new file mode 100644 index 0000000..54242a8 --- /dev/null +++ b/.github/workflows/generate-readme.yml @@ -0,0 +1,41 @@ +name: Auto-Generate README + +on: + push: + branches: + - shamanic/data-driven-automation + paths: + - 'data/**' + +permissions: + contents: write + +jobs: + generate: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.ref }} + persist-credentials: true + + - name: Setup Node.js 20 + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install dependencies + run: npm ci + + - name: Generate README + run: node scripts/generate-readme.js + + - name: Commit changes + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: "docs: auto-regenerate README from YAML data" + file_pattern: "README.md" + commit_user_name: "github-actions[bot]" + commit_user_email: "41898282+github-actions[bot]@users.noreply.github.com" diff --git a/.github/workflows/validate-pr.yml b/.github/workflows/validate-pr.yml new file mode 100644 index 0000000..e9a10be --- /dev/null +++ b/.github/workflows/validate-pr.yml @@ -0,0 +1,39 @@ +name: Validate PR YAML Files + +on: + pull_request: + paths: + - 'data/**' + +permissions: + contents: read + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install dependencies + run: npm ci + + - name: Get changed YAML files + id: changed-files + uses: tj-actions/changed-files@v44 + with: + files: | + data/**/*.yaml + + - name: Validate YAML files + run: | + if [[ -n "${{ steps.changed-files.outputs.all_changed_files }}" ]]; then + node scripts/validate.js ${{ steps.changed-files.outputs.all_changed_files }} + else + echo "No YAML files changed, skipping validation" + fi diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3af44cb --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# Dependencies +node_modules/ + +# Logs +*.log +npm-debug.log* + +# OS +.DS_Store +Thumbs.db + +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# Generated (for local testing only - README.md is committed) diff --git a/README.md b/README.md index d84e611..52c31d4 100644 --- a/README.md +++ b/README.md @@ -53,20 +53,20 @@
- Ring a Bell Example - Simple terminal bell plugin + Background - Background process management
- A simple plugin to ring the terminal bell once a request is complete. + Background process management plugin for opencode.

- πŸ”— View Gist + πŸ”— View Repository
- Google AI Search - Query Google AI Mode (SGE) + CC Safety Net - Safety net catching destructive commands
- An opencode plugin that exposes a native tool for querying Google AI Mode (SGE). + A Claude Code plugin that acts as a safety net, catching destructive git and filesystem commands before they execute.

- πŸ”— View Repository + πŸ”— View Repository
@@ -79,6 +79,69 @@ +
+ Direnv - Load direnv variables +
+ Automatically loads direnv environment variables at session start. Perfect for Nix flakes. +

+ πŸ”— View Repository +
+
+ +
+ Dynamic Context Pruning - Optimize token usage +
+ Plugin that optimises token usage by pruning obsolete tool outputs from conversation context. +

+ πŸ”— View Repository +
+
+ +
+ Gemini Auth - Google account auth +
+ Authenticate the Opencode CLI with your Google account so you can use your existing Gemini plan. +

+ πŸ”— View Repository +
+
+ +
+ Google AI Search - Query Google AI Mode (SGE) +
+ An opencode plugin that exposes a native tool for querying Google AI Mode (SGE). +

+ πŸ”— View Repository +
+
+ +
+ Oh My Opencode - Agents & Pre-built tools +
+ Background agents, pre-built tools (LSP/AST/MCP), curated agents, and a Claude Code compatible layer. +

+ πŸ”— View Repository +
+
+ +
+ OpenAI Codex Auth - ChatGPT Plus/Pro OAuth +
+ This plugin enables opencode to use OpenAI's Codex backend via ChatGPT Plus/Pro OAuth authentication. +

+ πŸ”— View Repository +
+
+ +
+ Opencode Canvas - Interactive terminal canvases in tmux splits +
+ Interactive terminal canvases (calendars, documents, flight booking) in tmux splits. Port of claude-canvas for OpenCode. +

+ πŸ”— View Repository +
+
+
Opencode Ignore - Ignore files based on pattern
@@ -88,6 +151,87 @@
+
+ Opencode Roadmap - Strategic planning +
+ Strategic roadmap planning and multi-agent coordination plugin. Provides project-wide planning capabilities. +

+ πŸ”— View Repository +
+
+ +
+ Opencode Sessions - Session management +
+ Session management plugin for OpenCode with multi-agent collaboration support. +

+ πŸ”— View Repository +
+
+ +
+ Opencode Skills - Manage skills and capabilities +
+ Plugin for managing and organising opencode skills and capabilities. +

+ πŸ”— View Repository +
+
+ +
+ OpenHax Codex - OAuth authentication +
+ OAuth authentication plugin for personal coding assistance with ChatGPT Plus/Pro subscriptions. +

+ πŸ”— View Repository +
+
+ +
+ Openskills - Alternative skills manager +
+ Alternative skills management plugin for opencode with enhanced features. +

+ πŸ”— View Repository +
+
+ +
+ Ring a Bell Example - Simple terminal bell plugin +
+ A simple plugin to ring the terminal bell once a request is complete. +

+ πŸ”— View Gist +
+
+ +
+ Smart Title - Auto-generate session titles +
+ Auto-generates meaningful session titles using AI. +

+ πŸ”— View Repository +
+
+ +
+ Subtask2 - Orchestration system +
+ Extend opencode /commands into a powerful orchestration system with granular flow control. +

+ πŸ”— View Repository +
+
+ +
+ Tokenscope - Token analysis & cost tracking +
+ Tokenscope, Comprehensive token usage analysis and cost tracking for opencode sessions. +

+ πŸ”— View Repository +
+
+
Warcraft Notifications - Fun sound notifications
@@ -106,132 +250,6 @@
-
- Opencode Skills - Manage skills and capabilities -
- Plugin for managing and organising opencode skills and capabilities. -

- πŸ”— View Repository -
-
- -
- Openskills - Alternative skills manager -
- Alternative skills management plugin for opencode with enhanced features. -

- πŸ”— View Repository -
-
- -
- Smart Title - Auto-generate session titles -
- Auto-generates meaningful session titles using AI. -

- πŸ”— View Repository -
-
- -
- Background - Background process management -
- Background process management plugin for opencode. -

- πŸ”— View Repository -
-
- -
- Dynamic Context Pruning - Optimize token usage -
- Plugin that optimises token usage by pruning obsolete tool outputs from conversation context. -

- πŸ”— View Repository -
-
- -
- Tokenscope - Token analysis & cost tracking -
- Tokenscope, Comprehensive token usage analysis and cost tracking for opencode sessions. -

- πŸ”— View Repository -
-
- -
- OpenAI Codex Auth - ChatGPT Plus/Pro OAuth -
- This plugin enables opencode to use OpenAI's Codex backend via ChatGPT Plus/Pro OAuth authentication. -

- πŸ”— View Repository -
-
- -
- OpenHax Codex - OAuth authentication -
- OAuth authentication plugin for personal coding assistance with ChatGPT Plus/Pro subscriptions. -

- πŸ”— View Repository -
-
- -
- Opencode Sessions - Session management -
- Session management plugin for OpenCode with multi-agent collaboration support. -

- πŸ”— View Repository -
-
- -
- Opencode Roadmap - Strategic planning -
- Strategic roadmap planning and multi-agent coordination plugin. Provides project-wide planning capabilities. -

- πŸ”— View Repository -
-
- -
- Gemini Auth - Google account auth -
- Authenticate the Opencode CLI with your Google account so you can use your existing Gemini plan. -

- πŸ”— View Repository -
-
- -
- Direnv - Load direnv variables -
- Automatically loads direnv environment variables at session start. Perfect for Nix flakes. -

- πŸ”— View Repository -
-
- -
- Oh My Opencode - Agents & Pre-built tools -
- Background agents, pre-built tools (LSP/AST/MCP), curated agents, and a Claude Code compatible layer. -

- πŸ”— View Repository -
-
- -
- Subtask2 - Orchestration system -
- Extend opencode /commands into a powerful orchestration system with granular flow control. -

- πŸ”— View Repository -
-
-
βž• Add a Plugin via PR @@ -244,6 +262,15 @@ 🎨 THEMES
+
+ Ayu Dark - Port of the popular Ayu Dark color scheme with golden yellow accent. +
+ Port of the popular Ayu Dark color scheme with golden yellow accent. +

+ πŸ”— View Repository +
+
+
Poimandres Theme - Poimandres theme
@@ -305,56 +332,11 @@
- OC Monitor Share - CLI monitoring tool + Beads - Project task management
- A CLI tool for monitoring and analysing opencode AI coding usage. + Steve Yegge's project/task management system for agents (with beads_viewer UI).

- πŸ”— View Repository -
-
- -
- OC Context (occtx) - Switch contexts quickly -
- A command-line tool for switching between different opencode contexts quickly. -

- πŸ”— View Repository -
-
- -
- MCP Voice Interface - Talk to AI assistants -
- Talk to AI assistants using your voice through a web browser. Compatible with Claude Desktop and opencode. -

- πŸ”— View Repository -
-
- -
- Codex Proxy Server - Local API proxy -
- A proxy server that provides a local API proxy for Codex/ChatGPT-like models. -

- πŸ”— View Repository -
-
- -
- Qwen Code OAI Proxy - Qwen model proxy -
- An OpenAI-Compatible Proxy Server for Qwen models. -

- πŸ”— View Repository -
-
- -
- Gemini CLI to API - Gemini proxy -
- A proxy that converts the Gemini CLI tool into OpenAI-compatible endpoints. -

- πŸ”— View Repository + πŸ”— View Repository
@@ -368,16 +350,34 @@
- Opencode Neovim - Neovim plugin + Codex Proxy Server - Local API proxy
- Neovim plugin for making convenient editor-aware prompts. + A proxy server that provides a local API proxy for Codex/ChatGPT-like models.

- πŸ”— View Repository + πŸ”— View Repository
- Kimaki - Discord bot controller + Gemini CLI to API - Gemini proxy +
+ A proxy that converts the Gemini CLI tool into OpenAI-compatible endpoints. +

+ πŸ”— View Repository +
+
+ +
+ Handy - Speech to Text +
+ Easy Open Source Speech to Text. +

+ πŸ”— View Repository +
+
+ +
+ Kimaki - Discord bot controller
A Discord bot to control opencode sessions on any computer via Discord.

@@ -386,20 +386,65 @@
- Vibe Kanban - Manage AI in parallel + MCP Voice Interface - Talk to AI assistants
- A Kanban board to manage and orchestrate AI coding agents in parallel. + Talk to AI assistants using your voice through a web browser. Compatible with Claude Desktop and opencode.

- πŸ”— View Repository + πŸ”— View Repository
- Opencode Skills - Skills management + OC Context (occtx) - Switch contexts quickly
- Skills management system for organising and tracking opencode capabilities. + A command-line tool for switching between different opencode contexts quickly.

- πŸ”— View Repository + πŸ”— View Repository +
+
+ +
+ OC Manager - Metadata TUI +
+ Terminal UI for inspecting, filtering, and pruning OpenCode metadata stored on disk. +

+ πŸ”— View Repository +
+
+ +
+ OC Monitor Share - CLI monitoring tool +
+ A CLI tool for monitoring and analysing opencode AI coding usage. +

+ πŸ”— View Repository +
+
+ +
+ Octto - Interactive browser UI for AI brainstorming +
+ Interactive browser UI for AI brainstorming with multi-question forms, parallel exploration branches, and visual feedback. +

+ πŸ”— View Repository +
+
+ +
+ Opencode DDEV - DDEV container wrapper +
+ Wraps bash commands to execute inside the DDEV container (Docker-based PHP development environments). +

+ πŸ”— View Repository +
+
+ +
+ Opencode Neovim - Neovim plugin +
+ Neovim plugin for making convenient editor-aware prompts. +

+ πŸ”— View Repository
@@ -413,11 +458,11 @@
- Handy - Speech to Text + Opencode Skills - Skills management
- Easy Open Source Speech to Text. + Skills management system for organising and tracking opencode capabilities.

- πŸ”— View Repository + πŸ”— View Repository
@@ -440,29 +485,29 @@
- OC Manager - Metadata TUI + Qwen Code OAI Proxy - Qwen model proxy
- Terminal UI for inspecting, filtering, and pruning OpenCode metadata stored on disk. + An OpenAI-Compatible Proxy Server for Qwen models.

- πŸ”— View Repository + πŸ”— View Repository
- Beads - Project task management + Tokscale - Token usage tracking CLI
- Steve Yegge's project/task management system for agents (with beads_viewer UI). + A CLI tool for tracking token usage from OpenCode and other coding agents (Claude Code, Codex, Gemini CLI, and Cursor IDE).

- πŸ”— View Repository + πŸ”— View Repository
- Opencode DDEV - DDEV container wrapper + Vibe Kanban - Manage AI in parallel
- Wraps bash commands to execute inside the DDEV container (Docker-based PHP development environments). + A Kanban board to manage and orchestrate AI coding agents in parallel.

- πŸ”— View Repository + πŸ”— View Repository
@@ -478,15 +523,6 @@ πŸ“š RESOURCES
-
- GoTTY - Turn CLI into Web App -
- A simple command-line tool that turns your CLI tools, like opencode, into web applications. -

- πŸ”— View Repository -
-
-
Debug Log to Text File - Troubleshooting guide
@@ -496,6 +532,15 @@
+
+ GoTTY - Turn CLI into Web App +
+ A simple command-line tool that turns your CLI tools, like opencode, into web applications. +

+ πŸ”— View Repository +
+
+
βž• Add a Resource via PR diff --git a/contributing.md b/contributing.md index ddf3b8d..a4bac4a 100644 --- a/contributing.md +++ b/contributing.md @@ -1,11 +1,9 @@ # Contribution Guidelines -Thank you for your interest in contributing to `awesome-opencode`! Your contributions help make this list a great resource for the community. +Thank you for contributing to `awesome-opencode`! Add your entries via YAML filesβ€”no need to edit README.md directly. ## How to Add an Entry -**Submit a Pull Request** adding your entry directly to the README. - ### Step 1: Fork & Clone ```bash @@ -13,95 +11,50 @@ git clone https://github.com/YOUR-USERNAME/awesome-opencode.git cd awesome-opencode ``` -### Step 2: Add Your Entry +### Step 2: Create YAML File -Add your entry to the appropriate section in `README.md` in **alphabetical order**. +Create a YAML file in the appropriate category folder under `data/`: +- `data/plugins/` - OpenCode plugins and extensions +- `data/themes/` - Color schemes and visual themes +- `data/agents/` - AI agents and subagents +- `data/projects/` - Tools, GUIs, integrations, and utilities +- `data/resources/` - Guides, templates, and configurations -Use this format: +**Filename:** kebab-case (e.g., `my-plugin.yaml`) -```html -
- Your Project Name - Short description -
- Full description here. -

- πŸ”— View Repository -
-
+### Step 3: Add YAML Content + +```yaml +name: Your Plugin Name +repo: https://github.com/owner/repo-name +description: Short description (max 100 chars, shown in summary) +full_description: Longer description explaining what it does. ``` -**Sections:** -- **Plugins** - OpenCode plugins and extensions -- **Themes** - Color schemes and visual themes -- **Agents** - AI agents and subagents -- **Projects** - Tools, GUIs, integrations, and utilities -- **Resources** - Guides, templates, and configurations - -### Step 3: Submit PR +### Step 4: Submit PR ```bash -git checkout -b add-my-project -git add README.md -git commit -m "docs: add my-project to plugins" -git push origin add-my-project +git checkout -b add-my-plugin +git add data/plugins/my-plugin.yaml +git commit -m "docs: add my-plugin to plugins" +git push origin add-my-plugin ``` -Then open a Pull Request on GitHub. +Open a Pull Request on GitHub. ## Entry Requirements -Before submitting, ensure your entry: +- [ ] **Relevant** - Directly related to OpenCode +- [ ] **Public** - Repository is publicly accessible +- [ ] **Maintained** - Active commits within the last 6 months +- [ ] **Unique** - Not a duplicate of existing entry +- [ ] **Complete** - All required fields included -- [ ] **Is relevant** - Directly related to OpenCode -- [ ] **Is public** - Repository is publicly accessible -- [ ] **Is maintained** - Active commits within the last 6 months -- [ ] **Is not a duplicate** - Check the list first -- [ ] **Is alphabetically ordered** - Placed correctly in the section -- [ ] **Uses correct format** - Collapsible details with star badge +## What Happens After PR? -## Quick Copy Templates +1. **Validation runs** - Automated checks verify YAML format +2. **Maintainer review** - Content and relevance verified +3. **Merge** - Once approved +4. **README auto-generates** - List updates automatically -### Plugin -```html -
- opencode-example - Brief description of what it does -
- Longer description with more details about the plugin. -

- πŸ”— View Repository -
-
-``` - -### Project -```html -
- My Project - Brief description -
- Longer description with more details. -

- πŸ”— View Repository -
-
-``` - -### Theme -```html -
- My Theme - Color scheme description -
- Theme description and color details. -

- πŸ”— View Repository -
-
-``` - -## What Happens Next? - -A maintainer will review your PR and: -- Verify the project meets our guidelines -- Check for security concerns -- Merge if approved, or request changes - -Thank you for contributing to the OpenCode community! +No need to edit README.mdβ€”it regenerates from YAML files. diff --git a/data/agents/agentic.yaml b/data/agents/agentic.yaml new file mode 100644 index 0000000..30f2662 --- /dev/null +++ b/data/agents/agentic.yaml @@ -0,0 +1,4 @@ +name: Agentic +repo: https://github.com/Cluster444/agentic +description: Modular AI agents +full_description: Modular AI agents and commands for structured software development with opencode. diff --git a/data/agents/claude-subagents.yaml b/data/agents/claude-subagents.yaml new file mode 100644 index 0000000..729bc9c --- /dev/null +++ b/data/agents/claude-subagents.yaml @@ -0,0 +1,4 @@ +name: Claude Subagents +repo: https://github.com/VoltAgent/awesome-claude-code-subagents +description: Claude Code subagents +full_description: Comprehensive reference repository for production-ready Claude Code subagents. diff --git a/data/agents/opencode-agents.yaml b/data/agents/opencode-agents.yaml new file mode 100644 index 0000000..2efa8f5 --- /dev/null +++ b/data/agents/opencode-agents.yaml @@ -0,0 +1,4 @@ +name: Opencode Agents +repo: https://github.com/darrenhinde/opencode-agents +description: Enhanced workflows +full_description: A set of opencode configurations, prompts, agents, and plugins for enhanced development workflows. diff --git a/data/plugins/background.yaml b/data/plugins/background.yaml new file mode 100644 index 0000000..b616a69 --- /dev/null +++ b/data/plugins/background.yaml @@ -0,0 +1,4 @@ +name: Background +repo: https://github.com/zenobi-us/opencode-background +description: Background process management +full_description: Background process management plugin for opencode. diff --git a/data/plugins/cc-safety-net.yaml b/data/plugins/cc-safety-net.yaml new file mode 100644 index 0000000..77c41d2 --- /dev/null +++ b/data/plugins/cc-safety-net.yaml @@ -0,0 +1,4 @@ +name: CC Safety Net +repo: https://github.com/kenryu42/claude-code-safety-net +description: Safety net catching destructive commands +full_description: A Claude Code plugin that acts as a safety net, catching destructive git and filesystem commands before they execute. \ No newline at end of file diff --git a/data/plugins/context-analysis.yaml b/data/plugins/context-analysis.yaml new file mode 100644 index 0000000..fdbe619 --- /dev/null +++ b/data/plugins/context-analysis.yaml @@ -0,0 +1,4 @@ +name: Context Analysis +repo: https://github.com/IgorWarzocha/Opencode-Context-Analysis-Plugin +description: Token usage analysis +full_description: An opencode plugin that provides detailed token usage analysis for your AI sessions. diff --git a/data/plugins/direnv.yaml b/data/plugins/direnv.yaml new file mode 100644 index 0000000..20a1ca8 --- /dev/null +++ b/data/plugins/direnv.yaml @@ -0,0 +1,4 @@ +name: Direnv +repo: https://github.com/simonwjackson/opencode-direnv +description: Load direnv variables +full_description: Automatically loads direnv environment variables at session start. Perfect for Nix flakes. diff --git a/data/plugins/dynamic-context-pruning.yaml b/data/plugins/dynamic-context-pruning.yaml new file mode 100644 index 0000000..9fc5010 --- /dev/null +++ b/data/plugins/dynamic-context-pruning.yaml @@ -0,0 +1,4 @@ +name: Dynamic Context Pruning +repo: https://github.com/Tarquinen/opencode-dynamic-context-pruning +description: Optimize token usage +full_description: Plugin that optimises token usage by pruning obsolete tool outputs from conversation context. diff --git a/data/plugins/gemini-auth.yaml b/data/plugins/gemini-auth.yaml new file mode 100644 index 0000000..9ebfe27 --- /dev/null +++ b/data/plugins/gemini-auth.yaml @@ -0,0 +1,4 @@ +name: Gemini Auth +repo: https://github.com/jenslys/opencode-gemini-auth +description: Google account auth +full_description: Authenticate the Opencode CLI with your Google account so you can use your existing Gemini plan. diff --git a/data/plugins/google-ai-search.yaml b/data/plugins/google-ai-search.yaml new file mode 100644 index 0000000..9689384 --- /dev/null +++ b/data/plugins/google-ai-search.yaml @@ -0,0 +1,4 @@ +name: Google AI Search +repo: https://github.com/IgorWarzocha/Opencode-Google-AI-Search-Plugin +description: Query Google AI Mode (SGE) +full_description: An opencode plugin that exposes a native tool for querying Google AI Mode (SGE). diff --git a/data/plugins/oh-my-opencode.yaml b/data/plugins/oh-my-opencode.yaml new file mode 100644 index 0000000..acbabd0 --- /dev/null +++ b/data/plugins/oh-my-opencode.yaml @@ -0,0 +1,4 @@ +name: Oh My Opencode +repo: https://github.com/code-yeongyu/oh-my-opencode +description: Agents & Pre-built tools +full_description: Background agents, pre-built tools (LSP/AST/MCP), curated agents, and a Claude Code compatible layer. diff --git a/data/plugins/openai-codex-auth.yaml b/data/plugins/openai-codex-auth.yaml new file mode 100644 index 0000000..7743fce --- /dev/null +++ b/data/plugins/openai-codex-auth.yaml @@ -0,0 +1,4 @@ +name: OpenAI Codex Auth +repo: https://github.com/numman-ali/opencode-openai-codex-auth +description: ChatGPT Plus/Pro OAuth +full_description: This plugin enables opencode to use OpenAI's Codex backend via ChatGPT Plus/Pro OAuth authentication. diff --git a/data/plugins/opencode-canvas.yaml b/data/plugins/opencode-canvas.yaml new file mode 100644 index 0000000..70c4bb3 --- /dev/null +++ b/data/plugins/opencode-canvas.yaml @@ -0,0 +1,4 @@ +name: Opencode Canvas +repo: https://github.com/mailshieldai/opencode-canvas +description: Interactive terminal canvases in tmux splits +full_description: Interactive terminal canvases (calendars, documents, flight booking) in tmux splits. Port of claude-canvas for OpenCode. \ No newline at end of file diff --git a/data/plugins/opencode-ignore.yaml b/data/plugins/opencode-ignore.yaml new file mode 100644 index 0000000..faa08b8 --- /dev/null +++ b/data/plugins/opencode-ignore.yaml @@ -0,0 +1,4 @@ +name: Opencode Ignore +repo: https://github.com/lgladysz/opencode-ignore +description: Ignore files based on pattern +full_description: Plugin to ignore directory/file based on pattern. diff --git a/data/plugins/opencode-roadmap.yaml b/data/plugins/opencode-roadmap.yaml new file mode 100644 index 0000000..c3e927e --- /dev/null +++ b/data/plugins/opencode-roadmap.yaml @@ -0,0 +1,4 @@ +name: Opencode Roadmap +repo: https://github.com/IgorWarzocha/Opencode-Roadmap +description: Strategic planning +full_description: Strategic roadmap planning and multi-agent coordination plugin. Provides project-wide planning capabilities. diff --git a/data/plugins/opencode-sessions.yaml b/data/plugins/opencode-sessions.yaml new file mode 100644 index 0000000..215cdd6 --- /dev/null +++ b/data/plugins/opencode-sessions.yaml @@ -0,0 +1,4 @@ +name: Opencode Sessions +repo: https://github.com/malhashemi/opencode-sessions +description: Session management +full_description: Session management plugin for OpenCode with multi-agent collaboration support. diff --git a/data/plugins/opencode-skills.yaml b/data/plugins/opencode-skills.yaml new file mode 100644 index 0000000..0a624e1 --- /dev/null +++ b/data/plugins/opencode-skills.yaml @@ -0,0 +1,4 @@ +name: Opencode Skills +repo: https://github.com/malhashemi/opencode-skills +description: Manage skills and capabilities +full_description: Plugin for managing and organising opencode skills and capabilities. diff --git a/data/plugins/openhax-codex.yaml b/data/plugins/openhax-codex.yaml new file mode 100644 index 0000000..440b6b9 --- /dev/null +++ b/data/plugins/openhax-codex.yaml @@ -0,0 +1,4 @@ +name: OpenHax Codex +repo: https://github.com/open-hax/codex +description: OAuth authentication +full_description: OAuth authentication plugin for personal coding assistance with ChatGPT Plus/Pro subscriptions. diff --git a/data/plugins/openskills.yaml b/data/plugins/openskills.yaml new file mode 100644 index 0000000..8f3b97d --- /dev/null +++ b/data/plugins/openskills.yaml @@ -0,0 +1,4 @@ +name: Openskills +repo: https://github.com/numman-ali/openskills +description: Alternative skills manager +full_description: Alternative skills management plugin for opencode with enhanced features. diff --git a/data/plugins/ring-a-bell-example.yaml b/data/plugins/ring-a-bell-example.yaml new file mode 100644 index 0000000..47f1725 --- /dev/null +++ b/data/plugins/ring-a-bell-example.yaml @@ -0,0 +1,4 @@ +name: Ring a Bell Example +repo: https://gist.github.com/ahosker/267f375a65378bcb9a867fd9a195db1e +description: Simple terminal bell plugin +full_description: A simple plugin to ring the terminal bell once a request is complete. diff --git a/data/plugins/smart-title.yaml b/data/plugins/smart-title.yaml new file mode 100644 index 0000000..9df1b7d --- /dev/null +++ b/data/plugins/smart-title.yaml @@ -0,0 +1,4 @@ +name: Smart Title +repo: https://github.com/Tarquinen/opencode-smart-title +description: Auto-generate session titles +full_description: Auto-generates meaningful session titles using AI. diff --git a/data/plugins/subtask2.yaml b/data/plugins/subtask2.yaml new file mode 100644 index 0000000..6c36199 --- /dev/null +++ b/data/plugins/subtask2.yaml @@ -0,0 +1,4 @@ +name: Subtask2 +repo: https://github.com/spoons-and-mirrors/subtask2 +description: Orchestration system +full_description: Extend opencode /commands into a powerful orchestration system with granular flow control. diff --git a/data/plugins/tokenscope.yaml b/data/plugins/tokenscope.yaml new file mode 100644 index 0000000..cec0491 --- /dev/null +++ b/data/plugins/tokenscope.yaml @@ -0,0 +1,4 @@ +name: Tokenscope +repo: https://github.com/ramtinJ95/opencode-tokenscope +description: Token analysis & cost tracking +full_description: Tokenscope, Comprehensive token usage analysis and cost tracking for opencode sessions. diff --git a/data/plugins/warcraft-notifications.yaml b/data/plugins/warcraft-notifications.yaml new file mode 100644 index 0000000..aaadc16 --- /dev/null +++ b/data/plugins/warcraft-notifications.yaml @@ -0,0 +1,4 @@ +name: Warcraft Notifications +repo: https://github.com/pantheon-org/opencode-warcraft-notifications +description: Fun sound notifications +full_description: Notification plugin with Warcraft sounds for opencode completion alerts. diff --git a/data/plugins/with-context-mcp.yaml b/data/plugins/with-context-mcp.yaml new file mode 100644 index 0000000..c3c393a --- /dev/null +++ b/data/plugins/with-context-mcp.yaml @@ -0,0 +1,4 @@ +name: With Context MCP +repo: https://github.com/boxpositron/with-context-mcp +description: Project-specific markdown notes +full_description: MCP server for managing project-specific markdown notes with templates, batch edits, and ignore patterns. diff --git a/data/projects/beads.yaml b/data/projects/beads.yaml new file mode 100644 index 0000000..eef9a53 --- /dev/null +++ b/data/projects/beads.yaml @@ -0,0 +1,4 @@ +name: Beads +repo: https://github.com/steveyegge/beads +description: Project task management +full_description: Steve Yegge's project/task management system for agents (with beads_viewer UI). diff --git a/data/projects/cli-proxy-api.yaml b/data/projects/cli-proxy-api.yaml new file mode 100644 index 0000000..f7e2171 --- /dev/null +++ b/data/projects/cli-proxy-api.yaml @@ -0,0 +1,4 @@ +name: CLI Proxy API +repo: https://github.com/router-for-me/CLIProxyAPI +description: Multi-model proxy +full_description: A proxy server providing compatible API interfaces for multiple model CLIs. diff --git a/data/projects/codex-proxy-server.yaml b/data/projects/codex-proxy-server.yaml new file mode 100644 index 0000000..1c92acb --- /dev/null +++ b/data/projects/codex-proxy-server.yaml @@ -0,0 +1,4 @@ +name: Codex Proxy Server +repo: https://github.com/unluckyjori/Codex-Proxy-Server +description: Local API proxy +full_description: A proxy server that provides a local API proxy for Codex/ChatGPT-like models. diff --git a/data/projects/gemini-cli-to-api.yaml b/data/projects/gemini-cli-to-api.yaml new file mode 100644 index 0000000..6e7d35f --- /dev/null +++ b/data/projects/gemini-cli-to-api.yaml @@ -0,0 +1,4 @@ +name: Gemini CLI to API +repo: https://github.com/gzzhongqi/geminicli2api +description: Gemini proxy +full_description: A proxy that converts the Gemini CLI tool into OpenAI-compatible endpoints. diff --git a/data/projects/handy.yaml b/data/projects/handy.yaml new file mode 100644 index 0000000..60e4410 --- /dev/null +++ b/data/projects/handy.yaml @@ -0,0 +1,4 @@ +name: Handy +repo: https://github.com/cjpais/Handy +description: Speech to Text +full_description: Easy Open Source Speech to Text. diff --git a/data/projects/kimaki.yaml b/data/projects/kimaki.yaml new file mode 100644 index 0000000..5cd6851 --- /dev/null +++ b/data/projects/kimaki.yaml @@ -0,0 +1,4 @@ +name: Kimaki +repo: https://github.com/remorses/kimaki/ +description: Discord bot controller +full_description: A Discord bot to control opencode sessions on any computer via Discord. diff --git a/data/projects/mcp-voice-interface.yaml b/data/projects/mcp-voice-interface.yaml new file mode 100644 index 0000000..0d9c561 --- /dev/null +++ b/data/projects/mcp-voice-interface.yaml @@ -0,0 +1,4 @@ +name: MCP Voice Interface +repo: https://github.com/shantur/mcp-voice-interface +description: Talk to AI assistants +full_description: Talk to AI assistants using your voice through a web browser. Compatible with Claude Desktop and opencode. diff --git a/data/projects/oc-context-occtx.yaml b/data/projects/oc-context-occtx.yaml new file mode 100644 index 0000000..e139526 --- /dev/null +++ b/data/projects/oc-context-occtx.yaml @@ -0,0 +1,4 @@ +name: OC Context (occtx) +repo: https://github.com/hungthai1401/occtx +description: Switch contexts quickly +full_description: A command-line tool for switching between different opencode contexts quickly. diff --git a/data/projects/oc-manager.yaml b/data/projects/oc-manager.yaml new file mode 100644 index 0000000..3b01980 --- /dev/null +++ b/data/projects/oc-manager.yaml @@ -0,0 +1,4 @@ +name: OC Manager +repo: https://github.com/kcrommett/oc-manager +description: Metadata TUI +full_description: Terminal UI for inspecting, filtering, and pruning OpenCode metadata stored on disk. diff --git a/data/projects/oc-monitor-share.yaml b/data/projects/oc-monitor-share.yaml new file mode 100644 index 0000000..bc30ace --- /dev/null +++ b/data/projects/oc-monitor-share.yaml @@ -0,0 +1,4 @@ +name: OC Monitor Share +repo: https://github.com/Shlomob/ocmonitor-share +description: CLI monitoring tool +full_description: A CLI tool for monitoring and analysing opencode AI coding usage. diff --git a/data/projects/octto.yaml b/data/projects/octto.yaml new file mode 100644 index 0000000..fb28f2c --- /dev/null +++ b/data/projects/octto.yaml @@ -0,0 +1,4 @@ +name: Octto +repo: https://github.com/vtemian/octto +description: Interactive browser UI for AI brainstorming +full_description: Interactive browser UI for AI brainstorming with multi-question forms, parallel exploration branches, and visual feedback. \ No newline at end of file diff --git a/data/projects/opencode-ddev.yaml b/data/projects/opencode-ddev.yaml new file mode 100644 index 0000000..84ca208 --- /dev/null +++ b/data/projects/opencode-ddev.yaml @@ -0,0 +1,4 @@ +name: Opencode DDEV +repo: https://github.com/JUVOJustin/opencode-ddev +description: DDEV container wrapper +full_description: Wraps bash commands to execute inside the DDEV container (Docker-based PHP development environments). diff --git a/data/projects/opencode-neovim.yaml b/data/projects/opencode-neovim.yaml new file mode 100644 index 0000000..c264b3f --- /dev/null +++ b/data/projects/opencode-neovim.yaml @@ -0,0 +1,4 @@ +name: Opencode Neovim +repo: https://github.com/NickvanDyke/opencode.nvim +description: Neovim plugin +full_description: Neovim plugin for making convenient editor-aware prompts. diff --git a/data/projects/opencode-sessions.yaml b/data/projects/opencode-sessions.yaml new file mode 100644 index 0000000..22e321f --- /dev/null +++ b/data/projects/opencode-sessions.yaml @@ -0,0 +1,4 @@ +name: Opencode Sessions +repo: https://github.com/malhashemi/opencode-sessions +description: Session tracker +full_description: Session management tool for opencode to track and organise coding sessions. diff --git a/data/projects/opencode-skills.yaml b/data/projects/opencode-skills.yaml new file mode 100644 index 0000000..3ca8ae8 --- /dev/null +++ b/data/projects/opencode-skills.yaml @@ -0,0 +1,4 @@ +name: Opencode Skills +repo: https://github.com/malhashemi/opencode-skills +description: Skills management +full_description: Skills management system for organising and tracking opencode capabilities. diff --git a/data/projects/opencode-web.yaml b/data/projects/opencode-web.yaml new file mode 100644 index 0000000..c0e4241 --- /dev/null +++ b/data/projects/opencode-web.yaml @@ -0,0 +1,4 @@ +name: Opencode Web +repo: https://github.com/kcrommett/opencode-web +description: Browser-based access +full_description: Web interface for opencode - browser-based access to AI coding agent. diff --git a/data/projects/openspec.yaml b/data/projects/openspec.yaml new file mode 100644 index 0000000..9db849e --- /dev/null +++ b/data/projects/openspec.yaml @@ -0,0 +1,4 @@ +name: OpenSpec +repo: https://github.com/Fission-AI/OpenSpec +description: Spec-driven development +full_description: Spec-driven development with opencode - structured specification management. diff --git a/data/projects/qwen-code-oai-proxy.yaml b/data/projects/qwen-code-oai-proxy.yaml new file mode 100644 index 0000000..0bab4f0 --- /dev/null +++ b/data/projects/qwen-code-oai-proxy.yaml @@ -0,0 +1,4 @@ +name: Qwen Code OAI Proxy +repo: https://github.com/aptdnfapt/qwen-code-oai-proxy +description: Qwen model proxy +full_description: An OpenAI-Compatible Proxy Server for Qwen models. diff --git a/data/projects/tokscale.yaml b/data/projects/tokscale.yaml new file mode 100644 index 0000000..2a42cf2 --- /dev/null +++ b/data/projects/tokscale.yaml @@ -0,0 +1,4 @@ +name: Tokscale +repo: https://github.com/junhoyeo/tokscale +description: Token usage tracking CLI +full_description: A CLI tool for tracking token usage from OpenCode and other coding agents (Claude Code, Codex, Gemini CLI, and Cursor IDE). \ No newline at end of file diff --git a/data/projects/vibe-kanban.yaml b/data/projects/vibe-kanban.yaml new file mode 100644 index 0000000..0aba3be --- /dev/null +++ b/data/projects/vibe-kanban.yaml @@ -0,0 +1,4 @@ +name: Vibe Kanban +repo: https://github.com/BloopAI/vibe-kanban +description: Manage AI in parallel +full_description: A Kanban board to manage and orchestrate AI coding agents in parallel. diff --git a/data/resources/debug-log-to-text-file.yaml b/data/resources/debug-log-to-text-file.yaml new file mode 100644 index 0000000..1ce71d6 --- /dev/null +++ b/data/resources/debug-log-to-text-file.yaml @@ -0,0 +1,4 @@ +name: Debug Log to Text File +repo: https://github.com/awesome-opencode/awesome-opencode/discussions/19 +description: Troubleshooting guide +full_description: How to output a debug log from opencode to a text file for troubleshooting. diff --git a/data/resources/gotty.yaml b/data/resources/gotty.yaml new file mode 100644 index 0000000..7871c5f --- /dev/null +++ b/data/resources/gotty.yaml @@ -0,0 +1,4 @@ +name: GoTTY +repo: https://github.com/sorenisanerd/gotty +description: Turn CLI into Web App +full_description: A simple command-line tool that turns your CLI tools, like opencode, into web applications. diff --git a/data/schema.json b/data/schema.json new file mode 100644 index 0000000..21d327a --- /dev/null +++ b/data/schema.json @@ -0,0 +1,71 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://github.com/awesome-opencode/awesome-opencode/data/schema.json", + "title": "Awesome Opencode Entry", + "description": "Schema for validating YAML entries in the awesome-opencode list", + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "description": "Name of the project or tool" + }, + "repo": { + "type": "string", + "format": "uri", + "description": "URL to the repository" + }, + "description": { + "type": "string", + "minLength": 1, + "maxLength": 120, + "description": "Short summary of the project" + }, + "full_description": { + "type": "string", + "minLength": 1, + "description": "Longer detailed description of the project" + }, + "install": { + "type": "object", + "description": "Installation instructions", + "properties": { + "type": { + "type": "string", + "enum": ["config", "npm", "pip", "go", "script", "manual"], + "description": "Type of installation method" + } + }, + "required": ["type"], + "additionalProperties": false + }, + "compatibility": { + "type": "object", + "description": "Compatibility information", + "properties": { + "platforms": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Supported platforms" + }, + "opencode": { + "type": "string", + "description": "Compatible Opencode version" + } + }, + "required": ["platforms", "opencode"], + "additionalProperties": false + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Tags for categorization" + } + }, + "required": ["name", "repo", "description", "full_description"], + "additionalProperties": false +} diff --git a/data/themes/ayu-dark.yaml b/data/themes/ayu-dark.yaml new file mode 100644 index 0000000..c14aa04 --- /dev/null +++ b/data/themes/ayu-dark.yaml @@ -0,0 +1,4 @@ +name: Ayu Dark +repo: https://github.com/postrednik/opencode-ayu-theme +description: Port of the popular Ayu Dark color scheme with golden yellow accent. +full_description: Port of the popular Ayu Dark color scheme with golden yellow accent. \ No newline at end of file diff --git a/data/themes/poimandres-theme.yaml b/data/themes/poimandres-theme.yaml new file mode 100644 index 0000000..30d3b90 --- /dev/null +++ b/data/themes/poimandres-theme.yaml @@ -0,0 +1,4 @@ +name: Poimandres Theme +repo: https://github.com/ajaxdude/opencode-ai-poimandres-theme +description: Poimandres theme +full_description: Poimandres theme for opencode. diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..4d84b20 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,581 @@ +{ + "name": "awesome-opencode", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "awesome-opencode", + "version": "1.0.0", + "license": "CC0-1.0", + "dependencies": { + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "glob": "^11.0.0", + "js-yaml": "^4.1.0" + } + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/minimatch": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..1af4133 --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "awesome-opencode", + "version": "1.0.0", + "description": "A curated list of plugins, themes, agents, and resources for Opencode", + "private": true, + "scripts": { + "generate": "node scripts/generate-readme.js", + "validate": "node scripts/validate.js", + "bootstrap": "node scripts/bootstrap.js" + }, + "keywords": ["opencode", "awesome-list", "plugins", "themes", "agents"], + "license": "CC0-1.0", + "dependencies": { + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "glob": "^11.0.0", + "js-yaml": "^4.1.0" + } +} diff --git a/scripts/generate-readme.js b/scripts/generate-readme.js new file mode 100644 index 0000000..9e2e9fd --- /dev/null +++ b/scripts/generate-readme.js @@ -0,0 +1,154 @@ +#!/usr/bin/env node + +/** + * Generate README.md from YAML files + * Reads all category YAML files, validates them, sorts alphabetically, + * generates HTML, and writes the final README.md + */ + +const fs = require('fs'); +const path = require('path'); +const { readYamlDir } = require('./utils/yaml'); +const { validateEntry, formatValidationErrors } = require('./utils/validation'); +const { readTemplate, replacePlaceholder, generateEntryHtml, writeReadme } = require('./utils/template'); + +// Category processing order +const CATEGORIES = ['plugins', 'themes', 'agents', 'projects', 'resources']; + +// Placeholder names for each category +const CATEGORY_PLACEHOLDERS = { + plugins: 'PLUGINS', + themes: 'THEMES', + agents: 'AGENTS', + projects: 'PROJECTS', + resources: 'RESOURCES' +}; + +/** + * Generate HTML for a single category + * @param {string} categoryName - Category directory name + * @returns {Promise} Generated HTML for all entries in the category + */ +async function generateCategorySection(categoryName) { + const categoryPath = path.join(__dirname, '../data', categoryName); + + let entries = []; + let errors = []; + + try { + entries = await readYamlDir(categoryPath); + } catch (err) { + // If directory doesn't exist, return empty string + if (err.code === 'ENOENT') { + return { html: '', count: 0, errors: [] }; + } + // Re-throw other errors + throw err; + } + + // Validate and filter entries + const validEntries = []; + for (const entry of entries) { + const result = validateEntry(entry, entry._filePath || path.join(categoryPath, `${entry.name || 'unknown'}.yaml`)); + + if (result.valid) { + validEntries.push(entry); + } else { + const errorMsg = formatValidationErrors(result); + errors.push(errorMsg); + console.error(errorMsg); + } + } + + // Sort alphabetically by name (case-insensitive) + validEntries.sort((a, b) => { + const nameA = (a.name || '').toLowerCase(); + const nameB = (b.name || '').toLowerCase(); + return nameA.localeCompare(nameB); + }); + + // Generate HTML for each entry + const htmlParts = validEntries.map(entry => generateEntryHtml(entry)); + + // Join with double newlines for proper spacing + const html = htmlParts.join('\n\n'); + + return { html, count: validEntries.length, errors }; +} + +/** + * Main function to generate the README + */ +async function main() { + console.log('Starting README generation...\n'); + + // Read template + let template; + try { + template = readTemplate(); + console.log('Template loaded successfully'); + } catch (err) { + throw new Error(`Failed to read template: ${err.message}`); + } + + // Process each category in order + const results = {}; + let totalEntries = 0; + let allErrors = []; + + for (const category of CATEGORIES) { + const placeholder = CATEGORY_PLACEHOLDERS[category]; + console.log(`Processing ${category}...`); + + try { + const result = await generateCategorySection(category); + results[placeholder] = result.html; + totalEntries += result.count; + allErrors = allErrors.concat(result.errors); + + if (result.count > 0) { + console.log(` - Found ${result.count} valid entries`); + } else { + console.log(` - No entries found`); + } + } catch (err) { + console.error(` - Error processing ${category}: ${err.message}`); + results[placeholder] = ''; + } + } + + // Replace each placeholder in template + let content = template; + for (const [placeholder, html] of Object.entries(results)) { + content = replacePlaceholder(content, placeholder, html); + } + + // Write final README + try { + writeReadme(content); + console.log('\nREADME.md written successfully'); + } catch (err) { + throw new Error(`Failed to write README.md: ${err.message}`); + } + + // Log summary + const errorCount = allErrors.length; + if (errorCount > 0) { + console.log(`\n⚠️ Generated README.md with ${totalEntries} entries across ${CATEGORIES.length} categories`); + console.log(` ${errorCount} validation error(s) were logged (affected entries were skipped)`); + } else { + console.log(`\nβœ… Generated README.md with ${totalEntries} entries across ${CATEGORIES.length} categories`); + } + + return { totalEntries, categoryCount: CATEGORIES.length, errorCount }; +} + +// Execute main function +main() + .then(({ totalEntries, categoryCount, errorCount }) => { + process.exit(errorCount > 0 ? 0 : 0); + }) + .catch(err => { + console.error('\n❌ Generation failed:', err.message); + process.exit(1); + }); diff --git a/scripts/utils/template.js b/scripts/utils/template.js new file mode 100644 index 0000000..3aaca32 --- /dev/null +++ b/scripts/utils/template.js @@ -0,0 +1,85 @@ +/** + * Template rendering utilities for awesome-opencode + */ + +const fs = require('fs'); +const path = require('path'); + +/** + * Read the README template + * @returns {string} Template content + */ +function readTemplate() { + const templatePath = path.join(__dirname, '../../templates/README.template.md'); + return fs.readFileSync(templatePath, 'utf8'); +} + +/** + * Replace a placeholder in the template + * @param {string} template - Template content + * @param {string} placeholder - Placeholder name (without braces) + * @param {string} content - Content to insert + * @returns {string} Updated template + */ +function replacePlaceholder(template, placeholder, content) { + const pattern = new RegExp(`\\{\\{${placeholder}\\}\\}`, 'g'); + return template.replace(pattern, content); +} + +/** + * Generate HTML for a single entry + * @param {object} entry - Parsed YAML data + * @returns {string} HTML string for the details element + */ +function generateEntryHtml(entry) { + // Determine link text based on URL type first + let linkText = 'πŸ”— View Repository'; + if (entry.repo.includes('gist.github.com')) { + linkText = 'πŸ”— View Gist'; + } else if (entry.repo.includes('/discussions/')) { + linkText = 'πŸ”— View Discussion'; + } + + // Extract owner/repo from URL for star badge (only for non-gist, non-discussion GitHub repos) + // Use negative lookahead to exclude gist.github.com + const isGist = entry.repo.includes('gist.github.com'); + const isDiscussion = entry.repo.includes('/discussions/'); + const repoMatch = entry.repo.match(/github\.com\/(?!gist\.)([^\/]+)\/([^\/]+)/); + + let summaryContent = `${entry.name}`; + + // Add star badge if it's a GitHub repo (not a gist or discussion) + if (repoMatch && !isGist && !isDiscussion) { + const owner = repoMatch[1]; + const repo = repoMatch[2].replace(/\.git$/, '').replace(/\/$/, ''); + const starBadge = `https://badgen.net/github/stars/${owner}/${repo}`; + summaryContent += ` `; + } + + summaryContent += ` - ${entry.description}`; + + return `
+ ${summaryContent} +
+ ${entry.full_description} +

+ ${linkText} +
+
`; +} + +/** + * Write the final README + * @param {string} content - Generated README content + */ +function writeReadme(content) { + const readmePath = path.join(__dirname, '../../README.md'); + fs.writeFileSync(readmePath, content, 'utf8'); +} + +module.exports = { + readTemplate, + replacePlaceholder, + generateEntryHtml, + writeReadme +}; diff --git a/scripts/utils/validation.js b/scripts/utils/validation.js new file mode 100644 index 0000000..f145890 --- /dev/null +++ b/scripts/utils/validation.js @@ -0,0 +1,74 @@ +/** + * Schema validation utilities for awesome-opencode + */ + +const fs = require('fs'); +const path = require('path'); +const Ajv = require('ajv'); +const addFormats = require('ajv-formats'); + +// Initialize AJV with all errors option +const ajv = new Ajv({ allErrors: true }); +addFormats(ajv); + +// Load schema lazily (on first use) +let validateFn = null; + +/** + * Get the compiled validation function + * @returns {Function} AJV validate function + */ +function getValidator() { + if (!validateFn) { + const schemaPath = path.join(__dirname, '../../data/schema.json'); + const schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8')); + validateFn = ajv.compile(schema); + } + return validateFn; +} + +/** + * Validate an entry against the schema + * @param {object} data - Parsed YAML data + * @param {string} filePath - File path for error messages + * @returns {object} { valid: boolean, errors: array|null } + */ +function validateEntry(data, filePath) { + const validate = getValidator(); + + // Remove internal metadata fields before validation + const cleanData = { ...data }; + delete cleanData._filePath; + delete cleanData._fileName; + + const valid = validate(cleanData); + + if (!valid) { + const errors = validate.errors.map(err => ({ + path: err.instancePath || '/', + message: err.message, + keyword: err.keyword, + params: err.params + })); + return { valid: false, errors, filePath }; + } + + return { valid: true, errors: null, filePath }; +} + +/** + * Format validation errors for display + * @param {object} result - Result from validateEntry + * @returns {string} Formatted error message + */ +function formatValidationErrors(result) { + if (result.valid) return ''; + + const lines = [`Validation failed for ${result.filePath}:`]; + for (const err of result.errors) { + lines.push(` - ${err.path}: ${err.message}`); + } + return lines.join('\n'); +} + +module.exports = { validateEntry, formatValidationErrors, getValidator }; diff --git a/scripts/utils/yaml.js b/scripts/utils/yaml.js new file mode 100644 index 0000000..dae0ec4 --- /dev/null +++ b/scripts/utils/yaml.js @@ -0,0 +1,52 @@ +/** + * YAML file reading utilities for awesome-opencode + */ + +const fs = require('fs'); +const path = require('path'); +const yaml = require('js-yaml'); +const { glob } = require('glob'); + +/** + * Read and parse a single YAML file + * @param {string} filePath - Path to YAML file + * @returns {object} Parsed YAML object + */ +function readYamlFile(filePath) { + try { + const content = fs.readFileSync(filePath, 'utf8'); + return yaml.load(content, { filename: filePath }); + } catch (e) { + throw new Error(`Failed to parse ${filePath}: ${e.message}`); + } +} + +/** + * Read all YAML files from a directory + * @param {string} dirPath - Directory containing YAML files + * @returns {Promise} Array of parsed YAML objects with _filePath metadata + */ +async function readYamlDir(dirPath) { + const pattern = path.join(dirPath, '*.yaml').replace(/\\/g, '/'); + const files = await glob(pattern); + + return files.map(file => ({ + ...readYamlFile(file), + _filePath: file, + _fileName: path.basename(file, '.yaml') + })); +} + +/** + * Convert a name to a filename-safe slug + * @param {string} name - Entry name + * @returns {string} Slugified filename (without extension) + */ +function slugify(name) { + return name + .toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, ''); +} + +module.exports = { readYamlFile, readYamlDir, slugify }; diff --git a/scripts/validate.js b/scripts/validate.js new file mode 100644 index 0000000..26594f7 --- /dev/null +++ b/scripts/validate.js @@ -0,0 +1,109 @@ +#!/usr/bin/env node +/** + * Validate YAML files against the schema + * Usage: node scripts/validate.js [file1.yaml] [file2.yaml] ... + * If no files specified, validates all YAML files in data/ + */ + +const fs = require('fs'); +const path = require('path'); +const glob = require('glob'); +const yaml = require('js-yaml'); +const { validateEntry, formatValidationErrors } = require('./utils/validation'); + +const DATA_DIR = path.join(__dirname, '../data'); + +/** + * Get all YAML files in the data directory + * @returns {string[]} Array of file paths + */ +function getAllYamlFiles() { + const pattern = path.join(DATA_DIR, '**/*.yaml'); + return glob.sync(pattern); +} + +/** + * Load and parse a YAML file + * @param {string} filePath - Path to the YAML file + * @returns {object|null} Parsed YAML object or null on error + */ +function loadYamlFile(filePath) { + try { + const content = fs.readFileSync(filePath, 'utf8'); + return yaml.load(content); + } catch (err) { + console.error(`Error reading ${filePath}: ${err.message}`); + return null; + } +} + +/** + * Validate a single file + * @param {string} filePath - Path to the YAML file + * @returns {boolean} True if valid, false otherwise + */ +function validateFile(filePath) { + const data = loadYamlFile(filePath); + if (!data) { + return false; + } + + const result = validateEntry(data, filePath); + if (result.valid) { + console.log(`βœ“ ${filePath}`); + } else { + console.error(formatValidationErrors(result)); + } + + return result.valid; +} + +/** + * Main validation function + */ +function main() { + const args = process.argv.slice(2); + let files = []; + + if (args.length > 0) { + // Validate specified files + files = args; + } else { + // Validate all YAML files in data/ + files = getAllYamlFiles(); + } + + if (files.length === 0) { + console.log('No YAML files to validate.'); + process.exit(0); + } + + console.log(`Validating ${files.length} YAML file(s)...\n`); + + let allValid = true; + let validatedCount = 0; + + for (const file of files) { + if (fs.existsSync(file)) { + if (!validateFile(file)) { + allValid = false; + } + validatedCount++; + } else { + console.warn(`⚠ File not found: ${file}`); + allValid = false; + } + } + + console.log(''); + + if (allValid) { + console.log(`βœ“ All ${validatedCount} file(s) passed validation.`); + process.exit(0); + } else { + console.error(`βœ— Validation failed for ${validatedCount} file(s).`); + process.exit(1); + } +} + +main(); diff --git a/templates/README.template.md b/templates/README.template.md new file mode 100644 index 0000000..1c4baaa --- /dev/null +++ b/templates/README.template.md @@ -0,0 +1,125 @@ + +
+ +
+ +Awesome Opencode +

+ + +

Awesome Opencode

+ + +

+Awesome +   +Opencode Stars +

+ +
+ + +

A curated list of plugins, themes, agents, and resources for Opencode.

+

The AI coding agent for the terminal, built by the team at SST.

+ +
+ +[**OFFICIAL**](#official) β€’ [**PLUGINS**](#plugins) β€’ [**THEMES**](#themes) β€’ [**AGENTS**](#agents) β€’ [**PROJECTS**](#projects) β€’ [**RESOURCES**](#resources) + +
+
+ +
+ + + +
+ +

⭐️ Official Repositories

+ +| Project | Stars | Description | +| :--- | :--- | :--- | +| **[opencode](https://github.com/sst/opencode)** | ![Stars](https://badgen.net/github/stars/sst/opencode) | The official opencode AI coding agent. | +| **[opencode-sdk-js](https://github.com/sst/opencode-sdk-js)** | ![Stars](https://badgen.net/github/stars/sst/opencode-sdk-js) | Official JavaScript/TypeScript SDK for opencode. | +| **[opencode-sdk-go](https://github.com/sst/opencode-sdk-go)** | ![Stars](https://badgen.net/github/stars/sst/opencode-sdk-go) | Official Go SDK for opencode. | +| **[opencode-sdk-python](https://github.com/sst/opencode-sdk-python)** | ![Stars](https://badgen.net/github/stars/sst/opencode-sdk-python) | Official Python SDK for opencode. | + +
+ +
+ +
+🧩 PLUGINS +
+ +{{PLUGINS}} + +
+βž• Add a Plugin via PR +
+ +
+ +
+ +
+🎨 THEMES +
+ +{{THEMES}} + +
+βž• Add a Theme via PR +
+ +
+ +
+ +
+πŸ€– AGENTS +
+ +{{AGENTS}} + +
+βž• Add an Agent via PR +
+ +
+ +
+ +
+πŸ›  PROJECTS +
+ +{{PROJECTS}} + +
+βž• Add a Project via PR +
+ +
+ +
+ +
+πŸ“š RESOURCES +
+ +{{RESOURCES}} + +
+βž• Add a Resource via PR +
+ +

+ +
+

🀝 Contributing

+Found an Awesome Opencode project?
+Submit a Pull Request to add it to the list! +

+Released under CC0 1.0 Universal. +