diff --git a/adapters/claude-code/adapter.sh b/adapters/claude-code/adapter.sh index 38e7ff9..3a67e23 100755 --- a/adapters/claude-code/adapter.sh +++ b/adapters/claude-code/adapter.sh @@ -7,7 +7,7 @@ # Claude Code expects in the user's vault. # ============================================================================= -FRAMEWORK="claude-code" +CC_FRAMEWORK="claude-code" # Capability → CC tools mapping. Each capability expands into one or more # Claude Code tool names. The expansion is order-preserving. @@ -146,7 +146,7 @@ adapter_translate_hooks() { while IFS= read -r yaml; do [[ -z "$yaml" ]] && continue - should_include "$yaml" "$FRAMEWORK" || continue + should_include "$yaml" "$CC_FRAMEWORK" || continue local meta; meta="$(parse_hook_yaml "$yaml")" local name; name="$(echo "$meta" | grep '^name=' | head -1 | cut -d= -f2- || true)" local script; script="$(echo "$meta" | grep '^script=' | head -1 | cut -d= -f2- || true)" @@ -205,7 +205,7 @@ adapter_translate_agents() { while IFS= read -r agent; do [[ -z "$agent" ]] && continue - should_include "$agent" "$FRAMEWORK" || continue + should_include "$agent" "$CC_FRAMEWORK" || continue local name; name="$(parse_frontmatter "$agent" name)" local model; model="$(parse_frontmatter "$agent" model)" local caps; caps="$(parse_capabilities "$agent")" @@ -250,7 +250,7 @@ adapter_translate_skills() { [[ -d "$src" ]] || return 0 for skill_dir in "$src"/*/; do [[ -f "${skill_dir}SKILL.md" ]] || continue - should_include "${skill_dir}SKILL.md" "$FRAMEWORK" || continue + should_include "${skill_dir}SKILL.md" "$CC_FRAMEWORK" || continue local name; name="$(basename "$skill_dir")" local out="$dst/.claude/skills/$name" mkdir -p "$out" @@ -267,7 +267,7 @@ adapter_translate_references() { mkdir -p "$out" for f in "$src"/*.md; do [[ -f "$f" ]] || continue - should_include "$f" "$FRAMEWORK" || continue + should_include "$f" "$CC_FRAMEWORK" || continue cp "$f" "$out/" done } diff --git a/adapters/lib.sh b/adapters/lib.sh index 06c5ed2..ebe7f73 100755 --- a/adapters/lib.sh +++ b/adapters/lib.sh @@ -15,10 +15,24 @@ CAPABILITY_VOCAB="read write edit bash webfetch websearch notebook task todo" # The closed set of hook event names the source .hook.yaml may declare. EVENT_VOCAB="before-tool-use after-tool-use on-notification on-session-start on-prompt-submit" +# ── Path rewriting ─────────────────────────────────────────────────────────── + +# rewrite_framework_paths +# Rewrites source-canonical framework path references in a text file in-place. +# Source files use .claude/ and CLAUDE.md as the canonical form. +# Call this after copying any text file from source to dist for non-CC frameworks. +# No-op when target_fw_dir is "claude" (i.e. building for claude-code, whose +# canonical output paths already match the source form — no rewrite needed). +rewrite_framework_paths() { + local file="$1" tgt_dir="$2" tgt_dispatcher="$3" + [[ "$tgt_dir" == "claude" && "$tgt_dispatcher" == "CLAUDE.md" ]] && return 0 + sed -i \ + -e "s|\.claude/|.${tgt_dir}/|g" \ + -e "s|CLAUDE\.md|${tgt_dispatcher}|g" \ + "$file" +} + # ── Parsing helpers ────────────────────────────────────────────────────────── -# Defined in subsequent tasks: parse_frontmatter, parse_capabilities, -# parse_hook_yaml, agent_body, should_include, enumerate_agents, -# enumerate_hooks, json_object. # parse_frontmatter # Echoes the value of a top-level YAML key from the file's --- ... --- block. diff --git a/adapters/opencode/adapter.sh b/adapters/opencode/adapter.sh index 8aab33f..8ae77db 100755 --- a/adapters/opencode/adapter.sh +++ b/adapters/opencode/adapter.sh @@ -8,6 +8,8 @@ # ============================================================================= OC_FRAMEWORK="opencode" +OC_FW_DIR="opencode" +OC_DISPATCHER="AGENTS.md" # Capability → opencode permission key. Returns the permission key to set to # "allow" for each capability, or empty string for capabilities that have no @@ -68,16 +70,17 @@ oc_model_to_provider() { # adapter_translate_dispatcher # Copies the source DISPATCHER.md to dest_dir/AGENTS.md (opencode's vault-root -# dispatcher filename). No content translation. +# dispatcher filename). Rewrites .claude/ and CLAUDE.md references in the body. adapter_translate_dispatcher() { local src="$1" dst="$2" [[ -f "$src" ]] || return 0 mkdir -p "$dst" cp "$src" "$dst/AGENTS.md" + rewrite_framework_paths "$dst/AGENTS.md" "$OC_FW_DIR" "$OC_DISPATCHER" } # adapter_translate_references -# Verbatim copy of *.md into dest_root/.opencode/references/. +# Copies *.md into dest_root/.opencode/references/, rewriting framework paths. adapter_translate_references() { local src="$1" dst="$2" [[ -d "$src" ]] || return 0 @@ -87,11 +90,13 @@ adapter_translate_references() { [[ -f "$f" ]] || continue should_include "$f" "$OC_FRAMEWORK" || continue cp "$f" "$out/" + rewrite_framework_paths "$out/$(basename "$f")" "$OC_FW_DIR" "$OC_DISPATCHER" done } # adapter_translate_skills -# Copies each skill directory's SKILL.md into dest_root/.opencode/skills//. +# Copies each skill directory's SKILL.md into dest_root/.opencode/skills//, +# rewriting framework paths in the body. adapter_translate_skills() { local src="$1" dst="$2" [[ -d "$src" ]] || return 0 @@ -102,40 +107,10 @@ adapter_translate_skills() { local out="$dst/.opencode/skills/$name" mkdir -p "$out" cp "${skill_dir}SKILL.md" "$out/SKILL.md" + rewrite_framework_paths "$out/SKILL.md" "$OC_FW_DIR" "$OC_DISPATCHER" done } -# _oc_flatten_description -# Reads the description field from the source agent, collapsing any YAML -# folded continuation lines into a single line with whitespace normalised. -# Echoes the bare description text with no leading/trailing quotes. -_oc_flatten_description() { - local file="$1" - awk ' - /^---$/ { fm++; next } - fm == 1 && /^description:/ { - sub(/^description:[[:space:]]*/, "") - desc = $0 - in_desc = 1 - next - } - fm == 1 && in_desc && /^[[:space:]]/ { - sub(/^[[:space:]]+/, "") - desc = desc " " $0 - next - } - fm == 1 && in_desc { in_desc = 0 } - fm >= 2 { exit } - END { - # Collapse runs of whitespace - gsub(/[[:space:]]+/, " ", desc) - sub(/^[[:space:]]+/, "", desc) - sub(/[[:space:]]+$/, "", desc) - print desc - } - ' "$file" -} - # adapter_translate_agents # For each *.md in source_agents_dir, translate the capabilities frontmatter # into an opencode permission block, map the model, and write to @@ -153,7 +128,6 @@ adapter_translate_agents() { local model_raw; model_raw="$(parse_frontmatter "$agent" model)" local mode_raw; mode_raw="$(parse_frontmatter "$agent" mode)" local caps; caps="$(parse_capabilities "$agent")" - local desc; desc="$(_oc_flatten_description "$agent")" local model_out; model_out="$(oc_model_to_provider "$model_raw")" local mode_out="${mode_raw:-subagent}" @@ -174,7 +148,8 @@ adapter_translate_agents() { local out_file="$out_dir/$(basename "$agent")" { echo "---" - echo "description: \"$desc\"" + # Copy description block verbatim (may be folded YAML with continuation lines) + awk '/^---$/{n++; next} n==1 && /^description:/{print; in_desc=1; next} n==1 && in_desc && /^[[:space:]]/{print; next} n==1 && in_desc && !/^[[:space:]]/{in_desc=0} n>=2{exit}' "$agent" echo "mode: $mode_out" echo "model: $model_out" if [[ -z "$perms" ]]; then @@ -189,6 +164,7 @@ adapter_translate_agents() { echo "" agent_body "$agent" } > "$out_file" + rewrite_framework_paths "$out_file" "$OC_FW_DIR" "$OC_DISPATCHER" done < <(enumerate_agents "$src") } @@ -252,6 +228,7 @@ adapter_translate_hooks() { [[ -f "$src/$script" ]] || continue cp "$src/$script" "$hooks_out/$script" chmod +x "$hooks_out/$script" + rewrite_framework_paths "$hooks_out/$script" "$OC_FW_DIR" "$OC_DISPATCHER" have_any=1 done < <(enumerate_hooks "$src") diff --git a/scripts/launchme.sh b/scripts/launchme.sh index aefb10a..1c97a94 100755 --- a/scripts/launchme.sh +++ b/scripts/launchme.sh @@ -63,13 +63,17 @@ fi # ── Check for existing installation ────────────────────────────────────────── EXISTING=0 -[[ -d "$VAULT_DIR/.claude" ]] && EXISTING=1 -[[ -f "$VAULT_DIR/CLAUDE.md" ]] && EXISTING=1 +[[ -d "$VAULT_DIR/.claude" ]] && EXISTING=1 +[[ -f "$VAULT_DIR/CLAUDE.md" ]] && EXISTING=1 +[[ -d "$VAULT_DIR/.opencode" ]] && EXISTING=1 +[[ -f "$VAULT_DIR/AGENTS.md" ]] && EXISTING=1 if [[ $EXISTING -eq 1 ]]; then warn "An existing installation was detected:" - [[ -d "$VAULT_DIR/.claude" ]] && warn " .claude/ directory exists" + [[ -d "$VAULT_DIR/.claude" ]] && warn " .claude/ directory exists" [[ -f "$VAULT_DIR/CLAUDE.md" ]] && warn " CLAUDE.md exists" + [[ -d "$VAULT_DIR/.opencode" ]] && warn " .opencode/ directory exists" + [[ -f "$VAULT_DIR/AGENTS.md" ]] && warn " AGENTS.md exists" echo "" echo -e " ${BOLD}The installer will overwrite core files. Custom agents are never deleted.${NC}" echo -e " ${DIM}Your vault notes are never touched.${NC}" @@ -114,6 +118,7 @@ case "$FRAMEWORK" in die "Unknown framework: $FRAMEWORK (install layout not defined)" ;; esac +FRAMEWORK_VAULT_DIR="$VAULT_COMPONENTS_DIR" # ── Migrate legacy manifests (if any) ──────────────────────────────────────── manifest_migrate @@ -207,25 +212,36 @@ echo "" echo -e "${GREEN}${BOLD} Setup complete!${NC}" echo "" echo -e " ${VAULT_DIR}/" -echo -e " ├── .claude/" +FW_DIR_NAME="$(basename "$VAULT_COMPONENTS_DIR")" +DISPATCHER_NAME="$(basename "$DISPATCHER_DST")" +echo -e " ├── .${FW_DIR_NAME}/" echo -e " │ ├── agents/ ${DIM}← agents${NC}" echo -e " │ ├── skills/ ${DIM}← skills${NC}" echo -e " │ ├── hooks/ ${DIM}← hooks${NC}" -echo -e " │ ├── settings.json ${DIM}← hooks configuration${NC}" +if [[ $HAS_PLUGINS -eq 1 ]]; then + echo -e " │ ├── plugins/ ${DIM}← hook plugins${NC}" +else + echo -e " │ ├── settings.json ${DIM}← hooks configuration${NC}" +fi echo -e " │ └── references/ ${DIM}← shared docs${NC}" echo -e " ├── Meta/" echo -e " │ └── scripts/ ${DIM}← ${ORCH_COUNT:-0} orchestra scripts${NC}" -echo -e " ├── CLAUDE.md ${DIM}← project instructions${NC}" +echo -e " └── ${DISPATCHER_NAME} ${DIM}← project instructions${NC}" if [[ "$MCP_ANSWER" =~ ^[Yy]$ ]]; then echo -e " └── .mcp.json ${DIM}← Gmail + Calendar${NC}" fi + if [[ $DEP_COUNT -gt 0 ]]; then echo "" - warn "$DEP_COUNT file(s) were deprecated (moved to .claude/deprecated/)" + warn "$DEP_COUNT file(s) were deprecated (moved to .${FW_DIR_NAME}/deprecated/)" fi echo "" echo -e " ${BOLD}Next steps:${NC}" -echo -e " 1. Open Claude Code in your vault folder" +if [[ "$FRAMEWORK" == "opencode" ]]; then + echo -e " 1. Open opencode in your vault folder" +else + echo -e " 1. Open Claude Code in your vault folder" +fi echo -e " 2. Say: ${BOLD}\"Initialize my vault\"${NC}" echo -e " 3. The Architect will guide you through setup" echo "" diff --git a/scripts/lib.sh b/scripts/lib.sh index 2f3cd38..a3dc2da 100755 --- a/scripts/lib.sh +++ b/scripts/lib.sh @@ -158,12 +158,16 @@ merge_marked_file() { } # ── Manifest helpers ────────────────────────────────────────────────────────── -# Single unified manifest at $VAULT_DIR/.claude/.mbifc-manifest +# Single unified manifest at $VAULT_DIR/.{framework}/.mbifc-manifest # Format: INI-style with [section] headers, one entry per line. +# +# FRAMEWORK_VAULT_DIR must be set before calling these functions (e.g. +# $VAULT_DIR/.claude for claude-code, $VAULT_DIR/.opencode for opencode). +# Defaults to $VAULT_DIR/.claude for backwards compatibility. manifest_read() { local section="$1" - local file="$VAULT_DIR/.claude/.mbifc-manifest" + local file="${FRAMEWORK_VAULT_DIR:-$VAULT_DIR/.claude}/.mbifc-manifest" [[ -f "$file" ]] || return 0 awk -v sec="[$section]" ' $0 == sec { found=1; next } @@ -175,7 +179,7 @@ manifest_read() { manifest_write() { local section="$1"; shift local entries=("$@") - local file="$VAULT_DIR/.claude/.mbifc-manifest" + local file="${FRAMEWORK_VAULT_DIR:-$VAULT_DIR/.claude}/.mbifc-manifest" mkdir -p "$(dirname "$file")" local tmpfile; tmpfile="$(mktemp)" local in_section=0 section_written=0 @@ -211,7 +215,7 @@ manifest_write() { manifest_remove() { local section="$1" name="$2" - local file="$VAULT_DIR/.claude/.mbifc-manifest" + local file="${FRAMEWORK_VAULT_DIR:-$VAULT_DIR/.claude}/.mbifc-manifest" [[ -f "$file" ]] || return 0 local tmpfile; tmpfile="$(mktemp)" local in_section=0 @@ -227,8 +231,9 @@ manifest_remove() { # Converts legacy per-directory .core-manifest files to the unified format. # Runs only when legacy files are present; idempotent thereafter. manifest_migrate() { - local agents_mf="$VAULT_DIR/.claude/agents/.core-manifest" - local refs_mf="$VAULT_DIR/.claude/references/.core-manifest" + local _fw_dir="${FRAMEWORK_VAULT_DIR:-$VAULT_DIR/.claude}" + local agents_mf="$_fw_dir/agents/.core-manifest" + local refs_mf="$_fw_dir/references/.core-manifest" [[ -f "$agents_mf" ]] || [[ -f "$refs_mf" ]] || return 0 warn "Migrating legacy manifests to unified .mbifc-manifest..." @@ -247,14 +252,14 @@ manifest_migrate() { rm "$refs_mf" fi - success "Manifest migrated to .claude/.mbifc-manifest" + success "Manifest migrated to $(basename "$_fw_dir")/.mbifc-manifest" } # ── Deprecation ─────────────────────────────────────────────────────────────── # deprecate_removed
# Moves files listed in the manifest for
that no longer exist in -# to $VAULT_DIR/.claude/deprecated/, prepending a DEPRECATED header. +# to $FRAMEWORK_VAULT_DIR/deprecated/, prepending a DEPRECATED header. # Prints the count of deprecated files to stdout. deprecate_removed() { local section="$1" src_dir="$2" dst_dir="$3" @@ -268,7 +273,7 @@ deprecate_removed() { [[ "$name" == *"-DEPRECATED"* ]] && continue # already deprecated local dep_name="${name%.md}-DEPRECATED.md" - local dep_dir="$VAULT_DIR/.claude/deprecated" + local dep_dir="${FRAMEWORK_VAULT_DIR:-$VAULT_DIR/.claude}/deprecated" mkdir -p "$dep_dir" [[ -f "$dep_dir/$dep_name" ]] && continue # already done in a prior run @@ -279,7 +284,7 @@ deprecate_removed() { mv "$dep_dir/$dep_name.tmp" "$dep_dir/$dep_name" manifest_remove "$section" "$name" - warn "Deprecated: $name → .claude/deprecated/$dep_name" + warn "Deprecated: $name → $(basename "${FRAMEWORK_VAULT_DIR:-$VAULT_DIR/.claude}")/deprecated/$dep_name" count=$((count + 1)) done < <(manifest_read "$section") diff --git a/scripts/updateme.sh b/scripts/updateme.sh index 390bd82..498dcb0 100755 --- a/scripts/updateme.sh +++ b/scripts/updateme.sh @@ -46,8 +46,12 @@ esac || die "No agents/ found in $VAULT_DIR for framework '$FRAMEWORK' — run launchme.sh first" # ── Confirm ─────────────────────────────────────────────────────────────────── -echo -e "${BOLD}This will update core agents, skills, references, hooks, and CLAUDE.md.${NC}" -echo -e " ${DIM}Custom agents in .claude/agents/ are never overwritten or deleted.${NC}" +case "$FRAMEWORK" in + opencode) _DISP_NAME="AGENTS.md"; _FW_DIR_NAME="opencode" ;; + *) _DISP_NAME="CLAUDE.md"; _FW_DIR_NAME="claude" ;; +esac +echo -e "${BOLD}This will update core agents, skills, references, hooks, and ${_DISP_NAME}.${NC}" +echo -e " ${DIM}Custom agents in .${_FW_DIR_NAME}/agents/ are never overwritten or deleted.${NC}" echo -e " ${DIM}Custom content between MBIFC markers in references is preserved.${NC}" echo -e " ${DIM}Your vault notes are never touched.${NC}" echo "" @@ -89,6 +93,7 @@ case "$FRAMEWORK" in die "Unknown framework: $FRAMEWORK (install layout not defined)" ;; esac +FRAMEWORK_VAULT_DIR="$VAULT_COMPONENTS_DIR" # ── Migrate legacy manifests (if any) ──────────────────────────────────────── manifest_migrate diff --git a/tests/adapters/lib.test.sh b/tests/adapters/lib.test.sh index 1cbc15c..4aa5c4d 100755 --- a/tests/adapters/lib.test.sh +++ b/tests/adapters/lib.test.sh @@ -205,3 +205,36 @@ test_enumerate_hooks() { rm -rf "$dir" [[ "$count" == "2" ]] || { echo "expected 2, got $count"; return 1; } } + +test_rewrite_framework_paths_opencode() { + local file; file="$(mktemp)" + printf 'See .claude/agents/ and .claude/references/foo.md\nAlso CLAUDE.md here.\n' > "$file" + rewrite_framework_paths "$file" "opencode" "AGENTS.md" + local result; result="$(cat "$file")" + rm "$file" + [[ "$result" == *".opencode/agents/"* ]] || { echo "agents/ not rewritten: $result"; return 1; } + [[ "$result" == *".opencode/references/"* ]] || { echo "references/ not rewritten: $result"; return 1; } + [[ "$result" == *"AGENTS.md"* ]] || { echo "AGENTS.md not present: $result"; return 1; } + [[ "$result" != *".claude/"* ]] || { echo ".claude/ still present: $result"; return 1; } + [[ "$result" != *"CLAUDE.md"* ]] || { echo "CLAUDE.md still present: $result"; return 1; } +} + +test_rewrite_framework_paths_noop_for_claude_code() { + local file; file="$(mktemp)" + local content='.claude/agents/ and CLAUDE.md' + printf '%s\n' "$content" > "$file" + rewrite_framework_paths "$file" "claude" "CLAUDE.md" + local result; result="$(cat "$file")" + rm "$file" + [[ "$result" == "$content" ]] || { echo "content was changed unexpectedly: $result"; return 1; } +} + +test_rewrite_framework_paths_preserves_product_name() { + local file; file="$(mktemp)" + printf 'Claude Code auto-loads agents from .claude/agents/\n' > "$file" + rewrite_framework_paths "$file" "opencode" "AGENTS.md" + local result; result="$(cat "$file")" + rm "$file" + [[ "$result" == *"Claude Code auto-loads"* ]] || { echo "product name was altered: $result"; return 1; } + [[ "$result" == *".opencode/agents/"* ]] || { echo "path not rewritten: $result"; return 1; } +} diff --git a/tests/adapters/opencode/adapter.test.sh b/tests/adapters/opencode/adapter.test.sh index 68c607b..ea8c766 100755 --- a/tests/adapters/opencode/adapter.test.sh +++ b/tests/adapters/opencode/adapter.test.sh @@ -9,13 +9,17 @@ test_oc_translate_dispatcher_renames_to_agents_md() { local dst; dst="$(mktemp -d)" cat > "$src/DISPATCHER.md" <<'EOF' # Dispatcher -Some content +See .claude/agents/ for agents. Consult CLAUDE.md for rules. EOF adapter_translate_dispatcher "$src/DISPATCHER.md" "$dst" local result=0 - [[ -f "$dst/AGENTS.md" ]] || { echo "AGENTS.md not created"; result=1; } + [[ -f "$dst/AGENTS.md" ]] || { echo "AGENTS.md not created"; result=1; } [[ ! -f "$dst/CLAUDE.md" ]] || { echo "CLAUDE.md should not exist"; result=1; } - [[ "$(cat "$dst/AGENTS.md")" == "$(cat "$src/DISPATCHER.md")" ]] || { echo "content mismatch"; result=1; } + local content; content="$(cat "$dst/AGENTS.md")" + [[ "$content" == *".opencode/agents/"* ]] || { echo ".opencode/agents/ not found: $content"; result=1; } + [[ "$content" == *"AGENTS.md"* ]] || { echo "AGENTS.md ref not rewritten: $content"; result=1; } + [[ "$content" != *".claude/"* ]] || { echo ".claude/ still present: $content"; result=1; } + [[ "$content" != *"CLAUDE.md"* ]] || { echo "CLAUDE.md still present: $content"; result=1; } rm -rf "$src" "$dst" return $result } @@ -34,6 +38,21 @@ test_oc_translate_references_copies_md_files() { return $result } +test_oc_translate_references_rewrites_paths() { + local src; src="$(mktemp -d)" + local dst; dst="$(mktemp -d)" + mkdir -p "$src/references" + printf 'See .claude/agents/ and CLAUDE.md for details.\n' > "$src/references/guide.md" + adapter_translate_references "$src/references" "$dst" + local content; content="$(cat "$dst/.opencode/references/guide.md")" + local result=0 + [[ "$content" == *".opencode/agents/"* ]] || { echo ".opencode/agents/ not found: $content"; result=1; } + [[ "$content" == *"AGENTS.md"* ]] || { echo "AGENTS.md not found: $content"; result=1; } + [[ "$content" != *".claude/"* ]] || { echo ".claude/ still present: $content"; result=1; } + rm -rf "$src" "$dst" + return $result +} + test_oc_translate_skills_copies_skill_md() { local src; src="$(mktemp -d)" local dst; dst="$(mktemp -d)" @@ -60,6 +79,27 @@ SKILLEOF return $result } +test_oc_translate_skills_rewrites_paths() { + local src; src="$(mktemp -d)" + local dst; dst="$(mktemp -d)" + mkdir -p "$src/skills/create-agent" + cat > "$src/skills/create-agent/SKILL.md" <<'SKILLEOF' +--- +name: create-agent +description: Create a new agent +--- +Save to .claude/agents/ and update CLAUDE.md. +SKILLEOF + adapter_translate_skills "$src/skills" "$dst" + local content; content="$(cat "$dst/.opencode/skills/create-agent/SKILL.md")" + local result=0 + [[ "$content" == *".opencode/agents/"* ]] || { echo ".opencode/agents/ not found: $content"; result=1; } + [[ "$content" == *"AGENTS.md"* ]] || { echo "AGENTS.md not found: $content"; result=1; } + [[ "$content" != *".claude/"* ]] || { echo ".claude/ still present: $content"; result=1; } + rm -rf "$src" "$dst" + return $result +} + test_oc_translate_skills_honors_exclude() { local src; src="$(mktemp -d)" local dst; dst="$(mktemp -d)" @@ -97,7 +137,7 @@ EOF local out="$dst/.opencode/agents/scribe.md" local result=0 [[ -f "$out" ]] || { echo "agent file missing"; result=1; } - grep -q '^description: "Test scribe"' "$out" || { echo "description missing or wrong quoting"; cat "$out"; result=1; } + grep -q '^description: Test scribe' "$out" || { echo "description missing or wrong format"; cat "$out"; result=1; } grep -q '^mode: subagent' "$out" || { echo "mode missing"; result=1; } grep -q '^model: anthropic/claude-sonnet-4-5' "$out" || { echo "model not mapped"; result=1; } grep -q '^permission:' "$out" || { echo "permission block missing"; result=1; } @@ -109,6 +149,31 @@ EOF return $result } +test_oc_translate_agents_rewrites_body_paths() { + local src; src="$(mktemp -d)" + local dst; dst="$(mktemp -d)" + mkdir -p "$src/agents" + cat > "$src/agents/scribe.md" <<'EOF' +--- +name: scribe +description: Test scribe +model: sonnet +capabilities: [read, write] +--- + +See .claude/references/agents.md and CLAUDE.md for context. +EOF + adapter_translate_agents "$src/agents" "$dst" + local out="$dst/.opencode/agents/scribe.md" + local result=0 + local content; content="$(cat "$out")" + [[ "$content" == *".opencode/references/agents.md"* ]] || { echo ".opencode/references/ not found: $content"; result=1; } + [[ "$content" == *"AGENTS.md"* ]] || { echo "AGENTS.md not found: $content"; result=1; } + [[ "$content" != *".claude/"* ]] || { echo ".claude/ still present: $content"; result=1; } + rm -rf "$src" "$dst" + return $result +} + test_oc_translate_agents_bash_capability() { local src; src="$(mktemp -d)" local dst; dst="$(mktemp -d)" diff --git a/tests/regression/snapshot/.claude/.mbifc-manifest b/tests/regression/snapshot/.claude/.mbifc-manifest deleted file mode 100755 index 0c8fe43..0000000 --- a/tests/regression/snapshot/.claude/.mbifc-manifest +++ /dev/null @@ -1,36 +0,0 @@ - - - - -[agents] -[hooks] -[references] -[skills] -agent-orchestration.md -agent-template.md -agents-registry.md -agents.md -architect.md -connector.md -create-agent -deadline-radar -deep-clean -defrag -email-triage -inbox-triage -librarian.md -manage-agent -meeting-prep -notify.sh -onboarding -postman.md -protect-system-files.sh -scribe.md -seeker.md -sorter.md -tag-garden -transcribe -transcriber.md -validate-frontmatter.sh -vault-audit -weekly-agenda diff --git a/tests/regression/snapshot/.claude/agents/architect.md b/tests/regression/snapshot/.claude/agents/architect.md index da3a3db..e071a30 100755 --- a/tests/regression/snapshot/.claude/agents/architect.md +++ b/tests/regression/snapshot/.claude/agents/architect.md @@ -17,7 +17,7 @@ description: > JA: "新しいプロジェクト". Also trigger when a new topic/project/area emerges that needs a home, or when another agent reports a missing structure. -tools: Read, Write, Edit, Bash, Glob, Grep +tools: Read, Glob, Grep, Write, Edit, Bash model: opus --- diff --git a/tests/regression/snapshot/.claude/agents/connector.md b/tests/regression/snapshot/.claude/agents/connector.md index 0e56920..c63ba9d 100755 --- a/tests/regression/snapshot/.claude/agents/connector.md +++ b/tests/regression/snapshot/.claude/agents/connector.md @@ -14,7 +14,7 @@ description: > "verbinde die Notizen", "finde Verbindungen", "Graphanalyse", "fehlende Links", "conecta as notas", "encontra conexões", "análise do grafo", "links em falta", or after a large batch of notes has been filed and needs cross-linking. -tools: Read, Edit, Glob, Grep +tools: Read, Glob, Grep, Edit model: sonnet --- diff --git a/tests/regression/snapshot/.claude/agents/librarian.md b/tests/regression/snapshot/.claude/agents/librarian.md index 081ed4f..4b57cde 100755 --- a/tests/regression/snapshot/.claude/agents/librarian.md +++ b/tests/regression/snapshot/.claude/agents/librarian.md @@ -15,7 +15,7 @@ description: > "wöchentliche Überprüfung", "Vault prüfen", "Wartung", "Vault aufräumen", "revisão semanal", "verifica o vault", "manutenção", "limpeza do vault", or when the user suspects broken links, misplaced files, or structural problems. -tools: Read, Write, Edit, Bash, Glob, Grep +tools: Read, Glob, Grep, Write, Edit, Bash model: opus --- diff --git a/tests/regression/snapshot/.claude/agents/postman.md b/tests/regression/snapshot/.claude/agents/postman.md index ec9f12a..2e10bb4 100755 --- a/tests/regression/snapshot/.claude/agents/postman.md +++ b/tests/regression/snapshot/.claude/agents/postman.md @@ -25,7 +25,7 @@ description: > PT: "verificar meus emails", "o que tem na caixa de entrada", "importar eventos", "criar evento", "o que tem no calendário", "triagem de email", "preparar a reunião", "agenda semanal", "rascunho de resposta". -tools: Read, Write, Edit, Bash, Glob, Grep +tools: Read, Glob, Grep, Write, Edit, Bash model: sonnet --- diff --git a/tests/regression/snapshot/.claude/agents/scribe.md b/tests/regression/snapshot/.claude/agents/scribe.md index 87cd033..5f3e56a 100755 --- a/tests/regression/snapshot/.claude/agents/scribe.md +++ b/tests/regression/snapshot/.claude/agents/scribe.md @@ -12,7 +12,7 @@ description: > "salva isso", "nota rápida", "escreve isso", "lembra-me que", or when the user pastes messy, unformatted text, speech-to-text output, or a chain of related thoughts that need to be turned into proper notes. -tools: Read, Write, Edit, Glob, Grep +tools: Read, Glob, Grep, Write, Edit model: sonnet --- diff --git a/tests/regression/snapshot/.claude/agents/sorter.md b/tests/regression/snapshot/.claude/agents/sorter.md index 3ade1df..ee01240 100755 --- a/tests/regression/snapshot/.claude/agents/sorter.md +++ b/tests/regression/snapshot/.claude/agents/sorter.md @@ -10,7 +10,7 @@ description: > "sortiere den Eingang", "Notizen sortieren", "organiza a caixa de entrada", "triagem", or when the Inbox has accumulated notes that need filing. -tools: Read, Write, Edit, Bash, Glob, Grep +tools: Read, Glob, Grep, Write, Edit, Bash model: sonnet --- diff --git a/tests/regression/snapshot/.claude/agents/transcriber.md b/tests/regression/snapshot/.claude/agents/transcriber.md index bb18fa5..6ac627a 100755 --- a/tests/regression/snapshot/.claude/agents/transcriber.md +++ b/tests/regression/snapshot/.claude/agents/transcriber.md @@ -17,7 +17,7 @@ description: > PT: "transcrever", "notas de reunião", "resumo do podcast", "notas de aula", "diário de voz", "resumo da chamada". Also triggers when the user uploads an audio file (mp3, m4a, wav) or pastes a raw transcript. -tools: Read, Write, Glob, Grep +tools: Read, Glob, Grep, Write model: sonnet --- diff --git a/tests/regression/snapshot/.mcp.json b/tests/regression/snapshot/.mcp.json new file mode 100755 index 0000000..5443e91 --- /dev/null +++ b/tests/regression/snapshot/.mcp.json @@ -0,0 +1,12 @@ +{ + "mcpServers": { + "Gmail": { + "type": "http", + "url": "https://gmail.mcp.claude.com/mcp" + }, + "Google Calendar": { + "type": "http", + "url": "https://gcal.mcp.claude.com/mcp" + } + } +} diff --git a/tests/run.sh b/tests/run.sh index fd6cbc5..dde2d46 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -12,7 +12,7 @@ PASS=0 FAIL=0 FAILED_TESTS=() -for test_file in $(find "$SCRIPT_DIR" -name '*.test.sh' | sort); do +while IFS= read -r test_file; do echo "── $(basename "$test_file") ──────────────────────" # Source the test file to get its functions source "$test_file" @@ -28,7 +28,7 @@ for test_file in $(find "$SCRIPT_DIR" -name '*.test.sh' | sort); do fi unset -f "$fn" done -done +done < <(find "$SCRIPT_DIR" -name '*.test.sh' | sort) echo "" echo "==========================="