diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json new file mode 100644 index 0000000..6b90600 --- /dev/null +++ b/.agents/plugins/marketplace.json @@ -0,0 +1,176 @@ +{ + "name": "duyet-claude-plugins", + "interface": { + "displayName": "Duyet Claude and Codex Plugins" + }, + "plugins": [ + { + "name": "team-agents", + "source": { + "source": "local", + "path": "./team-agents" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "Agents" + }, + { + "name": "commit", + "source": { + "source": "local", + "path": "./commit" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "Git" + }, + { + "name": "frontend-design", + "source": { + "source": "local", + "path": "./frontend-design" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "Design" + }, + { + "name": "interview", + "source": { + "source": "local", + "path": "./interview" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "Productivity" + }, + { + "name": "statusline", + "source": { + "source": "local", + "path": "./statusline" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "UI" + }, + { + "name": "orchestration", + "source": { + "source": "local", + "path": "./orchestration" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "Agents" + }, + { + "name": "duyetbot", + "source": { + "source": "local", + "path": "./duyetbot" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "Productivity" + }, + { + "name": "docs-generator", + "source": { + "source": "local", + "path": "./docs-generator" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "Docs" + }, + { + "name": "github", + "source": { + "source": "local", + "path": "./github" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "Git" + }, + { + "name": "fix", + "source": { + "source": "local", + "path": "./fix" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "Productivity" + }, + { + "name": "clickhouse", + "source": { + "source": "local", + "path": "./clickhouse" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "Database" + }, + { + "name": "prompt-engineering", + "source": { + "source": "local", + "path": "./prompt-engineering" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "Productivity" + }, + { + "name": "clickhouse-monitoring", + "source": { + "source": "local", + "path": "./clickhouse-monitoring" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "Monitoring" + }, + { + "name": "unsloth-training", + "source": { + "source": "local", + "path": "./unsloth-training" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "ML" + } + ] +} diff --git a/.github/workflows/pr-automation.yml b/.github/workflows/pr-automation.yml index c029456..fd1d616 100644 --- a/.github/workflows/pr-automation.yml +++ b/.github/workflows/pr-automation.yml @@ -34,36 +34,36 @@ jobs: # Area labels based on file paths if echo "$FILES" | grep -q "^src/api/\|^api/"; then - gh pr edit "$PR_NUMBER" --add-label "area:api" + gh pr edit "$PR_NUMBER" --add-label "area:api" || true fi if echo "$FILES" | grep -q "^src/frontend/\|^components/\|^ui/"; then - gh pr edit "$PR_NUMBER" --add-label "area:frontend" + gh pr edit "$PR_NUMBER" --add-label "area:frontend" || true fi if echo "$FILES" | grep -q "^src/backend/\|^server/"; then - gh pr edit "$PR_NUMBER" --add-label "area:backend" + gh pr edit "$PR_NUMBER" --add-label "area:backend" || true fi if echo "$FILES" | grep -q "^docs/\|\.md$"; then - gh pr edit "$PR_NUMBER" --add-label "area:docs" + gh pr edit "$PR_NUMBER" --add-label "area:docs" || true fi if echo "$FILES" | grep -q "^tests/\|^test/"; then - gh pr edit "$PR_NUMBER" --add-label "area:tests" + gh pr edit "$PR_NUMBER" --add-label "area:tests" || true fi # Check for breaking changes in commits COMMITS=$(gh api repos/${{ github.repository }}/pulls/$PR_NUMBER/commits --jq '.[].commit.message' | grep -i "BREAKING CHANGE" || true) if [ -n "$COMMITS" ]; then - gh pr edit "$PR_NUMBER" --add-label "type:breaking" + gh pr edit "$PR_NUMBER" --add-label "type:breaking" || true fi # Documentation only if echo "$FILES" | grep -v "^docs/\|\.md$" | grep -q .; then : else - gh pr edit "$PR_NUMBER" --add-label "documentation" + gh pr edit "$PR_NUMBER" --add-label "documentation" || true fi env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -101,7 +101,7 @@ jobs: PR_COUNT=$(gh pr list --author "$AUTHOR" --state all --limit 1 --json number --jq 'length') if [ "$PR_COUNT" -eq 1 ]; then - gh pr edit "$PR_NUMBER" --add-label "first-time-contributor" + gh pr edit "$PR_NUMBER" --add-label "first-time-contributor" || true gh pr comment "$PR_NUMBER" --body '## Welcome! 👋 diff --git a/.github/workflows/validate-plugins.yml b/.github/workflows/validate-plugins.yml index b610605..6b36129 100644 --- a/.github/workflows/validate-plugins.yml +++ b/.github/workflows/validate-plugins.yml @@ -6,12 +6,20 @@ on: branches: [main, master, "claude/**"] paths: - "**/.claude-plugin/plugin.json" + - "**/.codex-plugin/plugin.json" + - ".agents/plugins/marketplace.json" + - ".claude-plugin/marketplace.json" + - "marketplace.json" - "scripts/validate-plugins.sh" - ".github/workflows/validate-plugins.yml" pull_request: branches: [main, master] paths: - "**/.claude-plugin/plugin.json" + - "**/.codex-plugin/plugin.json" + - ".agents/plugins/marketplace.json" + - ".claude-plugin/marketplace.json" + - "marketplace.json" - "scripts/validate-plugins.sh" - ".github/workflows/validate-plugins.yml" diff --git a/CLAUDE.md b/CLAUDE.md index 57ce3c6..298aa34 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,6 +9,15 @@ Alternative: `npx skills add duyet/claude-plugins` ([skills.sh](https://skills.sh)) +## Plugin Metadata + +Codex plugin metadata is maintained beside Claude metadata in each plugin: + +- Claude manifest: `.claude-plugin/plugin.json` +- Codex manifest: `.codex-plugin/plugin.json` +- Claude marketplace: `marketplace.json` and `.claude-plugin/marketplace.json` +- Codex marketplace: `.agents/plugins/marketplace.json` + ## Versioning Follow semantic versioning (semver) for all plugins: @@ -33,6 +42,8 @@ Always update `plugin.json` version when making changes. plugin-name/ ├── .claude-plugin/ │ └── plugin.json # Manifest (name, version, description) +├── .codex-plugin/ +│ └── plugin.json # Codex manifest and interface metadata ├── agents/ # Sub-agent definitions ├── commands/ # Slash commands ├── skills/ # Reusable knowledge/procedures @@ -40,6 +51,8 @@ plugin-name/ └── README.md # Documentation ``` +When changing plugin metadata, keep Claude and Codex manifests in sync and run `bash scripts/validate-plugins.sh`. + ## Commit Convention Use semantic commits with plugin scope: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0c9a9df..107e3e2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing -Thanks for your interest in contributing to the Claude Plugins Marketplace! +Thanks for your interest in contributing to the Claude and Codex Plugins Marketplace! ## Quick Start @@ -16,6 +16,8 @@ Thanks for your interest in contributing to the Claude Plugins Marketplace! my-plugin/ ├── .claude-plugin/ │ └── plugin.json # Plugin manifest (name, version, description) +├── .codex-plugin/ +│ └── plugin.json # Codex manifest and interface metadata ├── README.md # Plugin documentation ├── CLAUDE.md # Claude-specific instructions (optional, for skills/agents) ├── agents/ # Agent definitions (optional) @@ -39,7 +41,30 @@ my-plugin/ } ``` -### 2. Documentation (README.md) +### 2. Codex Manifest (.codex-plugin/plugin.json) + +Every plugin also needs a Codex manifest with the same `name`, `version`, `description`, and `author.name` as the Claude manifest: + +```json +{ + "name": "my-plugin", + "version": "1.0.0", + "description": "A brief description of what this plugin does", + "author": { + "name": "your-username" + }, + "skills": "./skills/", + "interface": { + "displayName": "My Plugin", + "shortDescription": "A brief description of what this plugin does", + "developerName": "your-username", + "category": "Productivity", + "capabilities": ["Skill"] + } +} +``` + +### 3. Documentation (README.md) Required sections: - Description of what the plugin does @@ -48,14 +73,14 @@ Required sections: - Configuration options (if any) - Versioning guidelines (if applicable) -### 3. Claude Instructions (CLAUDE.md) +### 4. Claude Instructions (CLAUDE.md) Required for skills and agents: - How Claude should use the plugin - When to invoke it - Expected inputs and outputs -### 4. Update Marketplace +### 5. Update Marketplaces Add your plugin to `marketplace.json`: @@ -70,7 +95,24 @@ Add your plugin to `marketplace.json`: } ``` -### 5. Update Main README +Also add a Codex entry to `.agents/plugins/marketplace.json`: + +```json +{ + "name": "my-plugin", + "source": { + "source": "local", + "path": "./my-plugin" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "Productivity" +} +``` + +### 6. Update Main README Add your plugin to the main README.md table: @@ -94,6 +136,7 @@ Follow [Semantic Versioning](https://semver.org/): - **Major** (1.0.0 → 2.0.0): Breaking changes Update plugin.json version on every change. +Keep `.claude-plugin/plugin.json` and `.codex-plugin/plugin.json` in sync for shared metadata fields. ### Commits @@ -112,6 +155,7 @@ refactor(my-plugin): refactor code - Test your plugin thoroughly before submitting - Include usage examples in README - Consider edge cases and error handling +- Run `bash scripts/validate-plugins.sh` to validate Claude manifests, Codex manifests, and marketplace files ## Pull Request Process diff --git a/README.md b/README.md index b1aac8b..1f9eae7 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ [![GitHub Release](https://img.shields.io/github/v/duyet/claude-plugins?style=flat-square)](https://github.com/duyet/claude-plugins/releases) [![License](https://img.shields.io/github/license/duyet/claude-plugins?style=flat-square)](LICENSE) -[![Plugins](https://img.shields.io/badge/plugins-13-blue?style=flat-square)](#plugins-at-a-glance) +[![Plugins](https://img.shields.io/badge/plugins-14-blue?style=flat-square)](#plugins-at-a-glance) -> Extend Claude Code with specialized agents, commands, and skills. +> Extend Claude Code and Codex with specialized agents, commands, and skills. -A collection of production-quality plugins for Claude Code, including autonomous agents, workflow automation, and developer tools. +A collection of production-quality plugins for Claude Code and Codex, including autonomous agents, workflow automation, and developer tools. **Latest Release:** [v1.0.0](https://github.com/duyet/claude-plugins/releases/tag/v1.0.0) | [CHANGELOG](CHANGELOG.md) | [Issues](https://github.com/duyet/claude-plugins/issues) @@ -40,6 +40,14 @@ Install via the open [Skills](https://skills.sh) ecosystem, works with Claude Co npx skills add duyet/claude-plugins ``` +### Codex Metadata + +This repository also ships Codex plugin metadata in place: + +- Each plugin has a `.codex-plugin/plugin.json` beside its Claude `.claude-plugin/plugin.json`. +- Codex marketplace entries live in `.agents/plugins/marketplace.json`. +- Claude slash commands and agents stay in their original folders; Codex-facing wrapper skills live under `skills/*-workflow/SKILL.md` where a plugin needs a Codex entry point. + ## Usage Examples ### Parallel Team Execution @@ -272,13 +280,14 @@ Add to `~/.claude/settings.json`: ``` your-plugin/ ├── .claude-plugin/plugin.json # name, version, description +├── .codex-plugin/plugin.json # Codex manifest and interface metadata ├── agents/ # .md with YAML frontmatter ├── commands/ # slash commands ├── skills/ # reusable knowledge └── hooks/hooks.json # lifecycle hooks ``` -Update `marketplace.json` → PR → Done +Update `marketplace.json`, `.claude-plugin/marketplace.json`, `.agents/plugins/marketplace.json`, and both manifest files. Run `bash scripts/validate-plugins.sh` before opening a PR. --- diff --git a/clickhouse-monitoring/.codex-plugin/plugin.json b/clickhouse-monitoring/.codex-plugin/plugin.json new file mode 100644 index 0000000..6117908 --- /dev/null +++ b/clickhouse-monitoring/.codex-plugin/plugin.json @@ -0,0 +1,18 @@ +{ + "name": "clickhouse-monitoring", + "version": "1.0.1", + "description": "Agent Skill for the ClickHouse Monitor dashboard - a real-time monitoring and observability tool for ClickHouse clusters. Covers 45 dashboard pages, query performance analysis, table management, and API integration.", + "author": { + "name": "duyet" + }, + "skills": "./skills/", + "interface": { + "displayName": "Clickhouse Monitoring", + "shortDescription": "Agent Skill for the ClickHouse Monitor dashboard - a real-time monitoring and observability tool for ClickHouse clusters. Covers 45 dashboard pages, query performance analysis, table management, and API integration.", + "developerName": "duyet", + "category": "Monitoring", + "capabilities": [ + "Skill" + ] + } +} diff --git a/clickhouse/.codex-plugin/plugin.json b/clickhouse/.codex-plugin/plugin.json new file mode 100644 index 0000000..ee5e456 --- /dev/null +++ b/clickhouse/.codex-plugin/plugin.json @@ -0,0 +1,18 @@ +{ + "name": "clickhouse", + "version": "1.3.1", + "description": "ClickHouse expertise with 28 atomic review rules + 14 deep reference files. Rules synced with official ClickHouse/agent-skills repository. Schema design, query optimization, insert strategy, cluster management, backups, monitoring, and security best practices.", + "author": { + "name": "duyetbot" + }, + "skills": "./skills/", + "interface": { + "displayName": "Clickhouse", + "shortDescription": "ClickHouse expertise with 28 atomic review rules + 14 deep reference files. Rules synced with official ClickHouse/agent-skills repository. Schema design, query optimization, insert strategy, cluster management, backups, monitoring, and security best practices.", + "developerName": "duyetbot", + "category": "Database", + "capabilities": [ + "Skill" + ] + } +} diff --git a/commit/.codex-plugin/plugin.json b/commit/.codex-plugin/plugin.json new file mode 100644 index 0000000..95b6da4 --- /dev/null +++ b/commit/.codex-plugin/plugin.json @@ -0,0 +1,19 @@ +{ + "name": "commit", + "version": "1.3.1", + "description": "Create a Git commit with semantic commit message format and PR workflow with templates", + "author": { + "name": "duyet" + }, + "skills": "./skills/", + "interface": { + "displayName": "Commit", + "shortDescription": "Create a Git commit with semantic commit message format and PR workflow with templates", + "developerName": "duyet", + "category": "Git", + "capabilities": [ + "Skill", + "Command" + ] + } +} diff --git a/commit/skills/commit-workflow/SKILL.md b/commit/skills/commit-workflow/SKILL.md new file mode 100644 index 0000000..af905b5 --- /dev/null +++ b/commit/skills/commit-workflow/SKILL.md @@ -0,0 +1,23 @@ +--- +name: commit-workflow +description: Use when the user asks to create a semantic commit, commit and push, or create a pull request from local changes. +--- + +# Commit Workflow + +Use this skill to turn local repository changes into a clean semantic commit, and optionally push or open a pull request. + +## Workflow + +1. Inspect the current branch, git status, unstaged/staged diff, and recent commits. +2. Decide whether the change should be one commit or split into multiple focused commits. +3. Stage only the files that belong to the requested change. +4. Use semantic commit format: `type(scope): description`. +5. When creating a pull request, create a feature branch first if the current branch is the default branch. +6. Use the repo's existing pull request templates when present. + +## Safety + +- Do not include unrelated dirty work in the commit. +- Do not rewrite history unless the user explicitly asks. +- Prefer `--force-with-lease` over force push when a forced update is explicitly required. diff --git a/docs-generator/.codex-plugin/plugin.json b/docs-generator/.codex-plugin/plugin.json new file mode 100644 index 0000000..d77e867 --- /dev/null +++ b/docs-generator/.codex-plugin/plugin.json @@ -0,0 +1,21 @@ +{ + "name": "docs-generator", + "version": "1.0.0", + "description": "Automatically generate and maintain plugin documentation (README.md, CLAUDE.md)", + "author": { + "name": "duyet" + }, + "skills": "./skills/", + "hooks": "./hooks/hooks.json", + "interface": { + "displayName": "Docs Generator", + "shortDescription": "Automatically generate and maintain plugin documentation (README.md, CLAUDE.md)", + "developerName": "duyet", + "category": "Docs", + "capabilities": [ + "Skill", + "Command", + "Hook" + ] + } +} diff --git a/docs-generator/scripts/generate-docs.sh b/docs-generator/scripts/generate-docs.sh index 3f3d8f9..0964e15 100755 --- a/docs-generator/scripts/generate-docs.sh +++ b/docs-generator/scripts/generate-docs.sh @@ -1,6 +1,7 @@ #!/bin/bash # Main Documentation Generator # Generates root README.md and plugin CLAUDE.md files +# shellcheck disable=SC2034,SC2155 set -euo pipefail diff --git a/docs-generator/scripts/lib/feature-extractor.sh b/docs-generator/scripts/lib/feature-extractor.sh index dae06fd..25916ae 100755 --- a/docs-generator/scripts/lib/feature-extractor.sh +++ b/docs-generator/scripts/lib/feature-extractor.sh @@ -1,5 +1,6 @@ #!/bin/bash # Feature Extractor - Extract commands, agents, skills from plugin directories +# shellcheck disable=SC2034,SC2155 extract_commands() { local plugin_dir="$1" diff --git a/docs-generator/skills/docs-generator-workflow/SKILL.md b/docs-generator/skills/docs-generator-workflow/SKILL.md new file mode 100644 index 0000000..9ef6e14 --- /dev/null +++ b/docs-generator/skills/docs-generator-workflow/SKILL.md @@ -0,0 +1,20 @@ +--- +name: docs-generator +description: Use when regenerating or maintaining this plugin marketplace documentation from plugin manifests and component folders. +--- + +# Docs Generator + +Use this skill to regenerate and review the repository's plugin documentation. + +## Workflow + +1. Inspect plugin manifests and component folders before changing generated documentation. +2. Run `docs-generator/scripts/generate-docs.sh` from the repository root when regeneration is requested. +3. Review the generated root README and plugin CLAUDE files for accuracy. +4. Keep generated text grounded in manifest descriptions and actual files. + +## Notes + +- The Claude hook configuration lives in `hooks/hooks.json`. +- Keep Claude and Codex plugin metadata synchronized when both manifest families exist. diff --git a/duyetbot/.codex-plugin/plugin.json b/duyetbot/.codex-plugin/plugin.json new file mode 100644 index 0000000..07a5920 --- /dev/null +++ b/duyetbot/.codex-plugin/plugin.json @@ -0,0 +1,24 @@ +{ + "name": "duyetbot", + "version": "1.7.0", + "description": "Pragmatic software development companion with engineering discipline and transparent execution.", + "author": { + "name": "duyet" + }, + "skills": "./skills/", + "hooks": "./hooks/hooks.json", + "mcpServers": "./.mcp.json", + "interface": { + "displayName": "Duyetbot", + "shortDescription": "Pragmatic software development companion with engineering discipline and transparent execution.", + "developerName": "duyet", + "category": "Productivity", + "capabilities": [ + "Skill", + "Command", + "Agent", + "Hook", + "MCP" + ] + } +} diff --git a/duyetbot/scripts/fetch-duyet-data.sh b/duyetbot/scripts/fetch-duyet-data.sh index b43de43..4b67eda 100755 --- a/duyetbot/scripts/fetch-duyet-data.sh +++ b/duyetbot/scripts/fetch-duyet-data.sh @@ -25,7 +25,7 @@ echo -e "${BLUE}[1/4] Creating knowledge directory...${NC}" mkdir -p "$KNOWLEDGE_DIR" echo -e "${BLUE}[2/4] Fetching llms.txt sources...${NC}" -> "$DATA_FILE" +: > "$DATA_FILE" for source in "${SOURCES[@]}"; do IFS='|' read -r url name <<< "$source" diff --git a/duyetbot/skills/duyetbot-workflow/SKILL.md b/duyetbot/skills/duyetbot-workflow/SKILL.md new file mode 100644 index 0000000..1126c5e --- /dev/null +++ b/duyetbot/skills/duyetbot-workflow/SKILL.md @@ -0,0 +1,22 @@ +--- +name: duyetbot-workflow +description: Use when the user wants Duyetbot-style pragmatic engineering help, orchestration, learning, writing, or transparent task execution. +--- + +# Duyetbot Workflow + +Use this skill as the Codex entry point for the Duyetbot plugin's command and agent workflows. + +## Workflow + +1. Clarify the outcome only when repo inspection cannot resolve ambiguity. +2. Work in small loops: inspect, plan briefly, act, verify, and summarize. +3. Use the plugin's knowledge files for Duyet-specific profile, writing style, and topic context when relevant. +4. For orchestration requests, decompose the work into independent streams before delegating. +5. For learning requests, gather evidence before proposing durable knowledge updates. + +## Related Claude Assets + +- Commands in `commands/` cover summon, learn, orchestrate, spawn, think, and writing workflows. +- The `agents/duyetbot.md` file contains the Claude agent persona. +- Existing skills under `skills/` provide engineering discipline, team coordination, task loops, and transparency guidance. diff --git a/fix/.codex-plugin/plugin.json b/fix/.codex-plugin/plugin.json new file mode 100644 index 0000000..ecb2624 --- /dev/null +++ b/fix/.codex-plugin/plugin.json @@ -0,0 +1,19 @@ +{ + "name": "fix", + "version": "1.0.0", + "description": "Fix issues, tests, and CI failures with intelligent problem detection and resolution", + "author": { + "name": "duyet" + }, + "skills": "./skills/", + "interface": { + "displayName": "Fix", + "shortDescription": "Fix issues, tests, and CI failures with intelligent problem detection and resolution", + "developerName": "duyet", + "category": "Productivity", + "capabilities": [ + "Skill", + "Command" + ] + } +} diff --git a/fix/skills/fix-workflow/SKILL.md b/fix/skills/fix-workflow/SKILL.md new file mode 100644 index 0000000..e2f1e52 --- /dev/null +++ b/fix/skills/fix-workflow/SKILL.md @@ -0,0 +1,23 @@ +--- +name: fix-workflow +description: Use when the user asks to fix issues, tests, CI failures, or create/update/push a fix branch. +--- + +# Fix Workflow + +Use this skill to diagnose and repair repository failures with focused changes. + +## Workflow + +1. Identify the project type and the most relevant checks from repository files. +2. Reproduce the failure when possible before editing. +3. Make the smallest change that addresses the root cause. +4. Run targeted verification first, then broader checks when the change is shared or risky. +5. For PR/update flows, commit with semantic format and preserve unrelated local work. + +## Supported Checks + +- Python: pytest, ruff, mypy +- Node/TypeScript: package scripts, tsc, eslint, jest, vitest +- Rust: cargo test, clippy +- Go: go test and lint tools when configured diff --git a/frontend-design/.codex-plugin/plugin.json b/frontend-design/.codex-plugin/plugin.json new file mode 100644 index 0000000..e05a54e --- /dev/null +++ b/frontend-design/.codex-plugin/plugin.json @@ -0,0 +1,19 @@ +{ + "name": "frontend-design", + "version": "1.3.0", + "description": "Create distinctive, production-grade frontend interfaces avoiding AI slop aesthetics. Emphasizes shadcn/ui, Recharts, and bold design choices.", + "author": { + "name": "duyet", + "email": "me@duyet.net" + }, + "skills": "./skills/", + "interface": { + "displayName": "Frontend Design", + "shortDescription": "Create distinctive, production-grade frontend interfaces avoiding AI slop aesthetics. Emphasizes shadcn/ui, Recharts, and bold design choices.", + "developerName": "duyet", + "category": "Design", + "capabilities": [ + "Skill" + ] + } +} diff --git a/github/.codex-plugin/plugin.json b/github/.codex-plugin/plugin.json new file mode 100644 index 0000000..502a659 --- /dev/null +++ b/github/.codex-plugin/plugin.json @@ -0,0 +1,20 @@ +{ + "name": "github", + "version": "1.4.1", + "description": "GitHub operations using gh CLI - PRs, workflows, issues, repositories, batch operations, PR babysitting with AI review bot integration, and smart branch detection", + "author": { + "name": "duyetbot" + }, + "license": "MIT", + "skills": "./skills/", + "interface": { + "displayName": "GitHub", + "shortDescription": "GitHub operations using gh CLI - PRs, workflows, issues, repositories, batch operations, PR babysitting with AI review bot integration, and smart branch detection", + "developerName": "duyetbot", + "category": "Git", + "capabilities": [ + "Skill", + "Command" + ] + } +} diff --git a/github/examples/babysit-pr.sh b/github/examples/babysit-pr.sh index 5a06950..5911f94 100755 --- a/github/examples/babysit-pr.sh +++ b/github/examples/babysit-pr.sh @@ -1,6 +1,7 @@ #!/bin/bash # Babysit PR - Monitor, fix review bot suggestions, resolve conflicts, merge # Usage: ./babysit-pr.sh [--auto-merge] [--max-iterations N] [--dry-run] [--pr NUMBER] +# shellcheck disable=SC2034 set -euo pipefail diff --git a/github/skills/github-workflow/SKILL.md b/github/skills/github-workflow/SKILL.md new file mode 100644 index 0000000..6b8abe6 --- /dev/null +++ b/github/skills/github-workflow/SKILL.md @@ -0,0 +1,22 @@ +--- +name: github-workflow +description: Use when managing GitHub pull requests, issues, workflows, releases, or review-bot feedback with the gh CLI. +--- + +# GitHub Workflow + +Use this skill for GitHub operations in repositories that have the `gh` CLI available. + +## Workflow + +1. Inspect repository remote, current branch, and GitHub authentication before mutating remote state. +2. Use `gh pr`, `gh issue`, `gh run`, `gh workflow`, and `gh api` for GitHub operations. +3. For review-bot feedback, fetch inline comments and review bodies, then fix only actionable issues. +4. For CI failures, inspect failed run logs before changing code. +5. For merges, confirm branch status, required checks, and review state. + +## Safety + +- Avoid admin merges unless the user explicitly permits them. +- Preserve unrelated local changes. +- Prefer non-interactive `gh` commands with explicit flags. diff --git a/interview/.codex-plugin/plugin.json b/interview/.codex-plugin/plugin.json new file mode 100644 index 0000000..e2d345e --- /dev/null +++ b/interview/.codex-plugin/plugin.json @@ -0,0 +1,20 @@ +{ + "name": "interview", + "version": "1.0.0", + "description": "Conduct in-depth requirements interviews using Socratic questioning to clarify implementation details before coding", + "author": { + "name": "duyet", + "email": "me@duyet.net" + }, + "skills": "./skills/", + "interface": { + "displayName": "Interview", + "shortDescription": "Conduct in-depth requirements interviews using Socratic questioning to clarify implementation details before coding", + "developerName": "duyet", + "category": "Productivity", + "capabilities": [ + "Skill", + "Command" + ] + } +} diff --git a/interview/skills/interview-workflow/SKILL.md b/interview/skills/interview-workflow/SKILL.md new file mode 100644 index 0000000..ef770e4 --- /dev/null +++ b/interview/skills/interview-workflow/SKILL.md @@ -0,0 +1,20 @@ +--- +name: interview +description: Use when the user wants a Socratic requirements interview before implementation. +--- + +# Interview + +Use this skill to clarify requirements before coding when the task has meaningful product or technical ambiguity. + +## Workflow + +1. Ground the discussion in any existing repository files, designs, schemas, or docs. +2. Ask concise questions that change the implementation plan. +3. Separate product intent from implementation preferences. +4. Turn answers into a decision-complete plan with success criteria, scope, edge cases, and tests. +5. Stop interviewing once the plan is specific enough to implement. + +## Output + +Prefer a compact plan with summary, key changes, test plan, and assumptions. diff --git a/orchestration/.codex-plugin/plugin.json b/orchestration/.codex-plugin/plugin.json new file mode 100644 index 0000000..635c7a3 --- /dev/null +++ b/orchestration/.codex-plugin/plugin.json @@ -0,0 +1,18 @@ +{ + "name": "orchestration", + "version": "1.0.0", + "description": "Orchestrator skill for managing parallel agent workstreams. Transform complex requests into coordinated multi-agent execution with elegant result synthesis.", + "author": { + "name": "duyet" + }, + "skills": "./skills/", + "interface": { + "displayName": "Orchestration", + "shortDescription": "Orchestrator skill for managing parallel agent workstreams. Transform complex requests into coordinated multi-agent execution with elegant result synthesis.", + "developerName": "duyet", + "category": "Agents", + "capabilities": [ + "Skill" + ] + } +} diff --git a/prompt-engineering/.codex-plugin/plugin.json b/prompt-engineering/.codex-plugin/plugin.json new file mode 100644 index 0000000..5e51f0d --- /dev/null +++ b/prompt-engineering/.codex-plugin/plugin.json @@ -0,0 +1,18 @@ +{ + "name": "prompt-engineering", + "version": "1.0.0", + "description": "Prompt engineering skills for Grok, Claude, and Gemini LLMs with model-specific guidance and universal techniques", + "author": { + "name": "duyetbot" + }, + "skills": "./skills/", + "interface": { + "displayName": "Prompt Engineering", + "shortDescription": "Prompt engineering skills for Grok, Claude, and Gemini LLMs with model-specific guidance and universal techniques", + "developerName": "duyetbot", + "category": "Productivity", + "capabilities": [ + "Skill" + ] + } +} diff --git a/scripts/validate-plugins.sh b/scripts/validate-plugins.sh old mode 100644 new mode 100755 index acfe34f..19150af --- a/scripts/validate-plugins.sh +++ b/scripts/validate-plugins.sh @@ -1,52 +1,46 @@ #!/usr/bin/env bash -# Validate all plugin.json manifests in the repository. -# Exits 1 if any plugin fails validation. +# Validate Claude and Codex plugin manifests plus marketplace files. +# Exits 1 if any plugin or marketplace fails validation. set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" FAILED=0 CHECKED=0 +TMP_ERR="${TMPDIR:-/tmp}/plugin-validate-err.$$" +trap 'rm -f "$TMP_ERR"' EXIT -validate_plugin() { +validate_manifest() { local manifest="$1" + local mode="$2" local plugin_dir plugin_dir="$(dirname "$(dirname "$manifest")")" - local plugin_name - plugin_name="$(basename "$plugin_dir")" - - # 1. Valid JSON - if ! python3 -c "import json, sys; json.load(open('$manifest'))" 2>/dev/null; then - echo "❌ $plugin_name: invalid JSON" - return 1 - fi - - local errors=() - # 2. Run all checks via Python - python3 - "$manifest" "$plugin_dir" <<'PYEOF' -import json, sys, os, re + python3 - "$manifest" "$plugin_dir" "$mode" <<'PYEOF' +import json +import os +import re +import sys -manifest_path = sys.argv[1] -plugin_dir = sys.argv[2] +manifest_path, plugin_dir, mode = sys.argv[1:4] errors = [] -with open(manifest_path) as f: - data = json.load(f) +try: + with open(manifest_path) as f: + data = json.load(f) +except Exception as exc: + print(f" - invalid JSON: {exc}", file=sys.stderr) + sys.exit(1) -# Required string fields for field in ("name", "version", "description"): if field not in data: errors.append(f"missing required field: '{field}'") elif not isinstance(data[field], str) or not data[field].strip(): errors.append(f"'{field}' must be a non-empty string") -# Semver: version must match X.Y.Z -if "version" in data and isinstance(data["version"], str): - if not re.fullmatch(r"\d+\.\d+\.\d+", data["version"]): - errors.append(f"'version' must be semver (X.Y.Z), got: {data['version']!r}") +if isinstance(data.get("version"), str) and not re.fullmatch(r"\d+\.\d+\.\d+", data["version"]): + errors.append(f"'version' must be semver (X.Y.Z), got: {data['version']!r}") -# Author must be object with a 'name' key author = data.get("author") if author is None: errors.append("missing required field: 'author'") @@ -54,56 +48,221 @@ elif isinstance(author, str): errors.append(f"'author' must be an object with a 'name' key, got string: {author!r}") elif not isinstance(author, dict): errors.append(f"'author' must be an object, got: {type(author).__name__}") -elif "name" not in author or not isinstance(author["name"], str) or not author["name"].strip(): +elif not isinstance(author.get("name"), str) or not author["name"].strip(): errors.append("'author.name' must be a non-empty string") -# Skills array validation (optional field) skills = data.get("skills") -if skills is not None: - if not isinstance(skills, list): - errors.append("'skills' must be an array") +if isinstance(skills, list): + for i, skill in enumerate(skills): + if not isinstance(skill, dict): + errors.append(f"skills[{i}]: must be an object") + continue + if not isinstance(skill.get("name"), str) or not skill["name"].strip(): + errors.append(f"skills[{i}]: missing 'name'") + if not isinstance(skill.get("description"), str) or not skill["description"].strip(): + errors.append(f"skills[{i}]: missing 'description'") + path_value = skill.get("path") + if path_value is not None: + if not isinstance(path_value, str): + errors.append(f"skills[{i}]: 'path' must be a string") + elif not os.path.isfile(os.path.join(plugin_dir, path_value)): + errors.append(f"skills[{i}]: path '{path_value}' does not exist") + +if mode == "codex": + for field in ("skills", "hooks", "mcpServers", "apps"): + value = data.get(field) + if value is None: + continue + if not isinstance(value, str) or not value.startswith("./"): + errors.append(f"'{field}' must be a relative string beginning with './'") + continue + target = os.path.normpath(os.path.join(plugin_dir, value)) + if not os.path.exists(target): + errors.append(f"'{field}' path does not exist: {value}") + + interface = data.get("interface") + if not isinstance(interface, dict): + errors.append("'interface' must be an object") else: - for i, skill in enumerate(skills): - if not isinstance(skill, dict): - errors.append(f"skills[{i}]: must be an object") - continue - if "name" not in skill or not isinstance(skill["name"], str) or not skill["name"].strip(): - errors.append(f"skills[{i}]: missing 'name'") - if "description" not in skill or not isinstance(skill["description"], str) or not skill["description"].strip(): - errors.append(f"skills[{i}]: missing 'description'") - if "path" in skill: - skill_path = os.path.join(plugin_dir, skill["path"]) - if not os.path.isfile(skill_path): - errors.append(f"skills[{i}]: path '{skill['path']}' does not exist") + for field in ("displayName", "shortDescription", "developerName", "category"): + if not isinstance(interface.get(field), str) or not interface[field].strip(): + errors.append(f"'interface.{field}' must be a non-empty string") + capabilities = interface.get("capabilities") + if capabilities is not None: + if not isinstance(capabilities, list) or not all(isinstance(item, str) and item.strip() for item in capabilities): + errors.append("'interface.capabilities' must be an array of non-empty strings") if errors: - for e in errors: - print(f" - {e}", file=sys.stderr) + for error in errors: + print(f" - {error}", file=sys.stderr) sys.exit(1) PYEOF } -echo "Validating plugin manifests..." -echo "" - -while IFS= read -r -d '' manifest; do - plugin_dir="$(dirname "$(dirname "$manifest")")" - plugin_name="$(basename "$plugin_dir")" +check() { + local label="$1" + shift CHECKED=$((CHECKED + 1)) - - if validate_plugin "$manifest" 2>/tmp/plugin-validate-err; then - echo "✅ $plugin_name" + if "$@" 2>"$TMP_ERR"; then + echo "✅ $label" else - echo "❌ $plugin_name" + echo "❌ $label" while IFS= read -r line; do echo " $line" - done < /tmp/plugin-validate-err + done < "$TMP_ERR" FAILED=$((FAILED + 1)) fi +} + +validate_cross_manifest() { + python3 - "$REPO_ROOT" <<'PYEOF' +import json +import os +import sys + +repo_root = sys.argv[1] +errors = [] + +plugin_dirs = sorted( + name for name in os.listdir(repo_root) + if ( + os.path.isfile(os.path.join(repo_root, name, ".claude-plugin", "plugin.json")) + or os.path.isfile(os.path.join(repo_root, name, ".codex-plugin", "plugin.json")) + ) +) + +for plugin in plugin_dirs: + claude_path = os.path.join(repo_root, plugin, ".claude-plugin", "plugin.json") + codex_path = os.path.join(repo_root, plugin, ".codex-plugin", "plugin.json") + if not os.path.isfile(claude_path): + errors.append(f"{plugin}: missing .claude-plugin/plugin.json") + continue + if not os.path.isfile(codex_path): + errors.append(f"{plugin}: missing .codex-plugin/plugin.json") + continue + + with open(claude_path) as f: + claude = json.load(f) + with open(codex_path) as f: + codex = json.load(f) + + for field in ("name", "version", "description"): + if claude.get(field) != codex.get(field): + errors.append(f"{plugin}: {field} differs between Claude and Codex manifests") + if claude.get("author", {}).get("name") != codex.get("author", {}).get("name"): + errors.append(f"{plugin}: author.name differs between Claude and Codex manifests") + +if errors: + for error in errors: + print(f" - {error}", file=sys.stderr) + sys.exit(1) +PYEOF +} + +validate_marketplaces() { + python3 - "$REPO_ROOT" <<'PYEOF' +import json +import os +import sys + +repo_root = sys.argv[1] +errors = [] + +plugin_dirs = sorted( + name for name in os.listdir(repo_root) + if os.path.isfile(os.path.join(repo_root, name, ".claude-plugin", "plugin.json")) +) +plugin_set = set(plugin_dirs) + +def load_json(relpath): + try: + with open(os.path.join(repo_root, relpath)) as f: + return json.load(f) + except (FileNotFoundError, OSError, json.JSONDecodeError) as exc: + errors.append(f"{relpath}: {exc}") + return None + +def require(condition, message): + if not condition: + errors.append(message) + +root_marketplace = load_json("marketplace.json") +if root_marketplace is None: + root_marketplace = {"plugins": []} +root_names = [plugin.get("name") for plugin in root_marketplace.get("plugins", [])] +require(len(root_names) == len(set(root_names)), "marketplace.json contains duplicate plugin names") +require(set(root_names) == plugin_set, "marketplace.json plugin names must match plugin directories") +for plugin in root_marketplace.get("plugins", []): + name = plugin.get("name") + for field in ("name", "id", "description", "version", "type", "category"): + value = plugin.get(field) + require(isinstance(value, str) and value.strip(), f"marketplace.json {name}: missing {field}") + require(plugin.get("id") == f"{name}@{root_marketplace.get('name')}", f"marketplace.json {name}: id does not match marketplace name") + +claude_marketplace = load_json(".claude-plugin/marketplace.json") +if claude_marketplace is None: + claude_marketplace = {"plugins": []} +claude_names = [plugin.get("name") for plugin in claude_marketplace.get("plugins", [])] +require(set(claude_names).issubset(plugin_set), ".claude-plugin/marketplace.json references unknown plugin") +for plugin in claude_marketplace.get("plugins", []): + name = plugin.get("name") + source = plugin.get("source") + require(isinstance(source, str) and source.startswith("./"), f".claude-plugin/marketplace.json {name}: source must be relative string") + if isinstance(source, str): + require(os.path.isdir(os.path.join(repo_root, source)), f".claude-plugin/marketplace.json {name}: source path does not exist") + +codex_marketplace = load_json(".agents/plugins/marketplace.json") +if codex_marketplace is None: + codex_marketplace = {"plugins": []} +codex_names = [plugin.get("name") for plugin in codex_marketplace.get("plugins", [])] +require(len(codex_names) == len(set(codex_names)), ".agents/plugins/marketplace.json contains duplicate plugin names") +require(set(codex_names) == plugin_set, ".agents/plugins/marketplace.json plugin names must match plugin directories") +require(isinstance(codex_marketplace.get("interface", {}).get("displayName"), str), ".agents/plugins/marketplace.json missing interface.displayName") +for plugin in codex_marketplace.get("plugins", []): + name = plugin.get("name") + source = plugin.get("source") + policy = plugin.get("policy") + require(isinstance(source, dict), f".agents/plugins/marketplace.json {name}: source must be object") + if isinstance(source, dict): + require(source.get("source") == "local", f".agents/plugins/marketplace.json {name}: source.source must be local") + path_value = source.get("path") + require(isinstance(path_value, str) and path_value.startswith("./"), f".agents/plugins/marketplace.json {name}: source.path must be relative string") + if isinstance(path_value, str): + require(os.path.isdir(os.path.join(repo_root, path_value)), f".agents/plugins/marketplace.json {name}: source.path does not exist") + require(isinstance(policy, dict), f".agents/plugins/marketplace.json {name}: policy must be object") + if isinstance(policy, dict): + require(policy.get("installation") in {"NOT_AVAILABLE", "AVAILABLE", "INSTALLED_BY_DEFAULT"}, f".agents/plugins/marketplace.json {name}: invalid policy.installation") + require(policy.get("authentication") in {"ON_INSTALL", "ON_USE"}, f".agents/plugins/marketplace.json {name}: invalid policy.authentication") + category = plugin.get("category") + require(isinstance(category, str) and category.strip(), f".agents/plugins/marketplace.json {name}: missing category") + +if errors: + for error in errors: + print(f" - {error}", file=sys.stderr) + sys.exit(1) +PYEOF +} + +echo "Validating plugin manifests..." +echo "" + +while IFS= read -r -d '' manifest; do + plugin_dir="$(dirname "$(dirname "$manifest")")" + plugin_name="$(basename "$plugin_dir")" + check "Claude manifest: $plugin_name" validate_manifest "$manifest" claude done < <(find "$REPO_ROOT" -path "*/.claude-plugin/plugin.json" -not -path "*/node_modules/*" -print0 | sort -z) +while IFS= read -r -d '' manifest; do + plugin_dir="$(dirname "$(dirname "$manifest")")" + plugin_name="$(basename "$plugin_dir")" + check "Codex manifest: $plugin_name" validate_manifest "$manifest" codex +done < <(find "$REPO_ROOT" -path "*/.codex-plugin/plugin.json" -not -path "*/node_modules/*" -print0 | sort -z) + +check "Claude/Codex manifest parity" validate_cross_manifest +check "Marketplace files" validate_marketplaces + echo "" -echo "Checked $CHECKED plugin(s). $FAILED failed." +echo "Checked $CHECKED item(s). $FAILED failed." if [[ $FAILED -gt 0 ]]; then exit 1 diff --git a/statusline/.codex-plugin/plugin.json b/statusline/.codex-plugin/plugin.json new file mode 100644 index 0000000..92f1b52 --- /dev/null +++ b/statusline/.codex-plugin/plugin.json @@ -0,0 +1,21 @@ +{ + "name": "statusline", + "version": "1.7.0", + "description": "Multi-provider status bar with context health, API rate limits (Anthropic 5h/7d + z.ai GLM tokens/tools), git branch, and active tools. Auto-detects provider from model name. Supports 1/2/3 line layouts with smart hiding.", + "author": { + "name": "duyet" + }, + "skills": "./skills/", + "hooks": "./hooks/hooks.json", + "interface": { + "displayName": "Statusline", + "shortDescription": "Multi-provider status bar with context health, API rate limits (Anthropic 5h/7d + z.ai GLM tokens/tools), git branch, and active tools. Auto-detects provider from model name. Supports 1/2/3 line layouts with smart hiding.", + "developerName": "duyet", + "category": "UI", + "capabilities": [ + "Skill", + "Command", + "Hook" + ] + } +} diff --git a/statusline/hooks/session-start.sh b/statusline/hooks/session-start.sh index 2170b8b..e131fd7 100755 --- a/statusline/hooks/session-start.sh +++ b/statusline/hooks/session-start.sh @@ -1,6 +1,7 @@ #!/bin/bash # Statusline SessionStart hook # Outputs initial status line and enables monitoring for the session +# shellcheck disable=SC2034 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PLUGIN_ROOT="$(dirname "$SCRIPT_DIR")" diff --git a/statusline/skills/statusline-workflow/SKILL.md b/statusline/skills/statusline-workflow/SKILL.md new file mode 100644 index 0000000..acfed63 --- /dev/null +++ b/statusline/skills/statusline-workflow/SKILL.md @@ -0,0 +1,22 @@ +--- +name: statusline +description: Use when configuring, previewing, troubleshooting, or disabling the Claude Code statusline plugin. +--- + +# Statusline + +Use this skill to help configure and troubleshoot the statusline plugin. + +## Workflow + +1. Inspect the existing statusline config and scripts before changing settings. +2. For setup, choose layout, visible sections, context display style, separator, and color style. +3. Update the user's Claude settings only when requested or clearly part of setup. +4. Preview the generated statusline command with representative session input. +5. For troubleshooting, check provider detection, rate-limit fields, and empty-section hiding. + +## Files + +- Commands live in `commands/`. +- Hook configuration lives in `hooks/hooks.json`. +- Runtime scripts live in `scripts/`. diff --git a/team-agents/.codex-plugin/plugin.json b/team-agents/.codex-plugin/plugin.json new file mode 100644 index 0000000..c5f89f3 --- /dev/null +++ b/team-agents/.codex-plugin/plugin.json @@ -0,0 +1,20 @@ +{ + "name": "team-agents", + "version": "1.7.0", + "description": "Leader and Senior Engineer agents for coordinated parallel task execution. Leader breaks down complex tasks and delegates to multiple senior engineers working in parallel.", + "author": { + "name": "duyet" + }, + "skills": "./skills/", + "interface": { + "displayName": "Team Agents", + "shortDescription": "Leader and Senior Engineer agents for coordinated parallel task execution. Leader breaks down complex tasks and delegates to multiple senior engineers working in parallel.", + "developerName": "duyet", + "category": "Agents", + "capabilities": [ + "Skill", + "Command", + "Agent" + ] + } +} diff --git a/team-agents/skills/team-agents-workflow/SKILL.md b/team-agents/skills/team-agents-workflow/SKILL.md new file mode 100644 index 0000000..c75f9d9 --- /dev/null +++ b/team-agents/skills/team-agents-workflow/SKILL.md @@ -0,0 +1,21 @@ +--- +name: team-agents-workflow +description: Use when the user wants coordinated multi-agent planning, delegation, or role-based engineering guidance. +--- + +# Team Agents Workflow + +Use this skill as the Codex entry point for the team-agents plugin. + +## Workflow + +1. Decide whether the task truly benefits from multiple roles or parallel work. +2. Decompose work into independent streams with clear ownership and outputs. +3. Assign work according to the available agent role guidance in `agents/`. +4. Keep implementation ownership disjoint when multiple workers edit files. +5. Integrate results, verify the combined change, and summarize residual risk. + +## Related Assets + +- Agent role definitions live in `agents/`. +- Existing skills cover backend APIs, React/Next.js, TypeScript, task decomposition, and quality gates. diff --git a/unsloth-training/.codex-plugin/plugin.json b/unsloth-training/.codex-plugin/plugin.json new file mode 100644 index 0000000..4993cde --- /dev/null +++ b/unsloth-training/.codex-plugin/plugin.json @@ -0,0 +1,19 @@ +{ + "name": "unsloth-training", + "version": "1.0.0", + "description": "Fine-tune LLMs with Unsloth using GRPO or SFT. Includes dataset preparation, synthetic data generation, environment flags, MLX (Apple Silicon), FP8, vision models, mobile export, GGUF.", + "author": { + "name": "duyet" + }, + "license": "MIT", + "skills": "./skills/", + "interface": { + "displayName": "Unsloth Training", + "shortDescription": "Fine-tune LLMs with Unsloth using GRPO or SFT. Includes dataset preparation, synthetic data generation, environment flags, MLX (Apple Silicon), FP8, vision models, mobile export, GGUF.", + "developerName": "duyet", + "category": "ML", + "capabilities": [ + "Skill" + ] + } +}