mirror of
https://github.com/IgorWarzocha/Opencode-Workflows.git
synced 2026-09-14 16:22:53 +08:00
Cli (#2)
* Implement pack registry system and management CLI Add registry.json manifests to agent packs and commands to support structured discovery. Include a CLI for project initialization, a registry generator script, and a plugin version locker. Add a new learn command for session documentation. * Add CLI and centralized workflow registry Implement an interactive CLI tool to browse and install workflow packs. Consolidate decentralized manifests into a single root registry for easier maintenance. * update * buffer * Rewrite CLI as TUI workflow selector Replace the Commander-based CLI with a TUI built on Bun, Solid.js, and OpenTUI. This version provides hierarchical navigation for selecting workflow packs and standalone items. - Migrate code from packages/cli to howaboua-cli - Support global (~/.config/opencode/) and local (.opencode/) sync - Add one-shot verification rules to howaboua-cli/AGENTS.md * chore: ignore @howaboua-cli directory --------- Co-authored-by: OpenCode Agent <agent@opencode.ai>
This commit is contained in:
@@ -3,3 +3,4 @@ package.json
|
||||
bun.lock
|
||||
.gitignore
|
||||
remember.jsonc
|
||||
@howaboua-cli/
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "component-engineer",
|
||||
"description": "Professional React component engineering and audit pack.",
|
||||
"items": [
|
||||
{
|
||||
"name": "component-engineer",
|
||||
"description": "Expert React architecture specialist.",
|
||||
"type": "agent",
|
||||
"path": ".opencode/agent/component-engineer.md",
|
||||
"target": "agent/component-engineer.md"
|
||||
},
|
||||
{
|
||||
"name": "component-create",
|
||||
"description": "Create spec-compliant React components.",
|
||||
"type": "command",
|
||||
"path": ".opencode/command/component-create.md",
|
||||
"target": "command/component-create.md"
|
||||
},
|
||||
{
|
||||
"name": "component-review",
|
||||
"description": "Audit components against engineering spec.",
|
||||
"type": "command",
|
||||
"path": ".opencode/command/component-review.md",
|
||||
"target": "command/component-review.md"
|
||||
},
|
||||
{
|
||||
"name": "component-engineering",
|
||||
"description": "Component engineering specification skill.",
|
||||
"type": "skill",
|
||||
"path": ".opencode/skill/component-engineering",
|
||||
"target": "skill/component-engineering"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "create-opencode-plugin",
|
||||
"description": "Opencode plugin development and scaffolding pack. Works best when installed in opencode repository.",
|
||||
"items": [
|
||||
{
|
||||
"name": "plugin-creator",
|
||||
"description": "Specialist for building opencode plugins.",
|
||||
"type": "agent",
|
||||
"path": ".opencode/agent/plugin-creator.md",
|
||||
"target": "agent/plugin-creator.md"
|
||||
},
|
||||
{
|
||||
"name": "create-plugin",
|
||||
"description": "Scaffold a new opencode plugin based on description.",
|
||||
"type": "command",
|
||||
"path": ".opencode/command/create-plugin.md",
|
||||
"target": "command/create-plugin.md"
|
||||
},
|
||||
{
|
||||
"name": "create-opencode-plugin",
|
||||
"description": "Plugin development workflow skill.",
|
||||
"type": "skill",
|
||||
"path": ".opencode/skill/create-opencode-plugin",
|
||||
"target": "skill/create-opencode-plugin"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "generic",
|
||||
"description": "Canonical agents for global use.",
|
||||
"items": [
|
||||
{
|
||||
"name": "fast",
|
||||
"description": "High-speed atomic task workhorse. Configure to a model like Haiku. Pair with `smart`.",
|
||||
"type": "agent",
|
||||
"path": ".opencode/agent/fast.md",
|
||||
"target": "agent/fast.md"
|
||||
},
|
||||
{
|
||||
"name": "smart",
|
||||
"description": "Senior architect for complex refactoring. Configure to a model like Opus. Pair with `fast`.",
|
||||
"type": "agent",
|
||||
"path": ".opencode/agent/smart.md",
|
||||
"target": "agent/smart.md"
|
||||
},
|
||||
{
|
||||
"name": "subagent-orchestrator",
|
||||
"description": "Disciplined subagent dispatcher and coordinator.",
|
||||
"type": "agent",
|
||||
"path": ".opencode/agent/subagent-orchestrator.md",
|
||||
"target": "agent/subagent-orchestrator.md"
|
||||
},
|
||||
{
|
||||
"name": "openspec-orchestrator",
|
||||
"description": "Enforce strict OpenSpec formatting/validation. For use with Openspec only.",
|
||||
"type": "agent",
|
||||
"path": ".opencode/agent/openspec-orchestrator.md",
|
||||
"target": "agent/openspec-orchestrator.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"name": "opencode-configurator",
|
||||
"description": "Meta-configuration management system pack.",
|
||||
"items": [
|
||||
{
|
||||
"name": "opencode-configurator",
|
||||
"description": "Opencode configuration agent.",
|
||||
"type": "agent",
|
||||
"path": "agent/opencode-configurator.md",
|
||||
"target": "agent/opencode-configurator.md"
|
||||
},
|
||||
{
|
||||
"name": "refactor-rfc-xml",
|
||||
"description": "Structure markdown files with RFC/XML for better agent adherence.",
|
||||
"type": "command",
|
||||
"path": "command/refactor-rfc-xml.md",
|
||||
"target": "command/refactor-rfc-xml.md"
|
||||
},
|
||||
{
|
||||
"name": "permissions-update",
|
||||
"description": "Modernize configuration schema fields.",
|
||||
"type": "command",
|
||||
"path": "command/permissions-update.md",
|
||||
"target": "command/permissions-update.md"
|
||||
},
|
||||
{
|
||||
"name": "agent-architect",
|
||||
"description": "Design and refine agents.",
|
||||
"type": "skill",
|
||||
"path": "skill/agent-architect",
|
||||
"target": "skill/agent-architect"
|
||||
},
|
||||
{
|
||||
"name": "command-creator",
|
||||
"description": "Build custom slash commands.",
|
||||
"type": "skill",
|
||||
"path": "skill/command-creator",
|
||||
"target": "skill/command-creator"
|
||||
},
|
||||
{
|
||||
"name": "mcp-installer",
|
||||
"description": "Configure MCP server connections.",
|
||||
"type": "skill",
|
||||
"path": "skill/mcp-installer",
|
||||
"target": "skill/mcp-installer"
|
||||
},
|
||||
{
|
||||
"name": "model-researcher",
|
||||
"description": "Add verified AI models.",
|
||||
"type": "skill",
|
||||
"path": "skill/model-researcher",
|
||||
"target": "skill/model-researcher"
|
||||
},
|
||||
{
|
||||
"name": "opencode-config",
|
||||
"description": "Guided opencode.json setup.",
|
||||
"type": "skill",
|
||||
"path": "skill/opencode-config",
|
||||
"target": "skill/opencode-config"
|
||||
},
|
||||
{
|
||||
"name": "plugin-installer",
|
||||
"description": "Install and document community ecosystem plugins.",
|
||||
"type": "skill",
|
||||
"path": "skill/plugin-installer",
|
||||
"target": "skill/plugin-installer"
|
||||
},
|
||||
{
|
||||
"name": "skill-creator",
|
||||
"description": "Guide for creating skills.",
|
||||
"type": "skill",
|
||||
"path": "skill/skill-creator",
|
||||
"target": "skill/skill-creator"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "parallel-PRD",
|
||||
"description": "Parallel PRD planning and Best-of-N synthesis pack.",
|
||||
"items": [
|
||||
{
|
||||
"name": "parallel-prd-orchestrator",
|
||||
"description": "Orchestrates parallel PRD planners.",
|
||||
"type": "agent",
|
||||
"path": ".opencode/agent/parallel-prd-orchestrator.md",
|
||||
"target": "agent/parallel-prd-orchestrator.md"
|
||||
},
|
||||
{
|
||||
"name": "glm-planner",
|
||||
"description": "GLM-based PRD planner subagent example.",
|
||||
"type": "agent",
|
||||
"path": ".opencode/agent/glm-planner.md",
|
||||
"target": "agent/glm-planner.md"
|
||||
},
|
||||
{
|
||||
"name": "TEMPLATE-planner",
|
||||
"description": "Standard PRD planner template agent. Copy, rename, set your own model",
|
||||
"type": "agent",
|
||||
"path": ".opencode/agent/TEMPLATE-planner.md",
|
||||
"target": "agent/TEMPLATE-planner.md"
|
||||
},
|
||||
{
|
||||
"name": "parallel-prd",
|
||||
"description": "Generate parallel PRD architecture views.",
|
||||
"type": "command",
|
||||
"path": ".opencode/command/parallel-prd.md",
|
||||
"target": "command/parallel-prd.md"
|
||||
},
|
||||
{
|
||||
"name": "prd-authoring",
|
||||
"description": "Skill for drafting technical PRDs.",
|
||||
"type": "skill",
|
||||
"path": ".opencode/skill/prd-authoring",
|
||||
"target": "skill/prd-authoring"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
description: Document session findings
|
||||
---
|
||||
|
||||
<summary>
|
||||
You MUST document session findings and workarounds.
|
||||
You SHOULD append 1-3 concise lines to root or create a nested AGENTS.md.
|
||||
Findings MUST be captured from non-obvious fixes or decisions.
|
||||
</summary>
|
||||
|
||||
<user_guidelines>
|
||||
$ARGUMENTS
|
||||
</user_guidelines>
|
||||
|
||||
<objective>
|
||||
Capture critical "why" decisions, workarounds, or fixes from this session.
|
||||
Intelligently identify findings from context or user guidelines, if present.
|
||||
</objective>
|
||||
|
||||
<instructions>
|
||||
1. **Identify Findings**: you MUST analyze the session/task for friction points or non-obvious fixes.
|
||||
2. **Auto-Detect Scope**: you MUST determine if findings are nested-directory-specific or repo-wide.
|
||||
3. **Distill Lesson**: you MUST extract findings into 1-2 non-verbose lines. you SHOULD match existing documentation style.
|
||||
4. **Apply**: you MUST append to root or identified sub-directory AGENTS.md. If no AGENTS.md exist in the chosen directory, you MUST create a minimalist file containing ONLY the new findings.
|
||||
</instructions>
|
||||
|
||||
<rules>
|
||||
- MUST be extremely concise (1-2 lines of new content).
|
||||
- MUST focus on "how to avoid this issue next time."
|
||||
- MUST NOT use generic preambles or repeat info.
|
||||
</rules>
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "repo-navigator",
|
||||
"description": "Repository documentation specialist pack for enhanced /init.",
|
||||
"items": [
|
||||
{
|
||||
"name": "repo-navigator",
|
||||
"description": "Generate AGENTS.md documentation.",
|
||||
"type": "agent",
|
||||
"path": "agent/repo-navigator.md",
|
||||
"target": "agent/repo-navigator.md"
|
||||
},
|
||||
{
|
||||
"name": "init",
|
||||
"description": "Init command replacement. Supports vanilla `/init`, `/init user` for user-assistance agent-guidance & `/init full` with AGENTS.md + skills generation.",
|
||||
"type": "command",
|
||||
"path": "command/init.md",
|
||||
"target": "command/init.md"
|
||||
},
|
||||
{
|
||||
"name": "learn",
|
||||
"description": "A version of learn command hyperfocussed on minimal nested AGENTS.md creation.",
|
||||
"type": "command",
|
||||
"path": "command/learn.md",
|
||||
"target": "command/learn.md"
|
||||
},
|
||||
{
|
||||
"name": "agent-navigation-sop",
|
||||
"description": "AI-focused navigation SOP.",
|
||||
"type": "skill",
|
||||
"path": "skill/agent-navigation-sop",
|
||||
"target": "skill/agent-navigation-sop"
|
||||
},
|
||||
{
|
||||
"name": "user-onboarding-sop",
|
||||
"description": "User-focused onboarding SOP.",
|
||||
"type": "skill",
|
||||
"path": "skill/user-onboarding-sop",
|
||||
"target": "skill/user-onboarding-sop"
|
||||
},
|
||||
{
|
||||
"name": "skill-creator",
|
||||
"description": "Architect and refine skills.",
|
||||
"type": "skill",
|
||||
"path": "skill/skill-creator",
|
||||
"target": "skill/skill-creator"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"name": "security-reviewer",
|
||||
"description": "Vibecoding security audit and secret scanning pack.",
|
||||
"items": [
|
||||
{
|
||||
"name": "security-reviewer",
|
||||
"description": "Security auditor for vibecoding vulnerabilities.",
|
||||
"type": "agent",
|
||||
"path": ".opencode/agent/security-reviewer.md",
|
||||
"target": "agent/security-reviewer.md"
|
||||
},
|
||||
{
|
||||
"name": "security-ai-keys",
|
||||
"description": "Scan for exposed AI API keys.",
|
||||
"type": "skill",
|
||||
"path": ".opencode/skill/security-ai-keys",
|
||||
"target": "skill/security-ai-keys"
|
||||
},
|
||||
{
|
||||
"name": "security-bun",
|
||||
"description": "Bun-specific security audit skill.",
|
||||
"type": "skill",
|
||||
"path": ".opencode/skill/security-bun",
|
||||
"target": "skill/security-bun"
|
||||
},
|
||||
{
|
||||
"name": "security-convex",
|
||||
"description": "Convex-specific security audit skill.",
|
||||
"type": "skill",
|
||||
"path": ".opencode/skill/security-convex",
|
||||
"target": "skill/security-convex"
|
||||
},
|
||||
{
|
||||
"name": "security-django",
|
||||
"description": "Django-specific security audit skill.",
|
||||
"type": "skill",
|
||||
"path": ".opencode/skill/security-django",
|
||||
"target": "skill/security-django"
|
||||
},
|
||||
{
|
||||
"name": "security-docker",
|
||||
"description": "Docker-specific security audit skill.",
|
||||
"type": "skill",
|
||||
"path": ".opencode/skill/security-docker",
|
||||
"target": "skill/security-docker"
|
||||
},
|
||||
{
|
||||
"name": "security-express",
|
||||
"description": "Express-specific security audit skill.",
|
||||
"type": "skill",
|
||||
"path": ".opencode/skill/security-express",
|
||||
"target": "skill/security-express"
|
||||
},
|
||||
{
|
||||
"name": "security-fastapi",
|
||||
"description": "FastAPI-specific security audit skill.",
|
||||
"type": "skill",
|
||||
"path": ".opencode/skill/security-fastapi",
|
||||
"target": "skill/security-fastapi"
|
||||
},
|
||||
{
|
||||
"name": "security-nextjs",
|
||||
"description": "Next.js-specific security audit skill.",
|
||||
"type": "skill",
|
||||
"path": ".opencode/skill/security-nextjs",
|
||||
"target": "skill/security-nextjs"
|
||||
},
|
||||
{
|
||||
"name": "security-secrets",
|
||||
"description": "Generic secret and credential scanner.",
|
||||
"type": "skill",
|
||||
"path": ".opencode/skill/security-secrets",
|
||||
"target": "skill/security-secrets"
|
||||
},
|
||||
{
|
||||
"name": "security-vite",
|
||||
"description": "Vite-specific security audit skill.",
|
||||
"type": "skill",
|
||||
"path": ".opencode/skill/security-vite",
|
||||
"target": "skill/security-vite"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -9,6 +9,7 @@ mode: all
|
||||
permission:
|
||||
skill:
|
||||
"*": "deny"
|
||||
"vite-shadcn-tailwind4": "allow"
|
||||
---
|
||||
|
||||
<core_mission>
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
{
|
||||
"name": "vite-react-ts-convex-tailwind",
|
||||
"description": "Expert VRTCT stack toolkit pack. Designed to modernise outdated models & stacks.",
|
||||
"items": [
|
||||
{
|
||||
"name": "VRTCT-orchestrator",
|
||||
"description": "Master coordinator for VRTCT with no file-creation capabilities.",
|
||||
"type": "agent",
|
||||
"path": ".opencode/agent/VRTCT-orchestrator.md",
|
||||
"target": "agent/VRTCT-orchestrator.md"
|
||||
},
|
||||
{
|
||||
"name": "VRTCT-brain",
|
||||
"description": "Stack implementation and knowledge.",
|
||||
"type": "agent",
|
||||
"path": ".opencode/agent/VRTCT-brain.md",
|
||||
"target": "agent/VRTCT-brain.md"
|
||||
},
|
||||
{
|
||||
"name": "convex-database-expert",
|
||||
"description": "Senior Convex database engineer.",
|
||||
"type": "agent",
|
||||
"path": ".opencode/agent/convex-database-expert.md",
|
||||
"target": "agent/convex-database-expert.md"
|
||||
},
|
||||
{
|
||||
"name": "react-19-master",
|
||||
"description": "Deep React 19.2 mastery.",
|
||||
"type": "agent",
|
||||
"path": ".opencode/agent/react-19-master.md",
|
||||
"target": "agent/react-19-master.md"
|
||||
},
|
||||
{
|
||||
"name": "tailwind-41-architect",
|
||||
"description": "Tailwind 4.1 maestro.",
|
||||
"type": "agent",
|
||||
"path": ".opencode/agent/tailwind-41-architect.md",
|
||||
"target": "agent/tailwind-41-architect.md"
|
||||
},
|
||||
{
|
||||
"name": "typescript-59-engineer",
|
||||
"description": "Strict TypeScript 5.9 expert.",
|
||||
"type": "agent",
|
||||
"path": ".opencode/agent/typescript-59-engineer.md",
|
||||
"target": "agent/typescript-59-engineer.md"
|
||||
},
|
||||
{
|
||||
"name": "component-create",
|
||||
"description": "Generate spec-compliant components.",
|
||||
"type": "command",
|
||||
"path": ".opencode/command/component-create.md",
|
||||
"target": "command/component-create.md"
|
||||
},
|
||||
{
|
||||
"name": "component-review",
|
||||
"description": "Audit components against spec.",
|
||||
"type": "command",
|
||||
"path": ".opencode/command/component-review.md",
|
||||
"target": "command/component-review.md"
|
||||
},
|
||||
{
|
||||
"name": "CODING-TS",
|
||||
"description": "Universal engineering guidelines.",
|
||||
"type": "doc",
|
||||
"path": "CODING-TS.md",
|
||||
"target": "CODING-TS.md"
|
||||
},
|
||||
{
|
||||
"name": "CONVEX",
|
||||
"description": "Convex best practices guide.",
|
||||
"type": "doc",
|
||||
"path": "CONVEX.md",
|
||||
"target": "CONVEX.md"
|
||||
},
|
||||
{
|
||||
"name": "REACT19",
|
||||
"description": "React 19 reference patterns.",
|
||||
"type": "doc",
|
||||
"path": "REACT19.md",
|
||||
"target": "REACT19.md"
|
||||
},
|
||||
{
|
||||
"name": "TAILWIND4",
|
||||
"description": "Tailwind 4.1 migration guide.",
|
||||
"type": "doc",
|
||||
"path": "TAILWIND4.md",
|
||||
"target": "TAILWIND4.md"
|
||||
},
|
||||
{
|
||||
"name": "TS59",
|
||||
"description": "TypeScript 5.9 language reference.",
|
||||
"type": "doc",
|
||||
"path": "TS59.md",
|
||||
"target": "TS59.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"name": "additional-commands",
|
||||
"description": "Extended utility commands for various workflows.",
|
||||
"items": [
|
||||
{
|
||||
"name": "learn",
|
||||
"description": "A version of learn command hyperfocussed on minimal nested AGENTS.md creation.",
|
||||
"type": "command",
|
||||
"path": ".opencode/command/learn.md",
|
||||
"target": "command/learn.md"
|
||||
},
|
||||
{
|
||||
"name": "rmslop",
|
||||
"description": "Remove AI-generated boilerplate/slop. Adjusted from OC repo command.",
|
||||
"type": "command",
|
||||
"path": ".opencode/command/rmslop.md",
|
||||
"target": "command/rmslop.md"
|
||||
},
|
||||
{
|
||||
"name": "loop",
|
||||
"description": "Run autonomous development loops. Kinda Ralph, but via subagents.",
|
||||
"type": "command",
|
||||
"path": ".opencode/command/loop.md",
|
||||
"target": "command/loop.md"
|
||||
},
|
||||
{
|
||||
"name": "howto",
|
||||
"description": "Generate AGENTS.md tailored towards teaching the user how to use a repo.",
|
||||
"type": "command",
|
||||
"path": ".opencode/command/howto.md",
|
||||
"target": "command/howto.md"
|
||||
},
|
||||
{
|
||||
"name": "create-pack",
|
||||
"description": "Scaffold a new agent pack. Similar to you're looking at here.",
|
||||
"type": "command",
|
||||
"path": ".opencode/command/create-pack.md",
|
||||
"target": "command/create-pack.md"
|
||||
},
|
||||
{
|
||||
"name": "refactor",
|
||||
"description": "Refactor code according to best practices.",
|
||||
"type": "command",
|
||||
"path": ".opencode/command/refactor.md",
|
||||
"target": "command/refactor.md"
|
||||
},
|
||||
{
|
||||
"name": "npm",
|
||||
"description": "Helper for npm package management.",
|
||||
"type": "command",
|
||||
"path": ".opencode/command/npm.md",
|
||||
"target": "command/npm.md"
|
||||
},
|
||||
{
|
||||
"name": "improve-run",
|
||||
"description": "Run and improve prompts.",
|
||||
"type": "command",
|
||||
"path": ".opencode/command/improve-run.md",
|
||||
"target": "command/improve-run.md"
|
||||
},
|
||||
{
|
||||
"name": "improve-save",
|
||||
"description": "Improve and save a prompt.",
|
||||
"type": "command",
|
||||
"path": ".opencode/command/improve-save.md",
|
||||
"target": "command/improve-save.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -3,6 +3,9 @@
|
||||
> [!NOTE]
|
||||
> **Partially Deprecated**: OpenCode now supports [Skills](https://opencode.ai/docs/skills/) natively, which provide a more powerful way to extend agent capabilities. This template remains useful if you want agents to discover and execute your `/commands` agentically via a COMMANDS.md index file. For new projects, consider using Skills instead.
|
||||
|
||||
> [!NOTE]
|
||||
> **Highly recommended**: [opencode-snippets by JosXa](https://github.com/JosXa/opencode-snippets) - this plugin allows you to put in composable commands directly into your prompts. This effectively replaces custom commands so you could just replace `commands` with `snippets` and get `snippets2skills`!
|
||||
|
||||
A universal template for creating Opencode-based projects with structured commands, tool integration, and automated documentation maintenance.
|
||||
|
||||
## Architecture Overview
|
||||
@@ -127,10 +130,6 @@ project/
|
||||
|
||||
Commands.md is injected at the beginning of the session, so models need strong context management and instruction following capabilities.
|
||||
|
||||
### Future Enhancement
|
||||
|
||||
Currently Opencode only supports context injection at session start. A potential future enhancement would be if Opencode adds the ability to inject context into every user prompt. This would allow COMMANDS.md to be included in every prompt, not just at the start of the session, significantly improving command reliability and reducing context management requirements for models.
|
||||
|
||||
*Please let me know if you test this template on different models.*
|
||||
|
||||
### Tested Models
|
||||
@@ -144,7 +143,7 @@ Currently Opencode only supports context injection at session start. A potential
|
||||
### Model Requirements
|
||||
|
||||
For optimal performance, models should excel at:
|
||||
- **Context Management**: Maintaining COMMANDS.md throughout the session
|
||||
- **Context Attention**: "Seeing" COMMANDS.md throughout the session
|
||||
- **Instruction Following**: Precise execution of multi-step workflows
|
||||
- **Agentic Capabilities**: Understanding when and how to use available commands
|
||||
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
{
|
||||
"name": "cowork",
|
||||
"description": "Multi-agent orchestration system for complex collaborative tasks.",
|
||||
"items": [
|
||||
{
|
||||
"name": "cowork-orchestrator",
|
||||
"description": "Master coordinator for cowork sessions.",
|
||||
"type": "agent",
|
||||
"path": ".opencode/agent/cowork-orchestrator.md",
|
||||
"target": "agent/cowork-orchestrator.md"
|
||||
},
|
||||
{
|
||||
"name": "admin-assistant",
|
||||
"description": "Task management and administrative support.",
|
||||
"type": "agent",
|
||||
"path": ".opencode/agent/admin-assistant.md",
|
||||
"target": "agent/admin-assistant.md"
|
||||
},
|
||||
{
|
||||
"name": "research-specialist",
|
||||
"description": "Deep research and information synthesis.",
|
||||
"type": "agent",
|
||||
"path": ".opencode/agent/research-specialist.md",
|
||||
"target": "agent/research-specialist.md"
|
||||
},
|
||||
{
|
||||
"name": "data-analyst",
|
||||
"description": "Data processing and pattern analysis.",
|
||||
"type": "agent",
|
||||
"path": ".opencode/agent/data-analyst.md",
|
||||
"target": "agent/data-analyst.md"
|
||||
},
|
||||
{
|
||||
"name": "document-specialist",
|
||||
"description": "Professional documentation and report authoring.",
|
||||
"type": "agent",
|
||||
"path": ".opencode/agent/document-specialist.md",
|
||||
"target": "agent/document-specialist.md"
|
||||
},
|
||||
{
|
||||
"name": "presentation-expert",
|
||||
"description": "Visual communication and slide design.",
|
||||
"type": "agent",
|
||||
"path": ".opencode/agent/presentation-expert.md",
|
||||
"target": "agent/presentation-expert.md"
|
||||
},
|
||||
{
|
||||
"name": "cowork-configurator",
|
||||
"description": "Configuration expert for cowork environment.",
|
||||
"type": "agent",
|
||||
"path": ".opencode/agent/cowork-configurator.md",
|
||||
"target": "agent/cowork-configurator.md"
|
||||
},
|
||||
{
|
||||
"name": "cowork-cmd",
|
||||
"description": "Launch a collaborative cowork session.",
|
||||
"type": "command",
|
||||
"path": ".opencode/command/cowork.md",
|
||||
"target": "command/cowork.md"
|
||||
},
|
||||
{
|
||||
"name": "cowork-skill",
|
||||
"description": "Core cowork orchestration logic.",
|
||||
"type": "skill",
|
||||
"path": ".opencode/skill/cowork",
|
||||
"target": "skill/cowork"
|
||||
},
|
||||
{
|
||||
"name": "branding",
|
||||
"description": "Visual identity and branding guidelines.",
|
||||
"type": "skill",
|
||||
"path": ".opencode/skill/branding",
|
||||
"target": "skill/branding"
|
||||
},
|
||||
{
|
||||
"name": "comms",
|
||||
"description": "Communication templates and strategies.",
|
||||
"type": "skill",
|
||||
"path": ".opencode/skill/comms",
|
||||
"target": "skill/comms"
|
||||
},
|
||||
{
|
||||
"name": "excel",
|
||||
"description": "Advanced spreadsheet automation and analysis.",
|
||||
"type": "skill",
|
||||
"path": ".opencode/skill/excel",
|
||||
"target": "skill/excel"
|
||||
},
|
||||
{
|
||||
"name": "pdf",
|
||||
"description": "PDF manipulation and form processing.",
|
||||
"type": "skill",
|
||||
"path": ".opencode/skill/pdf",
|
||||
"target": "skill/pdf"
|
||||
},
|
||||
{
|
||||
"name": "powerpoint",
|
||||
"description": "Presentation generation and OOXML tools.",
|
||||
"type": "skill",
|
||||
"path": ".opencode/skill/powerpoint",
|
||||
"target": "skill/powerpoint"
|
||||
},
|
||||
{
|
||||
"name": "themes",
|
||||
"description": "Document themes and visual styles.",
|
||||
"type": "skill",
|
||||
"path": ".opencode/skill/themes",
|
||||
"target": "skill/themes"
|
||||
},
|
||||
{
|
||||
"name": "word",
|
||||
"description": "Word document automation and OOXML tools.",
|
||||
"type": "skill",
|
||||
"path": ".opencode/skill/word",
|
||||
"target": "skill/word"
|
||||
},
|
||||
{
|
||||
"name": "writing",
|
||||
"description": "Professional writing and editing workflow.",
|
||||
"type": "skill",
|
||||
"path": ".opencode/skill/writing",
|
||||
"target": "skill/writing"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<instructions>
|
||||
## Verification & Dev Flow
|
||||
This project is a **Workflow Selector CLI** built with Bun, Solid.js, and OpenTUI.
|
||||
- **Type Check**: `bun x tsc --noEmit` (MANDATORY verification command).
|
||||
- **Execution**: `bun run src/index.tsx` (PROHIBITED; direct execution is for users only).
|
||||
- **Dev Mode**: `bun run --watch src/index.tsx` (PROHIBITED; long-running process).
|
||||
- **Constraints**: MUST NOT run `bun run src/index.tsx` or `bun run dev`. MUST use one-shot verification commands like `tsc`.
|
||||
</instructions>
|
||||
|
||||
<rules>
|
||||
## Technical Conventions
|
||||
- **Solid.js + OpenTUI**: Uses `@opentui/solid` for TUI rendering. Follow Solid.js reactive patterns (`createSignal`, `createMemo`, `Show`, `For`).
|
||||
- **Keyboard Handling**: Use the `useKeyboard` hook from `@opentui/solid` for input management.
|
||||
- **Registry Management**: Interacts with `registry.json` at the repository root. Uses `fs-extra` for file operations.
|
||||
- **RFC 2119**: MUST use uppercase keywords (MUST, SHOULD, MAY) for requirements.
|
||||
</rules>
|
||||
|
||||
<routing>
|
||||
## Task Navigation
|
||||
| Feature | Path |
|
||||
|---------|------|
|
||||
| TUI Components & UI Logic | `src/index.tsx` |
|
||||
| Sync & File Ops Logic | `src/index.tsx` (see `performSync`) |
|
||||
| Registry Definition | `../registry.json` |
|
||||
| TS Configuration | `tsconfig.json` |
|
||||
</routing>
|
||||
|
||||
<context>
|
||||
## Architecture Details
|
||||
- **Path Resolution**: The app resolves `ROOT_DIR` relative to `src/index.tsx` (pointing two levels up).
|
||||
- **Registry Structure**: Supports `packs` (collections) and `standalone` items (agents, skills, commands, docs).
|
||||
- **Target Paths**: Specialized items are automatically prefixed with `.opencode/` unless already present.
|
||||
</context>
|
||||
@@ -0,0 +1,15 @@
|
||||
# solid
|
||||
|
||||
To install dependencies:
|
||||
|
||||
```bash
|
||||
bun install
|
||||
```
|
||||
|
||||
To run:
|
||||
|
||||
```bash
|
||||
bun dev
|
||||
```
|
||||
|
||||
This project was created using `bun create tui`. [create-tui](https://git.new/create-tui) is the easiest way to get started with OpenTUI.
|
||||
@@ -0,0 +1 @@
|
||||
preload = ["@opentui/solid/preload"]
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* ConfirmView.tsx
|
||||
* Displays pending changes summary and prompts user for confirmation.
|
||||
* Shows items to be installed and removed before sync.
|
||||
*/
|
||||
|
||||
import { TextAttributes } from "@opentui/core";
|
||||
import { For, Show } from "solid-js";
|
||||
import type { Accessor } from "solid-js";
|
||||
import type { Changes } from "../types";
|
||||
|
||||
interface ConfirmViewProps {
|
||||
changes: Accessor<Changes>;
|
||||
}
|
||||
|
||||
export function ConfirmView(props: ConfirmViewProps) {
|
||||
return (
|
||||
<box flexDirection="column">
|
||||
<text attributes={TextAttributes.BOLD}>Changes Summary:</text>
|
||||
|
||||
<Show when={props.changes().install.length > 0}>
|
||||
<text> Install ({props.changes().install.length}):</text>
|
||||
<For each={props.changes().install}>
|
||||
{(item) => <text> + {item.name}</text>}
|
||||
</For>
|
||||
</Show>
|
||||
|
||||
<Show when={props.changes().remove.length > 0}>
|
||||
<text> Remove ({props.changes().remove.length}):</text>
|
||||
<For each={props.changes().remove}>
|
||||
{(item) => <text> - {item.name}</text>}
|
||||
</For>
|
||||
</Show>
|
||||
|
||||
<Show
|
||||
when={
|
||||
props.changes().install.length === 0 &&
|
||||
props.changes().remove.length === 0
|
||||
}
|
||||
>
|
||||
<text>No changes to apply.</text>
|
||||
</Show>
|
||||
|
||||
<box marginTop={1}>
|
||||
<text attributes={TextAttributes.BOLD}>
|
||||
Proceed with changes? (Enter to confirm, Esc to abort)
|
||||
</text>
|
||||
</box>
|
||||
</box>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* SelectionView.tsx
|
||||
* Renders the interactive item selection interface with hierarchical navigation.
|
||||
* Displays packs, standalone items, and selection state indicators.
|
||||
*/
|
||||
|
||||
import { TextAttributes } from "@opentui/core";
|
||||
import { For, createMemo } from "solid-js";
|
||||
import type { Accessor } from "solid-js";
|
||||
import type { UIItem, RegistryItem, Pack, InstallMode } from "../types";
|
||||
|
||||
interface SelectionViewProps {
|
||||
items: Accessor<UIItem[]>;
|
||||
cursor: Accessor<number>;
|
||||
selectedItems: Accessor<Set<RegistryItem>>;
|
||||
isPackSelected: (pack: Pack) => boolean;
|
||||
installMode: Accessor<InstallMode>;
|
||||
}
|
||||
|
||||
export function SelectionView(props: SelectionViewProps) {
|
||||
const currentDescription = createMemo(
|
||||
() => props.items()[props.cursor()]?.description || ""
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<box flexDirection="column" marginBottom={1}>
|
||||
<box>
|
||||
<text attributes={TextAttributes.BOLD}>{props.selectedItems().size}</text>
|
||||
<text attributes={TextAttributes.DIM}> selected | </text>
|
||||
<text attributes={TextAttributes.BOLD}>
|
||||
{props.installMode() === "global" ? "Global" : "Local"}
|
||||
</text>
|
||||
<text attributes={TextAttributes.DIM}>
|
||||
{props.installMode() === "global"
|
||||
? " (~/.config/opencode/)"
|
||||
: " (.opencode/)"}
|
||||
</text>
|
||||
</box>
|
||||
<box height={1}>
|
||||
<text attributes={TextAttributes.DIM}>{currentDescription()}</text>
|
||||
</box>
|
||||
<text attributes={TextAttributes.DIM}>
|
||||
j/k: Nav | h/l: Drill | Space: Toggle | Tab: Mode | Enter: Sync
|
||||
</text>
|
||||
</box>
|
||||
|
||||
<box flexDirection="column" flexGrow={1}>
|
||||
<For each={props.items()}>
|
||||
{(item, index) => {
|
||||
const isCursor = createMemo(() => index() === props.cursor());
|
||||
|
||||
const isSelected = createMemo(() =>
|
||||
item.type === "item"
|
||||
? props.selectedItems().has(item.item!)
|
||||
: item.type === "pack"
|
||||
? props.isPackSelected(item.pack!)
|
||||
: false
|
||||
);
|
||||
|
||||
const indent =
|
||||
item.type === "category"
|
||||
? ""
|
||||
: item.type === "pack"
|
||||
? " "
|
||||
: item.parent === "agents" || item.parent === "commands"
|
||||
? " "
|
||||
: " ";
|
||||
|
||||
const chevron =
|
||||
item.type === "category" || item.type === "pack"
|
||||
? item.expanded
|
||||
? "v "
|
||||
: "> "
|
||||
: "";
|
||||
|
||||
const checkbox =
|
||||
item.type === "item" || item.type === "pack"
|
||||
? isSelected()
|
||||
? "[x] "
|
||||
: "[ ] "
|
||||
: "";
|
||||
|
||||
const itemCount =
|
||||
item.type === "pack" ? ` (${item.pack!.items.length})` : "";
|
||||
|
||||
return (
|
||||
<box
|
||||
backgroundColor={isCursor() ? "cyan" : undefined}
|
||||
height={1}
|
||||
>
|
||||
<text
|
||||
attributes={
|
||||
isCursor()
|
||||
? TextAttributes.BOLD
|
||||
: item.type === "category"
|
||||
? TextAttributes.BOLD
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{indent}
|
||||
{chevron}
|
||||
{checkbox}
|
||||
{item.title}
|
||||
{itemCount}
|
||||
</text>
|
||||
</box>
|
||||
);
|
||||
}}
|
||||
</For>
|
||||
</box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* SyncView.tsx
|
||||
* Shows sync progress and completion status with operation logs.
|
||||
* Displays each installed/removed item as the sync proceeds.
|
||||
*/
|
||||
|
||||
import { TextAttributes } from "@opentui/core";
|
||||
import { For, Show } from "solid-js";
|
||||
import type { Accessor } from "solid-js";
|
||||
import type { AppStatus } from "../types";
|
||||
|
||||
interface SyncViewProps {
|
||||
status: Accessor<AppStatus>;
|
||||
logs: Accessor<string[]>;
|
||||
}
|
||||
|
||||
export function SyncView(props: SyncViewProps) {
|
||||
return (
|
||||
<box flexDirection="column">
|
||||
<text attributes={TextAttributes.BOLD}>
|
||||
{props.status() === "syncing" ? "Syncing changes..." : "Sync complete!"}
|
||||
</text>
|
||||
|
||||
<box flexDirection="column" marginTop={1}>
|
||||
<For each={props.logs()}>
|
||||
{(log) => (
|
||||
<box>
|
||||
<text>[OK] </text>
|
||||
<text>{log}</text>
|
||||
</box>
|
||||
)}
|
||||
</For>
|
||||
</box>
|
||||
|
||||
<Show when={props.status() === "done"}>
|
||||
<text marginTop={1}>Press Enter to exit.</text>
|
||||
</Show>
|
||||
</box>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* components/index.ts
|
||||
* Barrel export for all UI components used by the workflow selector.
|
||||
*/
|
||||
|
||||
export { SelectionView } from "./SelectionView";
|
||||
export { ConfirmView } from "./ConfirmView";
|
||||
export { SyncView } from "./SyncView";
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* config.ts
|
||||
* Centralized path configuration for the workflow selector CLI.
|
||||
* Resolves paths relative to the repository root.
|
||||
*/
|
||||
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { homedir } from "os";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
export const ROOT_DIR = path.join(__dirname, "../../");
|
||||
export const REGISTRY_PATH = path.join(ROOT_DIR, "registry.json");
|
||||
|
||||
export const OPENCODE_PREFIX = ".opencode";
|
||||
|
||||
// TODO: Tighten up - make this configurable and explicit
|
||||
// This comment MUST stay until install paths are properly abstracted
|
||||
export const GLOBAL_INSTALL_DIR = path.join(homedir(), ".config", "opencode");
|
||||
export const LOCAL_INSTALL_DIR = path.join(ROOT_DIR, OPENCODE_PREFIX);
|
||||
@@ -0,0 +1,278 @@
|
||||
/**
|
||||
* index.tsx
|
||||
* Entry point for the Opencode Workflow Selector TUI application.
|
||||
* Orchestrates keyboard navigation, selection state, and sync operations.
|
||||
*/
|
||||
|
||||
import { render, useKeyboard } from "@opentui/solid";
|
||||
import { TextAttributes } from "@opentui/core";
|
||||
import { createSignal, createMemo, onMount, Show } from "solid-js";
|
||||
|
||||
import type { Registry, RegistryItem, Pack, UIItem, AppStatus, Changes, InstallMode } from "./types";
|
||||
import { loadRegistry, getAllItems, findInstalledItems } from "./registry";
|
||||
import { performSync } from "./sync";
|
||||
import { SelectionView, ConfirmView, SyncView } from "./components";
|
||||
|
||||
function buildVisibleItems(
|
||||
registry: Registry,
|
||||
expandedCategory: string | null,
|
||||
expandedPack: string | null
|
||||
): UIItem[] {
|
||||
const items: UIItem[] = [];
|
||||
|
||||
const packsExpanded = expandedCategory === "packs";
|
||||
items.push({
|
||||
type: "category",
|
||||
id: "packs",
|
||||
title: "Packs",
|
||||
expanded: packsExpanded,
|
||||
description: "Collection of specialized agent toolkits",
|
||||
});
|
||||
|
||||
if (packsExpanded) {
|
||||
for (const pack of registry.packs) {
|
||||
const packExpanded = expandedPack === pack.name;
|
||||
items.push({
|
||||
type: "pack",
|
||||
id: `pack:${pack.name}`,
|
||||
title: pack.name,
|
||||
expanded: packExpanded,
|
||||
parent: "packs",
|
||||
pack,
|
||||
description: pack.description,
|
||||
});
|
||||
if (packExpanded) {
|
||||
for (const item of pack.items) {
|
||||
items.push({
|
||||
type: "item",
|
||||
id: `item:${pack.name}:${item.name}`,
|
||||
title: item.name,
|
||||
item,
|
||||
parent: `pack:${pack.name}`,
|
||||
description: item.description,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const standaloneCategories = [
|
||||
{ id: "agents", title: "Agents", filter: "agent", desc: "Global orchestration and speed agents" },
|
||||
{ id: "commands", title: "Commands", filter: "command", desc: "Repository maintenance utility commands" },
|
||||
] as const;
|
||||
|
||||
for (const cat of standaloneCategories) {
|
||||
const isExpanded = expandedCategory === cat.id;
|
||||
items.push({
|
||||
type: "category",
|
||||
id: cat.id,
|
||||
title: cat.title,
|
||||
expanded: isExpanded,
|
||||
description: cat.desc,
|
||||
});
|
||||
if (isExpanded) {
|
||||
const filtered = registry.standalone.filter((s) => s.type === cat.filter);
|
||||
for (const item of filtered) {
|
||||
items.push({
|
||||
type: "item",
|
||||
id: `${cat.id}:${item.name}`,
|
||||
title: item.name,
|
||||
item,
|
||||
parent: cat.id,
|
||||
description: item.description,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
const App = () => {
|
||||
const [registry, setRegistry] = createSignal<Registry | null>(null);
|
||||
const [cursor, setCursor] = createSignal(0);
|
||||
const [expandedCategory, setExpandedCategory] = createSignal<string | null>("packs");
|
||||
const [expandedPack, setExpandedPack] = createSignal<string | null>(null);
|
||||
const [selectedItems, setSelectedItems] = createSignal<Set<RegistryItem>>(new Set());
|
||||
const [initialSelection, setInitialSelection] = createSignal<Set<RegistryItem>>(new Set());
|
||||
const [status, setStatus] = createSignal<AppStatus>("selecting");
|
||||
const [syncLogs, setSyncLogs] = createSignal<string[]>([]);
|
||||
// TODO: Tighten up install mode - add proper mode persistence and path validation
|
||||
// This comment MUST stay until install paths are properly abstracted
|
||||
const [installMode, setInstallMode] = createSignal<InstallMode>("global");
|
||||
|
||||
onMount(async () => {
|
||||
const reg = await loadRegistry();
|
||||
if (!reg) return;
|
||||
setRegistry(reg);
|
||||
|
||||
const allItems = getAllItems(reg);
|
||||
const installed = await findInstalledItems(allItems, installMode());
|
||||
setInitialSelection(installed);
|
||||
setSelectedItems(new Set(installed));
|
||||
});
|
||||
|
||||
const visibleItems = createMemo(() => {
|
||||
const reg = registry();
|
||||
if (!reg) return [];
|
||||
return buildVisibleItems(reg, expandedCategory(), expandedPack());
|
||||
});
|
||||
|
||||
const isPackSelected = (pack: Pack) => pack.items.every((i) => selectedItems().has(i));
|
||||
|
||||
const togglePack = (pack: Pack) => {
|
||||
const next = new Set(selectedItems());
|
||||
if (isPackSelected(pack)) {
|
||||
pack.items.forEach((i) => next.delete(i));
|
||||
} else {
|
||||
pack.items.forEach((i) => next.add(i));
|
||||
}
|
||||
setSelectedItems(next);
|
||||
};
|
||||
|
||||
const toggleItem = (item: RegistryItem) => {
|
||||
const next = new Set(selectedItems());
|
||||
if (next.has(item)) {
|
||||
next.delete(item);
|
||||
} else {
|
||||
next.add(item);
|
||||
}
|
||||
setSelectedItems(next);
|
||||
};
|
||||
|
||||
const changes = createMemo<Changes>(() => ({
|
||||
install: Array.from(selectedItems()).filter((i) => !initialSelection().has(i)),
|
||||
remove: Array.from(initialSelection()).filter((i) => !selectedItems().has(i)),
|
||||
}));
|
||||
|
||||
const executeSync = async () => {
|
||||
await performSync(changes(), installMode(), (msg) => {
|
||||
setSyncLogs((prev) => [...prev, msg]);
|
||||
});
|
||||
setStatus("done");
|
||||
};
|
||||
|
||||
useKeyboard((key: { name: string; ctrl?: boolean }) => {
|
||||
const input = key.name;
|
||||
|
||||
if (status() !== "selecting") {
|
||||
if (input === "return") {
|
||||
if (status() === "confirming") {
|
||||
setStatus("syncing");
|
||||
executeSync();
|
||||
} else if (status() === "done") {
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
if (input === "escape" || (key.ctrl && input === "c")) {
|
||||
process.exit(0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const items = visibleItems();
|
||||
const currentItem = items[cursor()];
|
||||
|
||||
if (input === "up" || input === "k") {
|
||||
setCursor((prev) => Math.max(0, prev - 1));
|
||||
}
|
||||
|
||||
if (input === "down" || input === "j") {
|
||||
setCursor((prev) => Math.min(items.length - 1, prev + 1));
|
||||
}
|
||||
|
||||
if ((input === "right" || input === "l") && currentItem) {
|
||||
const targetId = currentItem.id;
|
||||
if (currentItem.type === "category") {
|
||||
setExpandedCategory(currentItem.id);
|
||||
setExpandedPack(null);
|
||||
} else if (currentItem.type === "pack" && currentItem.pack) {
|
||||
setExpandedPack(currentItem.pack.name);
|
||||
}
|
||||
const newItems = visibleItems();
|
||||
const newIdx = newItems.findIndex((i) => i.id === targetId);
|
||||
if (newIdx !== -1) setCursor(newIdx);
|
||||
}
|
||||
|
||||
if ((input === "left" || input === "h") && currentItem) {
|
||||
const targetId = currentItem.id;
|
||||
if (currentItem.type === "category") {
|
||||
setExpandedCategory(null);
|
||||
} else if (currentItem.type === "pack") {
|
||||
if (currentItem.pack && expandedPack() === currentItem.pack.name) {
|
||||
setExpandedPack(null);
|
||||
} else {
|
||||
const newItems = visibleItems();
|
||||
const idx = newItems.findIndex((i) => i.id === "packs");
|
||||
if (idx !== -1) setCursor(idx);
|
||||
return;
|
||||
}
|
||||
} else if (currentItem.type === "item" && currentItem.parent) {
|
||||
const newItems = visibleItems();
|
||||
const idx = newItems.findIndex((i) => i.id === currentItem.parent);
|
||||
if (idx !== -1) setCursor(idx);
|
||||
return;
|
||||
}
|
||||
const newItems = visibleItems();
|
||||
const newIdx = newItems.findIndex((i) => i.id === targetId);
|
||||
if (newIdx !== -1) setCursor(newIdx);
|
||||
}
|
||||
|
||||
if (input === "space" && currentItem) {
|
||||
if (currentItem.type === "item" && currentItem.item) {
|
||||
toggleItem(currentItem.item);
|
||||
} else if (currentItem.type === "pack" && currentItem.pack) {
|
||||
togglePack(currentItem.pack);
|
||||
}
|
||||
}
|
||||
|
||||
if (input === "return") {
|
||||
setStatus("confirming");
|
||||
}
|
||||
|
||||
if (input === "tab") {
|
||||
const newMode = installMode() === "global" ? "local" : "global";
|
||||
setInstallMode(newMode);
|
||||
const reg = registry();
|
||||
if (reg) {
|
||||
const allItems = getAllItems(reg);
|
||||
findInstalledItems(allItems, newMode).then((installed) => {
|
||||
setInitialSelection(installed);
|
||||
setSelectedItems(new Set(installed));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (key.ctrl && input === "c") {
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<box flexDirection="column" padding={1} flexGrow={1}>
|
||||
<box marginBottom={1}>
|
||||
<text attributes={TextAttributes.BOLD}>Howaboua's Opencode Workflows</text>
|
||||
</box>
|
||||
|
||||
<Show when={status() === "selecting"}>
|
||||
<SelectionView
|
||||
items={visibleItems}
|
||||
cursor={cursor}
|
||||
selectedItems={selectedItems}
|
||||
isPackSelected={isPackSelected}
|
||||
installMode={installMode}
|
||||
/>
|
||||
</Show>
|
||||
|
||||
<Show when={status() === "confirming"}>
|
||||
<ConfirmView changes={changes} />
|
||||
</Show>
|
||||
|
||||
<Show when={status() === "syncing" || status() === "done"}>
|
||||
<SyncView status={status} logs={syncLogs} />
|
||||
</Show>
|
||||
</box>
|
||||
);
|
||||
};
|
||||
|
||||
render(App);
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* registry.ts
|
||||
* Handles registry loading and target path resolution.
|
||||
* Prefixes specialized items with .opencode/ when needed.
|
||||
*/
|
||||
|
||||
import fs from "fs-extra";
|
||||
import path from "path";
|
||||
import type { Registry, RegistryItem, Pack, InstallMode } from "./types";
|
||||
import { ROOT_DIR, REGISTRY_PATH, GLOBAL_INSTALL_DIR, LOCAL_INSTALL_DIR } from "./config";
|
||||
|
||||
const PREFIXED_TYPES = new Set(["agent", "skill", "command"]);
|
||||
|
||||
export function resolveTargetPath(item: RegistryItem, mode: InstallMode): string {
|
||||
const baseDir = mode === "global" ? GLOBAL_INSTALL_DIR : LOCAL_INSTALL_DIR;
|
||||
|
||||
if (PREFIXED_TYPES.has(item.type)) {
|
||||
return path.join(baseDir, item.target);
|
||||
}
|
||||
return path.join(ROOT_DIR, item.target);
|
||||
}
|
||||
|
||||
export async function loadRegistry(): Promise<Registry | null> {
|
||||
const exists = await fs.pathExists(REGISTRY_PATH);
|
||||
if (!exists) {
|
||||
throw new Error(`Registry file not found at ${REGISTRY_PATH}`);
|
||||
}
|
||||
return fs.readJson(REGISTRY_PATH) as Promise<Registry>;
|
||||
}
|
||||
|
||||
export function getAllItems(registry: Registry): RegistryItem[] {
|
||||
return [
|
||||
...registry.packs.flatMap((p: Pack) => p.items),
|
||||
...registry.standalone,
|
||||
];
|
||||
}
|
||||
|
||||
export async function findInstalledItems(
|
||||
items: RegistryItem[],
|
||||
mode: InstallMode
|
||||
): Promise<Set<RegistryItem>> {
|
||||
const installed = new Set<RegistryItem>();
|
||||
for (const item of items) {
|
||||
const exists = await fs.pathExists(resolveTargetPath(item, mode));
|
||||
if (exists) {
|
||||
installed.add(item);
|
||||
}
|
||||
}
|
||||
return installed;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* sync.ts
|
||||
* Executes file sync operations: installing and removing workflow items.
|
||||
* Uses fs-extra for atomic copy and removal.
|
||||
*/
|
||||
|
||||
import fs from "fs-extra";
|
||||
import path from "path";
|
||||
import type { RegistryItem, Changes, InstallMode } from "./types";
|
||||
import { ROOT_DIR } from "./config";
|
||||
import { resolveTargetPath } from "./registry";
|
||||
|
||||
export type SyncLogCallback = (message: string) => void;
|
||||
|
||||
export async function performSync(
|
||||
changes: Changes,
|
||||
mode: InstallMode,
|
||||
onLog: SyncLogCallback
|
||||
): Promise<void> {
|
||||
for (const item of changes.remove) {
|
||||
const dest = resolveTargetPath(item, mode);
|
||||
await fs.remove(dest);
|
||||
onLog(`Removed ${item.name}`);
|
||||
}
|
||||
|
||||
for (const item of changes.install) {
|
||||
const source = path.join(ROOT_DIR, item.path);
|
||||
const dest = resolveTargetPath(item, mode);
|
||||
await fs.ensureDir(path.dirname(dest));
|
||||
await fs.copy(source, dest);
|
||||
onLog(`Installed ${item.name}`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* types.ts
|
||||
* Defines registry data structures and UI item types for the workflow selector.
|
||||
* All types use discriminated unions for type-safe pattern matching.
|
||||
*/
|
||||
|
||||
export type ItemType = "agent" | "skill" | "command" | "doc";
|
||||
|
||||
export interface RegistryItem {
|
||||
name: string;
|
||||
description: string;
|
||||
type: ItemType;
|
||||
path: string;
|
||||
target: string;
|
||||
}
|
||||
|
||||
export interface Pack {
|
||||
name: string;
|
||||
description: string;
|
||||
path: string;
|
||||
items: RegistryItem[];
|
||||
}
|
||||
|
||||
export interface Registry {
|
||||
name: string;
|
||||
version: string;
|
||||
packs: Pack[];
|
||||
standalone: RegistryItem[];
|
||||
}
|
||||
|
||||
export type UIItemType = "category" | "pack" | "item";
|
||||
|
||||
export interface UIItem {
|
||||
type: UIItemType;
|
||||
id: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
expanded?: boolean;
|
||||
item?: RegistryItem;
|
||||
pack?: Pack;
|
||||
parent?: string;
|
||||
}
|
||||
|
||||
export type AppStatus = "selecting" | "confirming" | "syncing" | "done";
|
||||
|
||||
// TODO: Tighten up install mode handling - make paths explicit and configurable
|
||||
// This comment MUST stay until the install paths are properly abstracted
|
||||
export type InstallMode = "global" | "local";
|
||||
|
||||
export interface Changes {
|
||||
install: RegistryItem[];
|
||||
remove: RegistryItem[];
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
// Environment setup & latest features
|
||||
"lib": ["ESNext"],
|
||||
"target": "ESNext",
|
||||
"module": "Preserve",
|
||||
"moduleDetection": "force",
|
||||
"jsx": "preserve",
|
||||
"jsxImportSource": "@opentui/solid",
|
||||
"allowJs": true,
|
||||
|
||||
// Bundler mode
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
|
||||
// Best practices
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
|
||||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noPropertyAccessFromIndexSignature": false
|
||||
}
|
||||
}
|
||||
+638
@@ -0,0 +1,638 @@
|
||||
{
|
||||
"name": "opencode-workflows",
|
||||
"version": "1.0.0",
|
||||
"packs": [
|
||||
{
|
||||
"name": "vite-react-ts-convex-tailwind",
|
||||
"description": "Expert VRTCT stack toolkit pack. Designed to modernise outdated models & stacks.",
|
||||
"path": "agents/vite-react-ts-convex-tailwind",
|
||||
"items": [
|
||||
{
|
||||
"name": "VRTCT-orchestrator",
|
||||
"description": "Master coordinator for VRTCT with no file-creation capabilities.",
|
||||
"type": "agent",
|
||||
"path": "agents/vite-react-ts-convex-tailwind/.opencode/agent/VRTCT-orchestrator.md",
|
||||
"target": "agent/VRTCT-orchestrator.md"
|
||||
},
|
||||
{
|
||||
"name": "VRTCT-brain",
|
||||
"description": "Stack implementation and knowledge.",
|
||||
"type": "agent",
|
||||
"path": "agents/vite-react-ts-convex-tailwind/.opencode/agent/VRTCT-brain.md",
|
||||
"target": "agent/VRTCT-brain.md"
|
||||
},
|
||||
{
|
||||
"name": "convex-database-expert",
|
||||
"description": "Senior Convex database engineer.",
|
||||
"type": "agent",
|
||||
"path": "agents/vite-react-ts-convex-tailwind/.opencode/agent/convex-database-expert.md",
|
||||
"target": "agent/convex-database-expert.md"
|
||||
},
|
||||
{
|
||||
"name": "react-19-master",
|
||||
"description": "Deep React 19.2 mastery.",
|
||||
"type": "agent",
|
||||
"path": "agents/vite-react-ts-convex-tailwind/.opencode/agent/react-19-master.md",
|
||||
"target": "agent/react-19-master.md"
|
||||
},
|
||||
{
|
||||
"name": "tailwind-41-architect",
|
||||
"description": "Tailwind 4.1 maestro.",
|
||||
"type": "agent",
|
||||
"path": "agents/vite-react-ts-convex-tailwind/.opencode/agent/tailwind-41-architect.md",
|
||||
"target": "agent/tailwind-41-architect.md"
|
||||
},
|
||||
{
|
||||
"name": "typescript-59-engineer",
|
||||
"description": "Strict TypeScript 5.9 expert.",
|
||||
"type": "agent",
|
||||
"path": "agents/vite-react-ts-convex-tailwind/.opencode/agent/typescript-59-engineer.md",
|
||||
"target": "agent/typescript-59-engineer.md"
|
||||
},
|
||||
{
|
||||
"name": "component-create",
|
||||
"description": "Generate spec-compliant components.",
|
||||
"type": "command",
|
||||
"path": "agents/vite-react-ts-convex-tailwind/.opencode/command/component-create.md",
|
||||
"target": "command/component-create.md"
|
||||
},
|
||||
{
|
||||
"name": "component-review",
|
||||
"description": "Audit components against spec.",
|
||||
"type": "command",
|
||||
"path": "agents/vite-react-ts-convex-tailwind/.opencode/command/component-review.md",
|
||||
"target": "command/component-review.md"
|
||||
},
|
||||
{
|
||||
"name": "CODING-TS",
|
||||
"description": "Universal engineering guidelines.",
|
||||
"type": "doc",
|
||||
"path": "agents/vite-react-ts-convex-tailwind/CODING-TS.md",
|
||||
"target": "CODING-TS.md"
|
||||
},
|
||||
{
|
||||
"name": "CONVEX",
|
||||
"description": "Convex best practices guide.",
|
||||
"type": "doc",
|
||||
"path": "agents/vite-react-ts-convex-tailwind/CONVEX.md",
|
||||
"target": "CONVEX.md"
|
||||
},
|
||||
{
|
||||
"name": "REACT19",
|
||||
"description": "React 19 reference patterns.",
|
||||
"type": "doc",
|
||||
"path": "agents/vite-react-ts-convex-tailwind/REACT19.md",
|
||||
"target": "REACT19.md"
|
||||
},
|
||||
{
|
||||
"name": "TAILWIND4",
|
||||
"description": "Tailwind 4.1 migration guide.",
|
||||
"type": "doc",
|
||||
"path": "agents/vite-react-ts-convex-tailwind/TAILWIND4.md",
|
||||
"target": "TAILWIND4.md"
|
||||
},
|
||||
{
|
||||
"name": "TS59",
|
||||
"description": "TypeScript 5.9 language reference.",
|
||||
"type": "doc",
|
||||
"path": "agents/vite-react-ts-convex-tailwind/TS59.md",
|
||||
"target": "TS59.md"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "parallel-PRD",
|
||||
"description": "Parallel PRD planning and Best-of-N synthesis pack.",
|
||||
"path": "agents/parallel-PRD",
|
||||
"items": [
|
||||
{
|
||||
"name": "parallel-prd-orchestrator",
|
||||
"description": "Orchestrates parallel PRD planners.",
|
||||
"type": "agent",
|
||||
"path": "agents/parallel-PRD/.opencode/agent/parallel-prd-orchestrator.md",
|
||||
"target": "agent/parallel-prd-orchestrator.md"
|
||||
},
|
||||
{
|
||||
"name": "glm-planner",
|
||||
"description": "GLM-based PRD planner subagent example.",
|
||||
"type": "agent",
|
||||
"path": "agents/parallel-PRD/.opencode/agent/glm-planner.md",
|
||||
"target": "agent/glm-planner.md"
|
||||
},
|
||||
{
|
||||
"name": "TEMPLATE-planner",
|
||||
"description": "Standard PRD planner template agent. Copy, rename, set your own model",
|
||||
"type": "agent",
|
||||
"path": "agents/parallel-PRD/.opencode/agent/TEMPLATE-planner.md",
|
||||
"target": "agent/TEMPLATE-planner.md"
|
||||
},
|
||||
{
|
||||
"name": "parallel-prd",
|
||||
"description": "Generate parallel PRD architecture views.",
|
||||
"type": "command",
|
||||
"path": "agents/parallel-PRD/.opencode/command/parallel-prd.md",
|
||||
"target": "command/parallel-prd.md"
|
||||
},
|
||||
{
|
||||
"name": "prd-authoring",
|
||||
"description": "Skill for drafting technical PRDs.",
|
||||
"type": "skill",
|
||||
"path": "agents/parallel-PRD/.opencode/skill/prd-authoring",
|
||||
"target": "skill/prd-authoring"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "component-engineer",
|
||||
"description": "Professional React component engineering and audit pack.",
|
||||
"path": "agents/component-engineer",
|
||||
"items": [
|
||||
{
|
||||
"name": "component-engineer",
|
||||
"description": "Expert React architecture specialist.",
|
||||
"type": "agent",
|
||||
"path": "agents/component-engineer/.opencode/agent/component-engineer.md",
|
||||
"target": "agent/component-engineer.md"
|
||||
},
|
||||
{
|
||||
"name": "component-create",
|
||||
"description": "Create spec-compliant React components.",
|
||||
"type": "command",
|
||||
"path": "agents/component-engineer/.opencode/command/component-create.md",
|
||||
"target": "command/component-create.md"
|
||||
},
|
||||
{
|
||||
"name": "component-review",
|
||||
"description": "Audit components against engineering spec.",
|
||||
"type": "command",
|
||||
"path": "agents/component-engineer/.opencode/command/component-review.md",
|
||||
"target": "command/component-review.md"
|
||||
},
|
||||
{
|
||||
"name": "component-engineering",
|
||||
"description": "Component engineering specification skill.",
|
||||
"type": "skill",
|
||||
"path": "agents/component-engineer/.opencode/skill/component-engineering",
|
||||
"target": "skill/component-engineering"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "create-opencode-plugin",
|
||||
"description": "Opencode plugin development and scaffolding pack. Works best when installed in opencode repository.",
|
||||
"path": "agents/create-opencode-plugin",
|
||||
"items": [
|
||||
{
|
||||
"name": "plugin-creator",
|
||||
"description": "Specialist for building opencode plugins.",
|
||||
"type": "agent",
|
||||
"path": "agents/create-opencode-plugin/.opencode/agent/plugin-creator.md",
|
||||
"target": "agent/plugin-creator.md"
|
||||
},
|
||||
{
|
||||
"name": "create-plugin",
|
||||
"description": "Scaffold a new opencode plugin based on description.",
|
||||
"type": "command",
|
||||
"path": "agents/create-opencode-plugin/.opencode/command/create-plugin.md",
|
||||
"target": "command/create-plugin.md"
|
||||
},
|
||||
{
|
||||
"name": "create-opencode-plugin",
|
||||
"description": "Plugin development workflow skill.",
|
||||
"type": "skill",
|
||||
"path": "agents/create-opencode-plugin/.opencode/skill/create-opencode-plugin",
|
||||
"target": "skill/create-opencode-plugin"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "opencode-configurator",
|
||||
"description": "Meta-configuration management system pack.",
|
||||
"path": "agents/opencode-configurator",
|
||||
"items": [
|
||||
{
|
||||
"name": "opencode-configurator",
|
||||
"description": "Opencode configuration agent.",
|
||||
"type": "agent",
|
||||
"path": "agents/opencode-configurator/agent/opencode-configurator.md",
|
||||
"target": "agent/opencode-configurator.md"
|
||||
},
|
||||
{
|
||||
"name": "refactor-rfc-xml",
|
||||
"description": "Structure markdown files with RFC/XML for better agent adherence.",
|
||||
"type": "command",
|
||||
"path": "agents/opencode-configurator/command/refactor-rfc-xml.md",
|
||||
"target": "command/refactor-rfc-xml.md"
|
||||
},
|
||||
{
|
||||
"name": "permissions-update",
|
||||
"description": "Modernize configuration schema fields.",
|
||||
"type": "command",
|
||||
"path": "agents/opencode-configurator/command/permissions-update.md",
|
||||
"target": "command/permissions-update.md"
|
||||
},
|
||||
{
|
||||
"name": "agent-architect",
|
||||
"description": "Design and refine agents.",
|
||||
"type": "skill",
|
||||
"path": "agents/opencode-configurator/skill/agent-architect",
|
||||
"target": "skill/agent-architect"
|
||||
},
|
||||
{
|
||||
"name": "command-creator",
|
||||
"description": "Build custom slash commands.",
|
||||
"type": "skill",
|
||||
"path": "agents/opencode-configurator/skill/command-creator",
|
||||
"target": "skill/command-creator"
|
||||
},
|
||||
{
|
||||
"name": "mcp-installer",
|
||||
"description": "Configure MCP server connections.",
|
||||
"type": "skill",
|
||||
"path": "agents/opencode-configurator/skill/mcp-installer",
|
||||
"target": "skill/mcp-installer"
|
||||
},
|
||||
{
|
||||
"name": "model-researcher",
|
||||
"description": "Add verified AI models.",
|
||||
"type": "skill",
|
||||
"path": "agents/opencode-configurator/skill/model-researcher",
|
||||
"target": "skill/model-researcher"
|
||||
},
|
||||
{
|
||||
"name": "opencode-config",
|
||||
"description": "Guided opencode.json setup.",
|
||||
"type": "skill",
|
||||
"path": "agents/opencode-configurator/skill/opencode-config",
|
||||
"target": "skill/opencode-config"
|
||||
},
|
||||
{
|
||||
"name": "plugin-installer",
|
||||
"description": "Install and document community ecosystem plugins.",
|
||||
"type": "skill",
|
||||
"path": "agents/opencode-configurator/skill/plugin-installer",
|
||||
"target": "skill/plugin-installer"
|
||||
},
|
||||
{
|
||||
"name": "skill-creator",
|
||||
"description": "Guide for creating skills.",
|
||||
"type": "skill",
|
||||
"path": "agents/opencode-configurator/skill/skill-creator",
|
||||
"target": "skill/skill-creator"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "security-reviewer",
|
||||
"description": "Vibecoding security audit and secret scanning pack.",
|
||||
"path": "agents/security-reviewer",
|
||||
"items": [
|
||||
{
|
||||
"name": "security-reviewer",
|
||||
"description": "Security auditor for vibecoding vulnerabilities.",
|
||||
"type": "agent",
|
||||
"path": "agents/security-reviewer/.opencode/agent/security-reviewer.md",
|
||||
"target": "agent/security-reviewer.md"
|
||||
},
|
||||
{
|
||||
"name": "security-ai-keys",
|
||||
"description": "Scan for exposed AI API keys.",
|
||||
"type": "skill",
|
||||
"path": "agents/security-reviewer/.opencode/skill/security-ai-keys",
|
||||
"target": "skill/security-ai-keys"
|
||||
},
|
||||
{
|
||||
"name": "security-bun",
|
||||
"description": "Bun-specific security audit skill.",
|
||||
"type": "skill",
|
||||
"path": "agents/security-reviewer/.opencode/skill/security-bun",
|
||||
"target": "skill/security-bun"
|
||||
},
|
||||
{
|
||||
"name": "security-convex",
|
||||
"description": "Convex-specific security audit skill.",
|
||||
"type": "skill",
|
||||
"path": "agents/security-reviewer/.opencode/skill/security-convex",
|
||||
"target": "skill/security-convex"
|
||||
},
|
||||
{
|
||||
"name": "security-django",
|
||||
"description": "Django-specific security audit skill.",
|
||||
"type": "skill",
|
||||
"path": "agents/security-reviewer/.opencode/skill/security-django",
|
||||
"target": "skill/security-django"
|
||||
},
|
||||
{
|
||||
"name": "security-docker",
|
||||
"description": "Docker-specific security audit skill.",
|
||||
"type": "skill",
|
||||
"path": "agents/security-reviewer/.opencode/skill/security-docker",
|
||||
"target": "skill/security-docker"
|
||||
},
|
||||
{
|
||||
"name": "security-express",
|
||||
"description": "Express-specific security audit skill.",
|
||||
"type": "skill",
|
||||
"path": "agents/security-reviewer/.opencode/skill/security-express",
|
||||
"target": "skill/security-express"
|
||||
},
|
||||
{
|
||||
"name": "security-fastapi",
|
||||
"description": "FastAPI-specific security audit skill.",
|
||||
"type": "skill",
|
||||
"path": "agents/security-reviewer/.opencode/skill/security-fastapi",
|
||||
"target": "skill/security-fastapi"
|
||||
},
|
||||
{
|
||||
"name": "security-nextjs",
|
||||
"description": "Next.js-specific security audit skill.",
|
||||
"type": "skill",
|
||||
"path": "agents/security-reviewer/.opencode/skill/security-nextjs",
|
||||
"target": "skill/security-nextjs"
|
||||
},
|
||||
{
|
||||
"name": "security-secrets",
|
||||
"description": "Generic secret and credential scanner.",
|
||||
"type": "skill",
|
||||
"path": "agents/security-reviewer/.opencode/skill/security-secrets",
|
||||
"target": "skill/security-secrets"
|
||||
},
|
||||
{
|
||||
"name": "security-vite",
|
||||
"description": "Vite-specific security audit skill.",
|
||||
"type": "skill",
|
||||
"path": "agents/security-reviewer/.opencode/skill/security-vite",
|
||||
"target": "skill/security-vite"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "repo-navigator",
|
||||
"description": "Repository documentation specialist pack for enhanced /init.",
|
||||
"path": "agents/repo-navigator",
|
||||
"items": [
|
||||
{
|
||||
"name": "repo-navigator",
|
||||
"description": "Generate AGENTS.md documentation.",
|
||||
"type": "agent",
|
||||
"path": "agents/repo-navigator/agent/repo-navigator.md",
|
||||
"target": "agent/repo-navigator.md"
|
||||
},
|
||||
{
|
||||
"name": "init",
|
||||
"description": "Init command replacement. Supports vanilla `/init`, `/init user` for user-assistance agent-guidance & `/init full` with AGENTS.md + skills generation.",
|
||||
"type": "command",
|
||||
"path": "agents/repo-navigator/command/init.md",
|
||||
"target": "command/init.md"
|
||||
},
|
||||
{
|
||||
"name": "learn",
|
||||
"description": "A version of learn command hyperfocussed on minimal nested AGENTS.md creation.",
|
||||
"type": "command",
|
||||
"path": "agents/repo-navigator/command/learn.md",
|
||||
"target": "command/learn.md"
|
||||
},
|
||||
{
|
||||
"name": "agent-navigation-sop",
|
||||
"description": "AI-focused navigation SOP.",
|
||||
"type": "skill",
|
||||
"path": "agents/repo-navigator/skill/agent-navigation-sop",
|
||||
"target": "skill/agent-navigation-sop"
|
||||
},
|
||||
{
|
||||
"name": "user-onboarding-sop",
|
||||
"description": "User-focused onboarding SOP.",
|
||||
"type": "skill",
|
||||
"path": "agents/repo-navigator/skill/user-onboarding-sop",
|
||||
"target": "skill/user-onboarding-sop"
|
||||
},
|
||||
{
|
||||
"name": "skill-creator",
|
||||
"description": "Architect and refine skills.",
|
||||
"type": "skill",
|
||||
"path": "agents/repo-navigator/skill/skill-creator",
|
||||
"target": "skill/skill-creator"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "cowork",
|
||||
"description": "Multi-agent orchestration system for complex collaborative tasks.",
|
||||
"path": "cowork",
|
||||
"items": [
|
||||
{
|
||||
"name": "cowork-orchestrator",
|
||||
"description": "Master coordinator for cowork sessions.",
|
||||
"type": "agent",
|
||||
"path": "cowork/.opencode/agent/cowork-orchestrator.md",
|
||||
"target": "agent/cowork-orchestrator.md"
|
||||
},
|
||||
{
|
||||
"name": "admin-assistant",
|
||||
"description": "Task management and administrative support.",
|
||||
"type": "agent",
|
||||
"path": "cowork/.opencode/agent/admin-assistant.md",
|
||||
"target": "agent/admin-assistant.md"
|
||||
},
|
||||
{
|
||||
"name": "research-specialist",
|
||||
"description": "Deep research and information synthesis.",
|
||||
"type": "agent",
|
||||
"path": "cowork/.opencode/agent/research-specialist.md",
|
||||
"target": "agent/research-specialist.md"
|
||||
},
|
||||
{
|
||||
"name": "data-analyst",
|
||||
"description": "Data processing and pattern analysis.",
|
||||
"type": "agent",
|
||||
"path": "cowork/.opencode/agent/data-analyst.md",
|
||||
"target": "agent/data-analyst.md"
|
||||
},
|
||||
{
|
||||
"name": "document-specialist",
|
||||
"description": "Professional documentation and report authoring.",
|
||||
"type": "agent",
|
||||
"path": "cowork/.opencode/agent/document-specialist.md",
|
||||
"target": "agent/document-specialist.md"
|
||||
},
|
||||
{
|
||||
"name": "presentation-expert",
|
||||
"description": "Visual communication and slide design.",
|
||||
"type": "agent",
|
||||
"path": "cowork/.opencode/agent/presentation-expert.md",
|
||||
"target": "agent/presentation-expert.md"
|
||||
},
|
||||
{
|
||||
"name": "cowork-configurator",
|
||||
"description": "Configuration expert for cowork environment.",
|
||||
"type": "agent",
|
||||
"path": "cowork/.opencode/agent/cowork-configurator.md",
|
||||
"target": "agent/cowork-configurator.md"
|
||||
},
|
||||
{
|
||||
"name": "cowork-cmd",
|
||||
"description": "Launch a collaborative cowork session.",
|
||||
"type": "command",
|
||||
"path": "cowork/.opencode/command/cowork.md",
|
||||
"target": "command/cowork.md"
|
||||
},
|
||||
{
|
||||
"name": "cowork-skill",
|
||||
"description": "Core cowork orchestration logic.",
|
||||
"type": "skill",
|
||||
"path": "cowork/.opencode/skill/cowork",
|
||||
"target": "skill/cowork"
|
||||
},
|
||||
{
|
||||
"name": "branding",
|
||||
"description": "Visual identity and branding guidelines.",
|
||||
"type": "skill",
|
||||
"path": "cowork/.opencode/skill/branding",
|
||||
"target": "skill/branding"
|
||||
},
|
||||
{
|
||||
"name": "comms",
|
||||
"description": "Communication templates and strategies.",
|
||||
"type": "skill",
|
||||
"path": "cowork/.opencode/skill/comms",
|
||||
"target": "skill/comms"
|
||||
},
|
||||
{
|
||||
"name": "excel",
|
||||
"description": "Advanced spreadsheet automation and analysis.",
|
||||
"type": "skill",
|
||||
"path": "cowork/.opencode/skill/excel",
|
||||
"target": "skill/excel"
|
||||
},
|
||||
{
|
||||
"name": "pdf",
|
||||
"description": "PDF manipulation and form processing.",
|
||||
"type": "skill",
|
||||
"path": "cowork/.opencode/skill/pdf",
|
||||
"target": "skill/pdf"
|
||||
},
|
||||
{
|
||||
"name": "powerpoint",
|
||||
"description": "Presentation generation and OOXML tools.",
|
||||
"type": "skill",
|
||||
"path": "cowork/.opencode/skill/powerpoint",
|
||||
"target": "skill/powerpoint"
|
||||
},
|
||||
{
|
||||
"name": "themes",
|
||||
"description": "Document themes and visual styles.",
|
||||
"type": "skill",
|
||||
"path": "cowork/.opencode/skill/themes",
|
||||
"target": "skill/themes"
|
||||
},
|
||||
{
|
||||
"name": "word",
|
||||
"description": "Word document automation and OOXML tools.",
|
||||
"type": "skill",
|
||||
"path": "cowork/.opencode/skill/word",
|
||||
"target": "skill/word"
|
||||
},
|
||||
{
|
||||
"name": "writing",
|
||||
"description": "Professional writing and editing workflow.",
|
||||
"type": "skill",
|
||||
"path": "cowork/.opencode/skill/writing",
|
||||
"target": "skill/writing"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"standalone": [
|
||||
{
|
||||
"name": "fast",
|
||||
"description": "High-speed atomic task workhorse. Configure to a model like Haiku. Pair with `smart`.",
|
||||
"type": "agent",
|
||||
"path": "agents/generic/.opencode/agent/fast.md",
|
||||
"target": "agent/fast.md"
|
||||
},
|
||||
{
|
||||
"name": "smart",
|
||||
"description": "Senior architect for complex refactoring. Configure to a model like Opus. Pair with `fast`.",
|
||||
"type": "agent",
|
||||
"path": "agents/generic/.opencode/agent/smart.md",
|
||||
"target": "agent/smart.md"
|
||||
},
|
||||
{
|
||||
"name": "subagent-orchestrator",
|
||||
"description": "Disciplined subagent dispatcher and coordinator.",
|
||||
"type": "agent",
|
||||
"path": "agents/generic/.opencode/agent/subagent-orchestrator.md",
|
||||
"target": "agent/subagent-orchestrator.md"
|
||||
},
|
||||
{
|
||||
"name": "openspec-orchestrator",
|
||||
"description": "Enforce strict OpenSpec formatting/validation. For use with Openspec only.",
|
||||
"type": "agent",
|
||||
"path": "agents/generic/.opencode/agent/openspec-orchestrator.md",
|
||||
"target": "agent/openspec-orchestrator.md"
|
||||
},
|
||||
{
|
||||
"name": "learn",
|
||||
"description": "A version of learn command hyperfocussed on minimal nested AGENTS.md creation.",
|
||||
"type": "command",
|
||||
"path": "commands/.opencode/command/learn.md",
|
||||
"target": "command/learn.md"
|
||||
},
|
||||
{
|
||||
"name": "rmslop",
|
||||
"description": "Remove AI-generated boilerplate/slop. Adjusted from OC repo command.",
|
||||
"type": "command",
|
||||
"path": "commands/.opencode/command/rmslop.md",
|
||||
"target": "command/rmslop.md"
|
||||
},
|
||||
{
|
||||
"name": "loop",
|
||||
"description": "Run autonomous development loops. Kinda Ralph, but via subagents.",
|
||||
"type": "command",
|
||||
"path": "commands/.opencode/command/loop.md",
|
||||
"target": "command/loop.md"
|
||||
},
|
||||
{
|
||||
"name": "howto",
|
||||
"description": "Generate AGENTS.md tailored towards teaching the user how to use a repo.",
|
||||
"type": "command",
|
||||
"path": "commands/.opencode/command/howto.md",
|
||||
"target": "command/howto.md"
|
||||
},
|
||||
{
|
||||
"name": "create-pack",
|
||||
"description": "Scaffold a new agent pack. Similar to you're looking at here.",
|
||||
"type": "command",
|
||||
"path": "commands/.opencode/command/create-pack.md",
|
||||
"target": "command/create-pack.md"
|
||||
},
|
||||
{
|
||||
"name": "refactor",
|
||||
"description": "Refactor code according to best practices.",
|
||||
"type": "command",
|
||||
"path": "commands/.opencode/command/refactor.md",
|
||||
"target": "command/refactor.md"
|
||||
},
|
||||
{
|
||||
"name": "npm",
|
||||
"description": "Helper for npm package management.",
|
||||
"type": "command",
|
||||
"path": "commands/.opencode/command/npm.md",
|
||||
"target": "command/npm.md"
|
||||
},
|
||||
{
|
||||
"name": "improve-run",
|
||||
"description": "Run and improve prompts.",
|
||||
"type": "command",
|
||||
"path": "commands/.opencode/command/improve-run.md",
|
||||
"target": "command/improve-run.md"
|
||||
},
|
||||
{
|
||||
"name": "improve-save",
|
||||
"description": "Improve and save a prompt.",
|
||||
"type": "command",
|
||||
"path": "commands/.opencode/command/improve-save.md",
|
||||
"target": "command/improve-save.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
/**
|
||||
* Registry Generation Script
|
||||
* Collates pack manifests and standalone items into a master registry.
|
||||
*/
|
||||
async function generate() {
|
||||
const rootDir = '/home/igorw/Work/Opencode-Workflows';
|
||||
const finalRegistryPath = path.join(rootDir, 'registry.json');
|
||||
|
||||
const registry = {
|
||||
name: "opencode-workflows",
|
||||
version: "1.0.0",
|
||||
packs: [],
|
||||
standalone: []
|
||||
};
|
||||
|
||||
const manifests = [
|
||||
'agents/generic/registry.json',
|
||||
'agents/vite-react-ts-convex-tailwind/registry.json',
|
||||
'agents/parallel-PRD/registry.json',
|
||||
'agents/component-engineer/registry.json',
|
||||
'agents/create-opencode-plugin/registry.json',
|
||||
'agents/opencode-configurator/registry.json',
|
||||
'agents/security-reviewer/registry.json',
|
||||
'agents/repo-navigator/registry.json',
|
||||
'commands/registry.json',
|
||||
'cowork/registry.json'
|
||||
];
|
||||
|
||||
for (const relPath of manifests) {
|
||||
const fullPath = path.join(rootDir, relPath);
|
||||
if (!fs.existsSync(fullPath)) continue;
|
||||
|
||||
try {
|
||||
const content = fs.readFileSync(fullPath, 'utf-8');
|
||||
const data = JSON.parse(content);
|
||||
const baseDir = path.dirname(relPath);
|
||||
|
||||
const normalizedItems = data.items.map(item => ({
|
||||
...item,
|
||||
path: path.join(baseDir, item.path)
|
||||
}));
|
||||
|
||||
// standalone detection
|
||||
if (relPath === 'agents/generic/registry.json' || relPath === 'commands/registry.json') {
|
||||
registry.standalone.push(...normalizedItems);
|
||||
} else {
|
||||
registry.packs.push({
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
path: baseDir,
|
||||
items: normalizedItems
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`Error processing ${relPath}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync(finalRegistryPath, JSON.stringify(registry, null, 2));
|
||||
console.log(`Master registry generated with ${registry.packs.length} packs and ${registry.standalone.length} standalone items.`);
|
||||
}
|
||||
|
||||
generate();
|
||||
Executable
+75
@@ -0,0 +1,75 @@
|
||||
#!/bin/sh
|
||||
# Locks or unlocks plugins in the config file to a specific version.
|
||||
# This speeds up the startup time and possibly prevents bun install errors.
|
||||
#
|
||||
# Usage:
|
||||
# lock-plugins.sh [--locked | --latest] [--active | --all]
|
||||
#
|
||||
# Options:
|
||||
# --locked: Locks all plugins to the latest version, but pins them, ie 0.5.1
|
||||
# --latest: Unlocks all plugins to the latest version, aka @latest
|
||||
#
|
||||
# --active (default): affects only active, uncommented plugins
|
||||
# --all: affects all plugins, including those commented out
|
||||
|
||||
CONFIG_PATH="$HOME/.config/opencode/opencode.json"
|
||||
|
||||
# Modes
|
||||
MODE="" # locked | latest
|
||||
SCOPE="active" # active | all
|
||||
|
||||
for arg in "$@"; do
|
||||
case $arg in
|
||||
--locked) MODE="locked" ;;
|
||||
--latest) MODE="latest" ;;
|
||||
--active) SCOPE="active" ;;
|
||||
--all) SCOPE="all" ;;
|
||||
*) echo "Unknown argument: $arg"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$MODE" ]; then
|
||||
echo "Usage: $0 [--locked | --latest] [--active | --all]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$CONFIG_PATH" ]; then
|
||||
echo "Config not found at $CONFIG_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create a temporary file
|
||||
TEMP_FILE=$(mktemp)
|
||||
cp "$CONFIG_PATH" "$TEMP_FILE"
|
||||
|
||||
# Process with Perl
|
||||
# State machine approach to safely ignore the "plugin" key itself
|
||||
perl -i -pe '
|
||||
BEGIN { $in_plugins = 0; $mode = "'"$MODE"'"; $scope = "'"$SCOPE"'"; }
|
||||
|
||||
if (/"plugin":\s*\[/) { $in_plugins = 1; next; }
|
||||
if ($in_plugins && /\]/) { $in_plugins = 0; }
|
||||
|
||||
if ($in_plugins) {
|
||||
# Skip any line that looks like a key-value pair (contains :) to avoid matching "plugin"
|
||||
if (!/:/ && /^\s*(\/\/)?\s*"((?:@[^\/]+\/)?[^@"]+)(?:@([^"]+))?"/) {
|
||||
$is_commented = $1 ? 1 : 0;
|
||||
$pkg = $2;
|
||||
|
||||
if ($pkg !~ /^file:\/\//) {
|
||||
if ($scope eq "all" || !$is_commented) {
|
||||
$target_ver = "latest";
|
||||
if ($mode eq "locked") {
|
||||
print STDERR "Fetching version for $pkg...\n";
|
||||
$target_ver = `npm show $pkg version 2>/dev/null` || "latest";
|
||||
chomp($target_ver);
|
||||
}
|
||||
s/"\Q$pkg\E(?:@[^"]+)?"/"$pkg\@$target_ver"/;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
' "$TEMP_FILE"
|
||||
|
||||
mv "$TEMP_FILE" "$CONFIG_PATH"
|
||||
echo "Plugins updated: mode=$MODE, scope=$SCOPE"
|
||||
Reference in New Issue
Block a user