From 6b62d012fac126a59fd253ba13ea9ed76cd334bd Mon Sep 17 00:00:00 2001 From: Israel Fruchter Date: Sun, 26 Apr 2026 12:19:57 +0300 Subject: [PATCH 1/4] feat(opencode): add build script and JS plugin for OpenCode adaptation - platforms/opencode/build.sh: 8-phase transform pipeline producing commands, agents, instructions, and config fragments for OpenCode - platforms/opencode/plugin/: TypeScript plugin with tool.execute.before hook (destructive command blocking) and experimental.session.compacting hook (post-compaction workflow reminder) - .gitignore: exclude opencode build output and node_modules --- .gitignore | 7 + platforms/opencode/build.sh | 382 ++++++++++++++++++++++++ platforms/opencode/plugin/index.ts | 29 ++ platforms/opencode/plugin/package.json | 15 + platforms/opencode/plugin/tsconfig.json | 12 + 5 files changed, 445 insertions(+) create mode 100755 platforms/opencode/build.sh create mode 100644 platforms/opencode/plugin/index.ts create mode 100644 platforms/opencode/plugin/package.json create mode 100644 platforms/opencode/plugin/tsconfig.json diff --git a/.gitignore b/.gitignore index 0db5ed8..870a082 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,10 @@ # Maister task tracking (generated per-project, not part of plugin source) .maister/ /.worktrees/ + +# OpenCode build output (generated by build.sh) +platforms/opencode/output/ + +# Node modules (for OpenCode plugin) +platforms/opencode/plugin/node_modules/ +platforms/opencode/plugin/package-lock.json diff --git a/platforms/opencode/build.sh b/platforms/opencode/build.sh new file mode 100755 index 0000000..862aa5f --- /dev/null +++ b/platforms/opencode/build.sh @@ -0,0 +1,382 @@ +#!/bin/bash +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +CORE="$ROOT/plugins/maister" +OUT="$SCRIPT_DIR/output" + +# Cross-platform sed in-place (macOS needs '' arg, Linux doesn't) +sedi() { + if [[ "$OSTYPE" == "darwin"* ]]; then + sed -i '' "$@" + else + sed -i "$@" + fi +} + +echo "=== Maister → OpenCode Build ===" +echo "Source: $CORE" +echo "Output: $OUT" +echo "" + +# ───────────────────────────────────────────────────────────── +# Phase 1: Copy source +# ───────────────────────────────────────────────────────────── +echo "[Phase 1] Copying source..." +rm -rf "$OUT" +mkdir -p "$OUT" +cp -r "$CORE/." "$OUT/" + +# ───────────────────────────────────────────────────────────── +# Phase 2: Strip Claude Code artifacts +# ───────────────────────────────────────────────────────────── +echo "[Phase 2] Stripping Claude Code artifacts..." +rm -rf "$OUT/.claude-plugin" +rm -rf "$OUT/hooks" +rm -rf "$OUT/skills" +rm -f "$OUT/.mcp.json" + +# ───────────────────────────────────────────────────────────── +# Phase 3: Transform commands +# ───────────────────────────────────────────────────────────── +echo "[Phase 3] Transforming commands..." + +# Mapping: command filename (no extension) → agent name +get_agent_for_command() { + local cmd="$1" + case "$cmd" in + work) echo "maister-development" ;; + development) echo "maister-development" ;; + research) echo "maister-research" ;; + performance) echo "maister-performance" ;; + migration) echo "maister-migration" ;; + product-design) echo "maister-product-design" ;; + quick-plan) echo "maister-planner" ;; + quick-dev) echo "maister-developer" ;; + quick-bugfix) echo "maister-bugfix" ;; + reviews-code) echo "maister-code-reviewer" ;; + reviews-pragmatic) echo "maister-code-quality-pragmatist" ;; + reviews-spec-audit) echo "maister-spec-auditor" ;; + reviews-reality-check) echo "maister-reality-assessor" ;; + reviews-production-readiness) echo "maister-production-readiness-checker" ;; + *) echo "maister-development" ;; + esac +} + +find "$OUT/commands" -name "*.md" | while read -r f; do + cmd_name="$(basename "$f" .md)" + agent_name="$(get_agent_for_command "$cmd_name")" + + # Strip "name:" line from frontmatter + sedi '/^name: /d' "$f" + + # Add agent: and subtask: true into frontmatter (after the opening ---) + # Only if frontmatter exists (file starts with ---) + if head -1 "$f" | grep -q '^---$'; then + sedi "0,/^---\$/{/^---\$/!b; a\\ +agent: $agent_name\\ +subtask: true +}" "$f" + fi + + # Replace Skill(name="X") invocations with OpenCode instruction references + # Pattern: Skill(name="maister:foo") → Follow the workflow in @.opencode/instructions/maister-foo.md + sedi 's|Skill(name="maister:\([^"]*\)")|Follow the workflow in @.opencode/instructions/maister-\1.md|g' "$f" + sedi "s|Skill(name='maister:\([^']*\)')|Follow the workflow in @.opencode/instructions/maister-\1.md|g" "$f" + + # Strip maister: prefix from command references in body + sedi 's|/maister:\([a-z-]*\)|/\1|g' "$f" +done + +# ───────────────────────────────────────────────────────────── +# Phase 4: Transform agents → build agent-fragment.json +# ───────────────────────────────────────────────────────────── +echo "[Phase 4] Transforming agents and building agent-fragment.json..." + +mkdir -p "$OUT/agents" + +# We'll accumulate agent JSON entries in a temp file, build with Python at end +AGENT_ENTRIES_FILE="$(mktemp)" +echo "{}" > "$AGENT_ENTRIES_FILE" + +color_to_hex() { + case "$1" in + purple) echo "#9B59B6" ;; + blue) echo "#3498DB" ;; + green) echo "#2ECC71" ;; + red) echo "#E74C3C" ;; + orange) echo "#E67E22" ;; + yellow) echo "#F1C40F" ;; + cyan) echo "#1ABC9C" ;; + pink) echo "#E91E63" ;; + gray) echo "#95A5A6" ;; + *) echo "#95A5A6" ;; + esac +} + +# Process each agent file +for src_agent in "$CORE/agents/"*.md; do + filename="$(basename "$src_agent")" + agent_slug="${filename%.md}" + agent_id="maister-${agent_slug}" + dest="$OUT/agents/${filename}" + + # Parse YAML frontmatter fields + name_val="$(awk '/^---/{f++; next} f==1{print}' "$src_agent" | grep '^name:' | sed 's/^name:[[:space:]]*//')" + desc_val="$(awk '/^---/{f++; next} f==1{print}' "$src_agent" | grep '^description:' | sed 's/^description:[[:space:]]*//')" + model_val="$(awk '/^---/{f++; next} f==1{print}' "$src_agent" | grep '^model:' | sed 's/^model:[[:space:]]*//')" + color_val="$(awk '/^---/{f++; next} f==1{print}' "$src_agent" | grep '^color:' | sed 's/^color:[[:space:]]*//')" + + hex_color="$(color_to_hex "$color_val")" + + # Strip frontmatter: write body only (after second ---) to dest + awk 'BEGIN{f=0} /^---/{f++; if(f==2){found=1; next}} found{print}' "$src_agent" > "$dest" + + # Build JSON entry with Python to handle special chars + python3 -c " +import json, sys + +agent_id = sys.argv[1] +desc = sys.argv[2] +color = sys.argv[3] +model = sys.argv[4] +slug = sys.argv[5] + +entry = { + 'description': desc, + 'mode': 'subagent', + 'prompt': '{file:.opencode/agents/' + slug + '.md}', + 'color': color +} + +# Only add model if not 'inherit' and not empty +if model and model != 'inherit': + entry['model'] = model + +# Read existing JSON, add entry, write back +with open(sys.argv[6], 'r') as fh: + data = json.load(fh) +data[agent_id] = entry +with open(sys.argv[6], 'w') as fh: + json.dump(data, fh, indent=2) +" "$agent_id" "$desc_val" "$hex_color" "$model_val" "$agent_slug" "$AGENT_ENTRIES_FILE" +done + +# ───────────────────────────────────────────────────────────── +# Phase 5: Global text replacements across all output files +# ───────────────────────────────────────────────────────────── +echo "[Phase 5] Running global text replacements..." + +run_global_replacements() { + local f="$1" + sedi 's/`AskUserQuestion`/ask the user conversationally/g' "$f" + sedi 's/AskUserQuestion(\([^)]*\))/ask the user conversationally about \1/g' "$f" + sedi 's/AskUserQuestion/ask the user conversationally/g' "$f" + sedi 's/`TaskCreate`/Create a todo item/g' "$f" + sedi 's/`TaskUpdate`/Update the todo item/g' "$f" + sedi 's/TaskCreate(/Create a todo item with (/g' "$f" + sedi 's/TaskUpdate(/Update the todo item (/g' "$f" + sedi 's/TaskCreate\b/Create a todo item/g' "$f" + sedi 's/TaskUpdate\b/Update the todo item/g' "$f" + sedi 's/Task(subagent_type="maister:\([^"]*\)")/Use the @maister-\1 agent/g' "$f" + sedi "s/Task(subagent_type='maister:\([^']*\)')/Use the @maister-\1 agent/g" "$f" + sedi 's/CLAUDE\.md/.opencode\/instructions\/maister-rules.md/g' "$f" + sedi 's|/maister:\([a-z-]*\)|/\1|g' "$f" + sedi 's/maister:\([a-z-][a-z-]*\)/maister-\1/g' "$f" + sedi 's|Skill(name="\([^"]*\)")|Follow the workflow in @.opencode/instructions/\1.md|g' "$f" +} + +find "$OUT" -name "*.md" | while read -r f; do + run_global_replacements "$f" +done + +# Also run on the agent entries file's source docs... they'll be processed after copy + +# ───────────────────────────────────────────────────────────── +# Phase 6: MCP fragment +# ───────────────────────────────────────────────────────────── +echo "[Phase 6] Building mcp-fragment.json..." + +python3 -c " +import json + +with open('$CORE/.mcp.json', 'r') as f: + mcp = json.load(f) + +# OpenCode uses same mcpServers schema +out = mcp.get('mcpServers', mcp) + +with open('$OUT/mcp-fragment.json', 'w') as f: + json.dump(out, f, indent=2) +print(' mcp-fragment.json written') +" + +# ───────────────────────────────────────────────────────────── +# Phase 7: Transform CLAUDE.md → output/instructions/maister-rules.md +# ───────────────────────────────────────────────────────────── +echo "[Phase 7] Building maister-rules.md..." + +mkdir -p "$OUT/instructions" +cp "$CORE/CLAUDE.md" "$OUT/instructions/maister-rules.md" +run_global_replacements "$OUT/instructions/maister-rules.md" + +# Strip .claude-plugin/ and hooks/ specific sections (simple heuristic) +python3 -c " +import re, sys + +with open(sys.argv[1], 'r') as f: + content = f.read() + +# Remove sections that are Claude Code plugin/hooks specific +# These sections start with ## Hooks or ## Claude Code Documentation +patterns = [ + r'(?m)^## Hooks\n.*?(?=^## |\Z)', + r'(?m)^## Claude Code Documentation\n.*?(?=^## |\Z)', +] +for p in patterns: + content = re.sub(p, '', content, flags=re.DOTALL) + +with open(sys.argv[1], 'w') as f: + f.write(content) +" "$OUT/instructions/maister-rules.md" + +# Append skill-invocation-reminder content (the actual message, not the shell wrapper) +cat >> "$OUT/instructions/maister-rules.md" << 'REMINDER_EOF' + +--- + +## OpenCode Skill Invocation Rule + +⚠️ **MAISTER PLUGIN RULE**: When any maister workflow command appears in the user's prompt, you MUST invoke it (follow the corresponding `.opencode/instructions/maister-*.md` workflow) as your FIRST action. No exceptions. Do not analyze the task first, do not decide it's 'straightforward', do not substitute your own approach. The user chose this workflow intentionally. Complexity assessment is the workflow's job, not yours. +REMINDER_EOF + +# Remove the CLAUDE.md copy in output root (it's been transformed into maister-rules.md) +rm -f "$OUT/CLAUDE.md" + +# ───────────────────────────────────────────────────────────── +# Phase 8: Create output/plugins/ directory stub +# ───────────────────────────────────────────────────────────── +echo "[Phase 8] Creating output/plugins/ stub..." + +mkdir -p "$OUT/plugins" +cat > "$OUT/plugins/README.md" << 'STUB_EOF' +# Maister OpenCode Plugin + +Plugin file (maister-plugin.ts) will be copied here by build.sh after Task 3. + +## Installation + +After the plugin file is built, copy the contents of this directory to your project's `.opencode/plugins/` directory. +STUB_EOF + +# ───────────────────────────────────────────────────────────── +# Phase 9: Transform skills → output/instructions/maister-{name}.md +# ───────────────────────────────────────────────────────────── +echo "[Phase 9] Transforming skills..." + +mkdir -p "$OUT/instructions/references" + +# Copy orchestrator-framework shared references first +if [ -d "$CORE/skills/orchestrator-framework/references" ]; then + mkdir -p "$OUT/instructions/references/shared" + cp -r "$CORE/skills/orchestrator-framework/references/." "$OUT/instructions/references/shared/" + find "$OUT/instructions/references/shared" -name "*.md" | while read -r rf; do + run_global_replacements "$rf" + done +fi + +for skill_dir in "$CORE/skills"/*/; do + skill_name="$(basename "$skill_dir")" + + # Skip orchestrator-framework itself (used as shared references) + [ "$skill_name" = "orchestrator-framework" ] && continue + + skill_md="$skill_dir/SKILL.md" + [ -f "$skill_md" ] || continue + + dest_file="$OUT/instructions/maister-${skill_name}.md" + + # Copy SKILL.md with frontmatter stripped (body only) + awk 'BEGIN{f=0} /^---/{f++; if(f==2){found=1; next}} found{print}' "$skill_md" > "$dest_file" + + # If not starting with ---, just copy as-is (no frontmatter) + if ! head -1 "$skill_md" | grep -q '^---$'; then + cp "$skill_md" "$dest_file" + fi + + # Run global replacements on the instruction file + run_global_replacements "$dest_file" + + # Rewrite cross-skill references: ../orchestrator-framework/references/ → references/shared/ + sedi 's|\.\./orchestrator-framework/references/|references/shared/|g' "$dest_file" + + # If skill has references/ directory, copy it + if [ -d "$skill_dir/references" ]; then + mkdir -p "$OUT/instructions/references/${skill_name}" + cp -r "$skill_dir/references/." "$OUT/instructions/references/${skill_name}/" + + # Rewrite local references paths in instruction file: references/X.md → references/{name}/X.md + sedi "s|references/\([^/]\)|references/${skill_name}/\1|g" "$dest_file" + + # Run replacements on reference files too + find "$OUT/instructions/references/${skill_name}" -name "*.md" | while read -r rf; do + run_global_replacements "$rf" + sedi 's|\.\./orchestrator-framework/references/|references/shared/|g' "$rf" + done + fi + + # Add skill entry to agent fragment + python3 -c " +import json, sys + +skill_name = sys.argv[1] +agent_id = 'maister-' + skill_name + +# Title-case the skill name for description +nice_name = skill_name.replace('-', ' ').title() + +entry = { + 'description': 'Maister ' + nice_name + ' workflow', + 'mode': 'subagent', + 'prompt': '{file:.opencode/instructions/maister-' + skill_name + '.md}', + 'steps': 200 +} + +with open(sys.argv[2], 'r') as fh: + data = json.load(fh) +data[agent_id] = entry +with open(sys.argv[2], 'w') as fh: + json.dump(data, fh, indent=2) +" "$skill_name" "$AGENT_ENTRIES_FILE" + + echo " Processed skill: $skill_name" +done + +# ───────────────────────────────────────────────────────────── +# Finalize: Write agent-fragment.json +# ───────────────────────────────────────────────────────────── +echo "[Finalize] Writing agent-fragment.json..." +cp "$AGENT_ENTRIES_FILE" "$OUT/agent-fragment.json" +rm -f "$AGENT_ENTRIES_FILE" + +# ───────────────────────────────────────────────────────────── +# Summary +# ───────────────────────────────────────────────────────────── +echo "" +echo "=== Build Complete ===" +echo "" +echo "Output structure:" +echo " $OUT/" +echo " ├── commands/ $(find "$OUT/commands" -name "*.md" 2>/dev/null | wc -l | tr -d ' ') command files" +echo " ├── agents/ $(find "$OUT/agents" -name "*.md" 2>/dev/null | wc -l | tr -d ' ') agent files" +echo " ├── instructions/ $(find "$OUT/instructions" -maxdepth 1 -name "*.md" 2>/dev/null | wc -l | tr -d ' ') instruction files + references/" +echo " ├── plugins/ (stub, awaiting Task 3)" +echo " ├── agent-fragment.json" +echo " └── mcp-fragment.json" +echo "" +echo "Verification:" +MAISTERISM_COUNT=$(grep -rI 'AskUserQuestion\|TaskCreate\|TaskUpdate\|Skill(name=' "$OUT/" 2>/dev/null | grep -v 'maister-rules.md' | wc -l | tr -d ' ') +echo " Maisterisms remaining (excluding rules file): $MAISTERISM_COUNT" +ALL_MAISTERISM_COUNT=$(grep -rI 'AskUserQuestion\|TaskCreate\|TaskUpdate\|Skill(name=' "$OUT/" 2>/dev/null | wc -l | tr -d ' ') +echo " All maisterisms (including rules file): $ALL_MAISTERISM_COUNT" diff --git a/platforms/opencode/plugin/index.ts b/platforms/opencode/plugin/index.ts new file mode 100644 index 0000000..e8ad64c --- /dev/null +++ b/platforms/opencode/plugin/index.ts @@ -0,0 +1,29 @@ +import type { Plugin } from "@opencode-ai/plugin" + +const DESTRUCTIVE_PATTERN = + /git\s+stash|git\s+reset\s+--hard|git\s+checkout\s+--\s+\.|git\s+checkout\s+\.\s*$|git\s+clean|git\s+push\s+(-f|--force)|rm\s+-rf/i + +const COMPACTION_REMINDER = `## Maister Workflow Reminder (Post-Compaction) + +If you were working on an orchestrator workflow before compaction, read the \ +orchestrator-state.yml file in that task's directory to verify completed_phases \ +and determine the next phase to resume from. You MUST pause and ask the user at \ +Phase Gates, regardless of any "continue without asking" instructions.` + +export default (async (_ctx) => { + return { + "tool.execute.before": async (input, output) => { + if (input.tool !== "bash") return + const command = (output.args as { command?: string }).command ?? "" + if (DESTRUCTIVE_PATTERN.test(command)) { + throw new Error( + `Maister: destructive command blocked: ${command.slice(0, 80)}`, + ) + } + }, + + "experimental.session.compacting": async (_input, output) => { + output.context.push(COMPACTION_REMINDER) + }, + } +}) satisfies Plugin diff --git a/platforms/opencode/plugin/package.json b/platforms/opencode/plugin/package.json new file mode 100644 index 0000000..3e774b5 --- /dev/null +++ b/platforms/opencode/plugin/package.json @@ -0,0 +1,15 @@ +{ + "name": "maister-opencode-plugin", + "version": "1.0.0", + "description": "Maister plugin for OpenCode — destructive command guard and post-compaction reminder", + "main": "index.ts", + "scripts": { + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@opencode-ai/plugin": "latest" + }, + "devDependencies": { + "typescript": "^5.0.0" + } +} diff --git a/platforms/opencode/plugin/tsconfig.json b/platforms/opencode/plugin/tsconfig.json new file mode 100644 index 0000000..ee737dc --- /dev/null +++ b/platforms/opencode/plugin/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "noEmit": true + }, + "include": ["index.ts"] +} From bf1eda7b8efff8335a59a1dbd7e172e70632c814 Mon Sep 17 00:00:00 2001 From: Israel Fruchter Date: Sun, 26 Apr 2026 12:23:16 +0300 Subject: [PATCH 2/4] feat(opencode): add Makefile targets for opencode build/validate/clean --- Makefile | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2f885c1..7647304 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: build validate clean watch +.PHONY: build validate clean watch opencode opencode-build opencode-validate opencode-clean build: bash platforms/copilot-cli/build.sh @@ -23,3 +23,35 @@ clean: watch: fswatch -o plugins/maister/ | xargs -n1 -I{} make build + +opencode-build: + bash platforms/opencode/build.sh + +opencode-validate: + @test -d platforms/opencode/output || (echo "FAIL: Run make opencode-build first" && exit 1) + @echo "Checking no AskUserQuestion in output..." + @! grep -r 'AskUserQuestion' platforms/opencode/output/ 2>/dev/null || (echo "FAIL: AskUserQuestion found in output" && exit 1) + @echo "Checking no TaskCreate or TaskUpdate in output..." + @! grep -r 'TaskCreate\|TaskUpdate' platforms/opencode/output/ 2>/dev/null || (echo "FAIL: TaskCreate or TaskUpdate found in output" && exit 1) + @echo "Checking no Skill(name= in output..." + @! grep -r 'Skill(name=' platforms/opencode/output/ 2>/dev/null || (echo "FAIL: Skill(name= found in output" && exit 1) + @echo "Checking no maister: prefix in output..." + @! (grep -r 'maister:' platforms/opencode/output/ --include="*.md" --include="*.json" 2>/dev/null | grep -v '\[orchestrator-name\]' | grep -v 'skill: "maister-') || (echo "FAIL: maister: prefix found in output" && exit 1) + @echo "Checking no CLAUDE.md references in output..." + @! grep -ri 'CLAUDE\.md' platforms/opencode/output/ 2>/dev/null || (echo "FAIL: CLAUDE.md reference found in output" && exit 1) + @echo "Checking agent-fragment.json is valid JSON..." + @jq . platforms/opencode/output/agent-fragment.json > /dev/null || (echo "FAIL: agent-fragment.json is not valid JSON" && exit 1) + @echo "Checking mcp-fragment.json is valid JSON..." + @jq . platforms/opencode/output/mcp-fragment.json > /dev/null || (echo "FAIL: mcp-fragment.json is not valid JSON" && exit 1) + @echo "Checking minimum 8 commands present..." + @test $$(ls platforms/opencode/output/commands/ 2>/dev/null | wc -l) -ge 8 || (echo "FAIL: Minimum 8 commands not found" && exit 1) + @echo "Checking minimum 20 agents present..." + @test $$(ls platforms/opencode/output/agents/ 2>/dev/null | wc -l) -ge 20 || (echo "FAIL: Minimum 20 agents not found" && exit 1) + @echo "Checking minimum 14 instruction files present..." + @test $$(ls platforms/opencode/output/instructions/maister-*.md 2>/dev/null | wc -l) -ge 14 || (echo "FAIL: Minimum 14 instruction files not found" && exit 1) + @echo "All checks passed" + +opencode-clean: + rm -rf platforms/opencode/output/ + +opencode: opencode-build opencode-validate From cc875da02a6b2d89420c784794facf997f710144 Mon Sep 17 00:00:00 2001 From: Israel Fruchter Date: Sun, 26 Apr 2026 12:26:01 +0300 Subject: [PATCH 3/4] docs(opencode): add setup guide --- .sisyphus/evidence/task-5-no-claude-refs.txt | 1 + .sisyphus/evidence/task-5-path-check.txt | 120 ++++++++++++++++ docs/opencode-setup.md | 140 +++++++++++++++++++ 3 files changed, 261 insertions(+) create mode 100644 .sisyphus/evidence/task-5-no-claude-refs.txt create mode 100644 .sisyphus/evidence/task-5-path-check.txt create mode 100644 docs/opencode-setup.md diff --git a/.sisyphus/evidence/task-5-no-claude-refs.txt b/.sisyphus/evidence/task-5-no-claude-refs.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/.sisyphus/evidence/task-5-no-claude-refs.txt @@ -0,0 +1 @@ +0 diff --git a/.sisyphus/evidence/task-5-path-check.txt b/.sisyphus/evidence/task-5-path-check.txt new file mode 100644 index 0000000..bbf81fb --- /dev/null +++ b/.sisyphus/evidence/task-5-path-check.txt @@ -0,0 +1,120 @@ +platforms/opencode/output/: +agent-fragment.json +agents +commands +instructions +mcp-fragment.json +plugins + +platforms/opencode/output/agents: +bottleneck-analyzer.md +codebase-analysis-reporter.md +code-quality-pragmatist.md +code-reviewer.md +docs-operator.md +e2e-test-verifier.md +gap-analyzer.md +implementation-completeness-checker.md +implementation-planner.md +information-gatherer.md +production-readiness-checker.md +project-analyzer.md +reality-assessor.md +research-planner.md +research-synthesizer.md +solution-brainstormer.md +solution-designer.md +spec-auditor.md +specification-creator.md +task-classifier.md +task-group-implementer.md +test-suite-runner.md +ui-mockup-generator.md +user-docs-generator.md + +platforms/opencode/output/commands: +quick-dev.md +quick-plan.md +reviews-code.md +reviews-pragmatic.md +reviews-production-readiness.md +reviews-reality-check.md +reviews-spec-audit.md +work.md + +platforms/opencode/output/instructions: +maister-codebase-analyzer.md +maister-development.md +maister-docs-manager.md +maister-implementation-plan-executor.md +maister-implementation-verifier.md +maister-init.md +maister-migration.md +maister-performance.md +maister-product-design.md +maister-quick-bugfix.md +maister-research.md +maister-rules.md +maister-standards-discover.md +maister-standards-update.md +references + +platforms/opencode/output/instructions/references: +codebase-analyzer +docs-manager +init +migration +performance +product-design +research +shared +standards-discover + +platforms/opencode/output/instructions/references/codebase-analyzer: +code-analysis.md +combined.md +context-discovery.md +file-discovery.md +migration-target.md +pattern-mining.md + +platforms/opencode/output/instructions/references/docs-manager: +claude-md-template.md +index-md-template.md + +platforms/opencode/output/instructions/references/init: +architecture-template.md +roadmap-templates.md +tech-stack-template.md +vision-templates.md + +platforms/opencode/output/instructions/references/migration: +migration-strategies.md +migration-types.md + +platforms/opencode/output/instructions/references/performance: +performance-optimization-guide.md + +platforms/opencode/output/instructions/references/product-design: +characteristic-detection.md +interaction-patterns.md +visual-companion.md + +platforms/opencode/output/instructions/references/research: +brainstorming-techniques.md +design-techniques.md +research-methodologies.md + +platforms/opencode/output/instructions/references/shared: +orchestrator-creation-checklist.md +orchestrator-patterns.md + +platforms/opencode/output/instructions/references/standards-discover: +aggregation-strategy.md +code-pattern-prompt.md +config-analyzer-prompt.md +docs-extractor-prompt.md +external-analyzer-prompt.md + +platforms/opencode/output/plugins: +README.md diff --git a/docs/opencode-setup.md b/docs/opencode-setup.md new file mode 100644 index 0000000..6927039 --- /dev/null +++ b/docs/opencode-setup.md @@ -0,0 +1,140 @@ +# OpenCode Setup Guide + +This guide explains how to install and configure the Maister plugin for use with [OpenCode](https://github.com/anomalyco/opencode). + +## Prerequisites + +- **OpenCode installed**: See the [OpenCode repository](https://github.com/anomalyco/opencode) for installation instructions. +- **Node.js ≥ 18**: Required for running the plugin. +- **jq**: Installed for merging configuration fragments. +- **git, make, bash**: Standard development tools. + +## Quick Start + +```bash +git clone https://github.com/SkillPanel/Maister.git maister-src +cd maister-src +make opencode-build +# copy output to your project +cp -r platforms/opencode/output/commands/ /path/to/your-project/.opencode/commands/ +cp -r platforms/opencode/output/agents/ /path/to/your-project/.opencode/agents/ +cp -r platforms/opencode/output/instructions/ /path/to/your-project/.opencode/instructions/ +cp platforms/opencode/output/plugins/maister-plugin.ts /path/to/your-project/.opencode/plugins/maister-plugin.ts +``` + +## Build from Source + +The `make opencode-build` command runs the `platforms/opencode/build.sh` script, which transforms the core Maister plugin into OpenCode-compatible artifacts. It produces: + +- `platforms/opencode/output/commands/`: 8 command files (e.g., `work.md`, `quick-dev.md`). +- `platforms/opencode/output/agents/`: 24 agent definitions. +- `platforms/opencode/output/instructions/`: 14 instruction files (workflows). +- `platforms/opencode/output/agent-fragment.json`: Configuration fragment for agents. +- `platforms/opencode/output/mcp-fragment.json`: Configuration fragment for MCP servers. + +## Installation (Per-Project) + +To install Maister in your project, copy the built artifacts into your project's `.opencode/` directory: + +```bash +mkdir -p .opencode/commands .opencode/agents .opencode/instructions .opencode/plugins + +cp -r platforms/opencode/output/commands/* .opencode/commands/ +cp -r platforms/opencode/output/agents/* .opencode/agents/ +cp -r platforms/opencode/output/instructions/* .opencode/instructions/ +``` + +Note: References in `agent-fragment.json` resolve relative to the project root, so paths like `.opencode/agents/...` are used. + +## Configuration — Merging Fragments + +You need to merge the generated fragments into your `opencode.json` configuration file. + +### Merging Agent Fragment + +If `opencode.json` already exists: + +```bash +jq -s '.[0] * {"agent": (.[0].agent // {} + .[1])}' opencode.json platforms/opencode/output/agent-fragment.json > opencode.json.tmp && mv opencode.json.tmp opencode.json +``` + +If starting fresh: + +```bash +cp platforms/opencode/output/agent-fragment.json opencode.json +``` + +### Merging MCP Fragment + +Merge the MCP server configuration: + +```bash +jq -s '.[0] * {"mcpServers": (.[0].mcpServers // {} + .[1])}' opencode.json platforms/opencode/output/mcp-fragment.json > opencode.json.tmp && mv opencode.json.tmp opencode.json +``` + +### Adding Instructions + +Add the Maister instructions to the `"instructions"` array in `opencode.json`: + +```json +{ + "instructions": [ + ".opencode/instructions/maister-rules.md" + ] +} +``` + +## Plugin Setup + +OpenCode loads plugins as top-level `.opencode/plugins/*.ts` or `*.js` files. + +```bash +# In your project root: +mkdir -p .opencode/plugins +cp platforms/opencode/output/plugins/maister-plugin.ts .opencode/plugins/maister-plugin.ts + +# Install plugin dependency +cd .opencode +npm init -y 2>/dev/null || true +npm install @opencode-ai/plugin +cd .. +``` + +## Verify Installation + +Confirm the setup by checking the file structure and running the validation command: + +```bash +# From the Maister source directory: +make opencode-validate + +# In your target project: +ls .opencode/commands/ # Should show 8 .md files +ls .opencode/agents/ # Should show 24 .md files +``` + +## Available Commands + +Maister provides several entry points for different development tasks: + +| Command | Description | +|---------|-------------| +| `work` | Unified entry point — auto-classifies tasks and routes to appropriate workflow. | +| `quick-dev` | Implement task directly with standards awareness (no planning mode). | +| `quick-plan` | Enter planning mode with standards awareness. | +| `quick-bugfix` | Quick TDD-driven bug fix — write failing test, fix, verify. | +| `reviews-code` | Run automated code quality, security, and performance analysis. | +| `reviews-pragmatic` | Run pragmatic code review to detect over-engineering. | +| `reviews-production-readiness` | Verify production deployment readiness with comprehensive checks. | +| `reviews-reality-check` | Comprehensive reality assessment of completed work. | +| `reviews-spec-audit` | Independent specification audit to verify completeness and clarity. | + +## Differences from Claude Code Version + +The OpenCode version of Maister has some differences due to platform variations: + +- **Conversational Interaction**: There is no structured option selection (like `AskUserQuestion`). Instead, the AI asks questions conversationally. +- **Todo Management**: State tracking uses the `todo` tool rather than internal `TaskCreate`/`TaskUpdate` primitives. +- **Static Skills**: Skills are implemented as instruction files rather than dynamically invocable tools. +- **Step Limits**: The `steps` limit in agents may require more frequent manual intervention for very long orchestration workflows. +- **Command Syntax**: Use command names directly as slash commands (e.g., `/work` instead of `/maister:work`). From 7423b3f232bf67e8a8c486a571f7b2d3aa7ab47e Mon Sep 17 00:00:00 2001 From: Israel Fruchter Date: Sun, 26 Apr 2026 17:38:57 +0300 Subject: [PATCH 4/4] feat(opencode): add install script and fix build bugs - Add install.sh for single-command Maister installation into OpenCode projects - Fix agent filenames missing 'maister-' prefix in build output - Fix agent prompt paths referencing wrong filenames in agent-fragment.json - Fix Phase 2 to clean stale unprefixed agent files before rebuild - Wrap agent-fragment.json under 'agent' key for OpenCode config format --- platforms/opencode/build.sh | 6 ++- platforms/opencode/install.sh | 84 +++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 2 deletions(-) create mode 100755 platforms/opencode/install.sh diff --git a/platforms/opencode/build.sh b/platforms/opencode/build.sh index 862aa5f..f8e837c 100755 --- a/platforms/opencode/build.sh +++ b/platforms/opencode/build.sh @@ -36,6 +36,8 @@ rm -rf "$OUT/.claude-plugin" rm -rf "$OUT/hooks" rm -rf "$OUT/skills" rm -f "$OUT/.mcp.json" +rm -rf "$OUT/agents" +mkdir -p "$OUT/agents" # ───────────────────────────────────────────────────────────── # Phase 3: Transform commands @@ -120,7 +122,7 @@ for src_agent in "$CORE/agents/"*.md; do filename="$(basename "$src_agent")" agent_slug="${filename%.md}" agent_id="maister-${agent_slug}" - dest="$OUT/agents/${filename}" + dest="$OUT/agents/maister-${filename}" # Parse YAML frontmatter fields name_val="$(awk '/^---/{f++; next} f==1{print}' "$src_agent" | grep '^name:' | sed 's/^name:[[:space:]]*//')" @@ -146,7 +148,7 @@ slug = sys.argv[5] entry = { 'description': desc, 'mode': 'subagent', - 'prompt': '{file:.opencode/agents/' + slug + '.md}', + 'prompt': '{file:.opencode/agents/maister-' + slug + '.md}', 'color': color } diff --git a/platforms/opencode/install.sh b/platforms/opencode/install.sh new file mode 100755 index 0000000..8348c7d --- /dev/null +++ b/platforms/opencode/install.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +# install.sh — Install Maister into an OpenCode project +# Usage: bash platforms/opencode/install.sh /path/to/your-project + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +MAISTER_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +# ── Args ──────────────────────────────────────────────────────────────────── +TARGET="${1:-}" +if [[ -z "$TARGET" ]]; then + echo "Usage: $0 /path/to/your-project" + exit 1 +fi + +if [[ ! -d "$TARGET" ]]; then + echo "Error: target directory '$TARGET' does not exist." + exit 1 +fi + +# ── Build artifacts if output is stale/missing ─────────────────────────────── +OUT="$SCRIPT_DIR/output" +if [[ ! -d "$OUT/commands" ]] || [[ ! -d "$OUT/agents" ]]; then + echo "==> Building Maister artifacts..." + bash "$SCRIPT_DIR/build.sh" +fi + +echo "==> Installing Maister into $TARGET" + +# ── 1. Copy commands / agents / instructions ───────────────────────────────── +mkdir -p "$TARGET/.opencode/commands" \ + "$TARGET/.opencode/agents" \ + "$TARGET/.opencode/instructions" \ + "$TARGET/.opencode/plugins" + +cp -r "$OUT/commands/." "$TARGET/.opencode/commands/" +rm -rf "$TARGET/.opencode/agents" && mkdir -p "$TARGET/.opencode/agents" +cp -r "$OUT/agents/." "$TARGET/.opencode/agents/" +cp -r "$OUT/instructions/." "$TARGET/.opencode/instructions/" +echo " ✔ commands / agents / instructions copied" + +# ── 2. Copy plugin (source file, no build step needed) ─────────────────────── +cp "$SCRIPT_DIR/plugin/index.ts" "$TARGET/.opencode/plugins/maister-plugin.ts" +echo " ✔ plugin copied" + +# ── 3. Merge into opencode.json ─────────────────────────────────────────────── +OPENCODE_JSON="$TARGET/opencode.json" + +TMPJSON="$(mktemp)" + +# Wrap agent-fragment.json under {"agent": {...}} and add instructions +jq '{ agent: ., instructions: [".opencode/instructions/maister-rules.md"] }' \ + "$OUT/agent-fragment.json" > "$TMPJSON" +if [[ -s "$TMPJSON" ]]; then + cp "$TMPJSON" "$OPENCODE_JSON" + echo " ✔ opencode.json created (agents + instructions)" +else + echo "Error: failed to build opencode.json" >&2; rm -f "$TMPJSON"; exit 1 +fi +rm -f "$TMPJSON" + +# ── 4. Install plugin npm dependency ───────────────────────────────────────── +PLUGIN_DIR="$TARGET/.opencode" +if ! node -e "require('@opencode-ai/plugin')" 2>/dev/null; then + echo "==> Installing @opencode-ai/plugin..." + (cd "$PLUGIN_DIR" && npm init -y 2>/dev/null || true && npm install @opencode-ai/plugin --silent) + echo " ✔ @opencode-ai/plugin installed" +else + echo " ✔ @opencode-ai/plugin already available" +fi + +# ── 5. Summary ──────────────────────────────────────────────────────────────── +echo "" +echo "✅ Maister installed successfully!" +echo "" +echo " Commands: $(ls "$TARGET/.opencode/commands/" | wc -l) files" +echo " Agents: $(ls "$TARGET/.opencode/agents/" | wc -l) files" +echo " Instructions: $(ls "$TARGET/.opencode/instructions/" | wc -l) files" +echo " Plugin: $TARGET/.opencode/plugins/maister-plugin.ts" +echo " Config: $TARGET/opencode.json" +echo "" +echo "Next: cd $TARGET && opencode" +echo "Then try: /work Add a hello world function"