port-plugin — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited port-plugin (Agent Skill) and scored it 96/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 1 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Convert Agent Alchemy plugins (skills, agents, hooks, references, MCP configs) into formats compatible with other AI coding platforms using an extensible markdown-based adapter framework, live platform research, and interactive conversion workflows.
CRITICAL: Complete ALL 7 phases. The workflow is not complete until Phase 7: Summary is finished. After completing each phase, immediately proceed to the next phase without waiting for user prompts.
IMPORTANT: You MUST use the AskUserQuestion tool for ALL questions to the user. Never ask questions through regular text output.
Text output should only be used for:
If you need the user to make a choice or provide input, use AskUserQuestion.
NEVER do this (asking via text output):
Which plugin groups would you like to port?
1. core-tools
2. dev-tools
3. sdd-toolsALWAYS do this (using AskUserQuestion tool):
AskUserQuestion:
questions:
- header: "Plugin Groups"
question: "Which plugin groups would you like to port?"
options:
- label: "core-tools"
description: "3 skills, 2 agents — Deep analysis, codebase analysis, language patterns"
- label: "dev-tools"
description: "6 skills, 4 agents — Feature dev, code review, docs, changelog"
- label: "sdd-tools"
description: "6 skills, 3 agents — Spec creation, task management, execution"
multiSelect: trueCRITICAL: This skill performs an interactive conversion workflow, NOT an implementation plan. When invoked during Claude Code's plan mode:
Execute these phases in order, completing ALL of them:
4.5. Dry-Run Preview - Optional preview of expected output before conversion
Goal: Load configuration, parse arguments, and determine the target platform.
Parse $ARGUMENTS for:
--target <platform> — Target platform slug (default: opencode)--dry-run — Preview mode; show expected output without writing filesSet TARGET_PLATFORM from --target value (default: opencode). Set DRY_RUN from --dry-run flag (default: false).
Check if .claude/agent-alchemy.local.md exists and read for any plugin-tools section with settings:
- **plugin-tools**:
- **default-target**: opencode
- **default-output-dir**: ported/
- **auto-research**: trueIf settings exist, apply them as defaults (CLI arguments override settings file values).
Check that an adapter file exists for the target platform:
Glob to check ${CLAUDE_PLUGIN_ROOT}/references/adapters/{TARGET_PLATFORM}.mdADAPTERADAPTER = nullRead the marketplace registry to enumerate available plugin groups:
Read: ${CLAUDE_PLUGIN_ROOT}/../../.claude-plugin/marketplace.jsonParse the plugins array. Each entry has:
name — Full plugin name (e.g., agent-alchemy-core-tools)version — Current versiondescription — Brief descriptionsource — Relative path to plugin directory (e.g., ./core-tools)Build a list of available plugin groups, extracting the short group name from the source path (e.g., core-tools from ./core-tools).
Exclude `plugin-tools` from the selection list — the porter should not attempt to port itself.
Goal: Guide the user through selecting which plugin groups and individual components to port.
Inform the user of the target platform and configuration:
Target platform: {TARGET_PLATFORM}
Adapter: {adapter file path or "None — research agent will investigate"}
Dry-run: {DRY_RUN}Present all available plugin groups for selection using AskUserQuestion with multiSelect: true.
For each plugin group, scan its directory to count components:
Glob to count skills: claude/{group}/skills/*/SKILL.mdGlob to count agents: claude/{group}/agents/*.mdGlob to check for hooks: claude/{group}/hooks/hooks.jsonGlob to count reference dirs: claude/{group}/skills/*/references/Glob to check for MCP config: claude/{group}/.mcp.jsonBuild the selection options:
AskUserQuestion:
questions:
- header: "Plugin Group Selection"
question: "Which plugin groups would you like to port to {TARGET_PLATFORM}?"
options:
- label: "{group-name} (v{version})"
description: "{skill_count} skills, {agent_count} agents{, hooks}{, MCP config} — {marketplace description}"
multiSelect: trueEdge case — Empty plugin group: If a scanned group has zero skills, zero agents, no hooks, and no MCP config, skip it from the selection list and note: "Skipped {group}: no portable components found."
Store the selected groups as SELECTED_GROUPS.
If no groups are selected (user cancels or selects nothing), use AskUserQuestion to confirm:
AskUserQuestion:
questions:
- header: "No Selection"
question: "No plugin groups were selected. Would you like to start over or cancel?"
options:
- label: "Start over"
description: "Return to group selection"
- label: "Cancel"
description: "Exit the porter"
multiSelect: falseIf "Start over", repeat Step 2. If "Cancel", end the workflow gracefully.
For each selected group, scan and enumerate all individual components, then present for selection.
Component scanning per group:
claude/{group}/skills/*/SKILL.md:name and descriptionreferences/ subdirectory and count reference filesskill:{group}/{skill-name} with descriptionclaude/{group}/agents/*.md:name, description, and modelagent:{group}/{agent-name} with description and model tierclaude/{group}/hooks/hooks.json exists:hooks:{group} with hook count and event typesclaude/{group}/.mcp.json exists:mcp:{group} with server namesPresent components for each group using AskUserQuestion:
AskUserQuestion:
questions:
- header: "{group-name} Components"
question: "Select components to port from {group-name}:"
options:
- label: "All components"
description: "Port everything in {group-name} ({total_count} components)"
- label: "skill: {skill-name}"
description: "{skill description} ({ref_count} references)"
- label: "agent: {agent-name}"
description: "{agent description} (model: {model})"
- label: "hooks: {group}"
description: "{hook_count} hooks ({event types})"
- label: "mcp: {group}"
description: "MCP config ({server names})"
multiSelect: trueEdge case — "All components" selected: If the user selects "All components", include every component from that group in the selection. Do not present further sub-selection.
Build the final component list as SELECTED_COMPONENTS, a flat list of component identifiers with their types:
[
{ type: "skill", group: "core-tools", name: "deep-analysis", path: "claude/core-tools/skills/deep-analysis/SKILL.md" },
{ type: "agent", group: "core-tools", name: "code-explorer", path: "claude/core-tools/agents/code-explorer.md" },
{ type: "hooks", group: "core-tools", name: "hooks", path: "claude/core-tools/hooks/hooks.json" },
...
]Present a summary of the selection to the user:
## Selection Summary
**Target platform:** {TARGET_PLATFORM}
**Plugin groups:** {count} selected
**Total components:** {count}
| Group | Skills | Agents | Hooks | MCP | References |
|-------|--------|--------|-------|-----|------------|
| {group} | {n} | {n} | {yes/no} | {yes/no} | {n} files |
Components:
- {component list with types}Use AskUserQuestion to confirm:
AskUserQuestion:
questions:
- header: "Confirm Selection"
question: "Proceed with this selection?"
options:
- label: "Proceed"
description: "Continue to dependency validation"
- label: "Modify selection"
description: "Go back and change component selection"
- label: "Cancel"
description: "Exit the porter"
multiSelect: falseIf "Modify selection", return to Step 2. If "Cancel", end the workflow gracefully.
Goal: Build a full dependency graph from selected components, detect all five dependency types, flag missing references and external dependencies, and produce a dependency-sorted conversion order for Phase 5.
This phase consumes SELECTED_COMPONENTS and SELECTED_GROUPS from Phase 2 and produces DEPENDENCY_GRAPH (including CONVERSION_ORDER) for Phase 5.
For each component in SELECTED_COMPONENTS, read its source file and extract all dependency references. Initialize a flat dependency list:
DEPENDENCIES = [] // each entry: { source, target_type, target_ref, dep_type, raw_pattern, transitive }Process each component type using the sub-steps below. Apply these error handling rules during scanning:
Error: Unreadable file. If a component file cannot be read (permission error, file missing, I/O error), log a warning and continue scanning the remaining components:
WARNING: Could not read {component.path} -- skipping dependency scan for this component.Error: Malformed YAML frontmatter. If a component's YAML frontmatter cannot be parsed (invalid YAML between --- delimiters), log a parsing error and fall back to body-only scanning for that component:
WARNING: Malformed YAML frontmatter in {component.path} -- scanning body text only for dependencies.#### 1a: Skill Dependencies
For each skill component (type: "skill"), read its SKILL.md and scan for all five dependency types:
Dependency Type 1 -- Skill-to-skill references:
Scan the skill body for patterns that load another skill from the same plugin group:
Read ${CLAUDE_PLUGIN_ROOT}/skills/{skill-name}/SKILL.mdFor each match, extract {skill-name} and record:
{ source: this_component, target_type: "skill", target_ref: "{this_group}/{skill-name}", dep_type: "skill-to-skill", raw_pattern: "{matched line}", transitive: false }Dependency Type 2 -- Cross-plugin references:
Scan the body for patterns that reference a different plugin group:
${CLAUDE_PLUGIN_ROOT}/../{source-dir-name}/skills/{skill-name}/SKILL.md
${CLAUDE_PLUGIN_ROOT}/../{source-dir-name}/agents/{agent-name}.md
${CLAUDE_PLUGIN_ROOT}/../{source-dir-name}/For each match, extract {source-dir-name} and the component name, and record:
{ source: this_component, target_type: "skill" or "agent", target_ref: "{source-dir-name}/{name}", dep_type: "cross-plugin", raw_pattern: "{matched line}", transitive: false }Dependency Type 3 -- Reference file includes:
Scan the body for patterns that load reference files:
Read ${CLAUDE_PLUGIN_ROOT}/skills/{skill-name}/references/{filename}
${CLAUDE_PLUGIN_ROOT}/skills/{skill-name}/references/{filename}For each match, determine if the reference belongs to this skill or another skill:
references/ directory are internal by definition and do not create a dependency edgereferences/ directory create a dependency:{ source: this_component, target_type: "reference", target_ref: "{group}/{skill-name}/references/{filename}", dep_type: "reference-include", raw_pattern: "{matched line}", transitive: false }Dependency Type 4 -- Agent/subagent references:
Scan the body for Task tool invocations that spawn agents:
subagent_type: "{agent-name}"For each match, record:
{ source: this_component, target_type: "agent", target_ref: "{group}/{agent-name}", dep_type: "skill-to-skill", raw_pattern: "{matched line}", transitive: false }External dependencies (non-component):
Scan for system-level dependencies that cannot be ported as components:
mcp__{server}__{tool} or the mcp__ prefix in the body or allowed-tools list.sh, .py, .js)For each external dependency found:
{ source: this_component, target_type: "external", target_ref: "{description}", dep_type: "external", raw_pattern: "{matched line}", transitive: false }#### 1b: Agent Dependencies
For each agent component (type: "agent"), read its .md file:
Dependency Type 5 -- Agent-to-skill references:
Parse the YAML frontmatter and extract the skills: field (array of skill identifiers). For each skill listed:
{ source: this_component, target_type: "skill", target_ref: "{resolved skill reference}", dep_type: "agent-to-skill", raw_pattern: "skills: [{skill}]", transitive: false }Also scan the agent's markdown body for Read ${CLAUDE_PLUGIN_ROOT} and ${CLAUDE_PLUGIN_ROOT}/../ patterns, applying the same Type 1, Type 2, and Type 3 scans from Step 1a.
Tool dependencies (informational only):
Parse the tools: field from frontmatter. These are Claude Code platform tools classified as System dependencies. They do not create dependency edges but are tracked for the gap report.
#### 1c: Hook Dependencies
For each hooks component (type: "hooks"), read the hooks.json file and parse the JSON:
Dependency Type 4 -- Hook-to-script references:
For each hook entry, extract the command field and scan for:
${CLAUDE_PLUGIN_ROOT}/hooks/scripts/auto-approve.sh).sh, .py, .js, or other executable extensionsFor each script reference:
{ source: this_component, target_type: "script", target_ref: "{script path}", dep_type: "hook-to-script", raw_pattern: "{command value}", transitive: false }Tool matcher patterns in hooks reference Claude Code tool names -- classify these as System dependencies for informational tracking only.
#### 1d: MCP Config Dependencies
For each MCP config component (type: "mcp"), read the .mcp.json file:
Each configured MCP server is an external dependency requiring manual porting on the target platform. Record each server:
{ source: this_component, target_type: "external", target_ref: "MCP server: {server_name} ({command})", dep_type: "external", raw_pattern: "{server config snippet}", transitive: false }Construct a directed dependency graph from the parsed dependencies.
#### 2a: Create Graph Nodes and Edges
Nodes: All components in SELECTED_COMPONENTS, each identified by {type}:{group}/{name}.
Edges: For each entry in DEPENDENCIES where dep_type is NOT "external" and NOT a system dependency, create a directed edge from the source node to the node matching target_ref.
#### 2b: Classify Each Dependency
For each dependency in DEPENDENCIES, determine its classification by checking target_ref against SELECTED_COMPONENTS and SELECTED_GROUPS:
| Classification | Condition |
|---|---|
| Internal | Target component exists in SELECTED_COMPONENTS |
| External-selected | Target component's group is in SELECTED_GROUPS but the specific component was not individually selected |
| External-missing | Target component's group is NOT in SELECTED_GROUPS and not available in the marketplace |
| System | Target is a Claude Code platform feature (built-in tool, MCP protocol feature) |
| External | Target is a non-component dependency (MCP server process, bash script, external service) |
#### 2c: Resolve Deeply Nested References (Recursive)
Dependencies can form chains: Skill A loads Skill B, which loads Skill C, which loads Skill D. To ensure the full transitive dependency tree is captured:
transitive: false)SCANNED_COMPONENTS containing all components already processed in Step 1External-missing or External-selected:a. Determine the file path of the target component on disk (using the component scanning conventions: claude/{group}/skills/{name}/SKILL.md for skills, claude/{group}/agents/{name}.md for agents) b. If the file exists and the component is NOT in SCANNED_COMPONENTS:
transitive: trueDEPENDENCIESSCANNED_COMPONENTSc. If the file does not exist, skip it (it will be reported as unresolvable in Step 4)
External-missing or External-selected dependencies WARNING: Dependency chain depth limit (10) reached starting from {root component}. Some transitive dependencies may not be fully resolved.#### 2d: Detect Circular Dependencies
Run a cycle detection pass on the dependency graph using depth-first search:
CIRCULAR_REFS = [
{ cycle: ["{component_id_1}", "{component_id_2}", ..., "{component_id_1}"], warning: "Circular dependency detected" }
]Before alerting the user, determine whether the selection has any unresolved dependencies.
Count dependencies by classification:
internal_count -- dependencies already satisfied within the selectionexternal_selected_count -- dependencies in selected groups but not individually selectedexternal_missing_count -- dependencies not in any selected groupexternal_count -- non-component external dependencies (MCP servers, scripts)system_count -- Claude Code platform dependenciesIf `external_selected_count == 0` AND `external_missing_count == 0` AND `external_count == 0`:
The selection is fully self-contained. Display a brief confirmation and skip to Step 5:
All dependencies resolved -- no missing cross-plugin references.
{internal_count} internal dependencies found between selected components.Otherwise, proceed to Step 4.
Present the dependency analysis to the user, organized by category:
## Dependency Analysis
**Components scanned:** {SELECTED_COMPONENTS.length}
**Total dependencies found:** {DEPENDENCIES.length}
**Internal (resolved):** {internal_count}
**Missing component dependencies:** {external_selected_count + external_missing_count}
**External (manual porting needed):** {external_count}
**System (Claude Code platform):** {system_count}
{If external_selected_count + external_missing_count > 0:}
### Missing Component Dependencies
These selected components depend on components not in your selection:
| Source Component | Depends On | Dependency Type | Group | Reference Pattern |
|---|---|---|---|---|
| {source.name} | {target_ref} | {dep_type} | {target_group} | `{raw_pattern snippet}` |
{If any dependency has transitive: true:}
Note: Dependencies marked with (*) are transitive -- discovered by following dependency chains beyond the initial selection.
{If external_count > 0:}
### External Dependencies (Manual Porting Required)
These dependencies cannot be automatically ported and will require manual setup on the target platform:
| Source Component | External Dependency | Type | Notes |
|---|---|---|---|
| {source.name} | {target_ref} | MCP server / bash script | Requires manual configuration |
These will be documented in the gap report with setup instructions.#### 4a: Resolve Missing Component Dependencies
If there are missing component dependencies (external_selected_count + external_missing_count > 0):
Use AskUserQuestion to let the user decide:
AskUserQuestion:
questions:
- header: "Missing Dependencies"
question: "Some selected components depend on {external_selected_count + external_missing_count} components not in your selection. How would you like to handle this?"
options:
- label: "Add missing dependencies"
description: "Include {count} additional components to resolve all references"
- label: "Proceed without them"
description: "Continue -- missing references will be flagged in the gap report"
- label: "Go back to selection"
description: "Return to component selection to adjust"
multiSelect: falseIf "Add missing dependencies":
External-selected dependency: locate the component in its group directory, build its component entry (type, group, name, path), and add to SELECTED_COMPONENTSExternal-missing dependency: scan the component from disk using the component scanning conventions, build its entry, and add to SELECTED_COMPONENTSIf "Go back to selection": Return to Phase 2 Step 2.
If "Proceed without them": Continue to Step 5. Missing dependencies will be tracked as entries in CONVERSION_GAPS during Phase 5.
#### 4b: Report Circular References
If circular references were detected (from Step 2d):
Display a warning after the missing dependencies resolution:
### Circular References Detected
WARNING: {CIRCULAR_REFS.length} circular dependency chain(s) found:
{For each cycle in CIRCULAR_REFS:}
- {cycle[0]} -> {cycle[1]} -> ... -> {cycle[0]}
These will be handled during conversion (Phase 5) using topological sort with cycle-breaking.
The component with the fewest incoming dependencies in each cycle will be processed first.Circular references do not require user action -- they are informational warnings. Proceed to Step 5.
Build the final dependency graph summary and compute the conversion order for Phase 5.
#### 5a: Topological Sort
Compute a dependency-sorted conversion order:
{type}:{group}/{name} for deterministic orderingStore the sorted list as CONVERSION_ORDER.
#### 5b: Display Summary
## Dependency Graph
**Components:** {SELECTED_COMPONENTS.length}
**Internal dependencies:** {internal_count}
**External dependencies (to be flagged):** {external_count + external_missing_count}
**Circular references:** {CIRCULAR_REFS.length or "none"}
**System dependencies:** {system_count} Claude Code platform features
Conversion order (dependency-sorted):
1. {component_id} -- no dependencies
2. {component_id} -- depends on: {dep1}, {dep2}
3. {component_id} -- depends on: {dep1}
...#### 5c: Store Dependency Graph
Store the complete graph as DEPENDENCY_GRAPH for consumption by Phase 5:
DEPENDENCY_GRAPH = {
nodes: SELECTED_COMPONENTS,
edges: DEPENDENCIES,
circular_refs: CIRCULAR_REFS,
conversion_order: CONVERSION_ORDER,
external_deps: [entries where dep_type == "external"],
classification_counts: { internal, external_selected, external_missing, external, system }
}Goal: Spawn a research subagent to investigate the target platform's current plugin architecture, compare findings against the existing adapter file, and produce a merged knowledge base for conversion.
Before spawning the research agent, gather the context it needs:
SELECTED_COMPONENTS:skill_count — number of skill componentsagent_count — number of agent componentshook_count — number of hooks componentsref_count — total reference files across all selected skillsmcp_count — number of MCP config componentsADAPTER exists):${CLAUDE_PLUGIN_ROOT}/references/adapters/{TARGET_PLATFORM}.mdADAPTER_CONTENT for inclusion in the research prompttarget_platform_version field from the Adapter Version section Components to convert:
- Skills: {list of skill names}
- Agents: {list of agent names with model tiers}
- Hooks: {yes/no, with event types if yes}
- References: {count} files
- MCP configs: {yes/no, with server names if yes}
Key features used by these components:
- {e.g., "Task tool for subagent spawning (used by deep-analysis)"}
- {e.g., "AskUserQuestion for interactive workflows (used by create-spec)"}
- {e.g., "Cross-plugin composition via ${CLAUDE_PLUGIN_ROOT}/../ paths"}Spawn the research agent using the Task tool with subagent_type: "researcher" and model: "sonnet":
Task tool prompt:
"Research the plugin/extension system for: {TARGET_PLATFORM}
You are researching this platform to support converting Agent Alchemy (Claude Code) plugins to {TARGET_PLATFORM} format. The conversion needs to handle {skill_count} skills, {agent_count} agents, {hook_count} hook configs, {ref_count} reference files, and {mcp_count} MCP configs.
{If ADAPTER exists:}
## Existing Adapter File
An adapter file already exists for this platform. Here is its full content:
---BEGIN ADAPTER---
{ADAPTER_CONTENT}
---END ADAPTER---
IMPORTANT: Compare your research findings against every mapping in this adapter file. In your Adapter Comparison section:
- Flag any mappings that appear outdated based on your research
- Identify new platform features or changes not covered by the adapter
- Note the adapter's platform version ({adapter_platform_version}) vs. the current version you find
- For each stale or missing mapping, provide the corrected/new mapping
{If ADAPTER is null:}
## No Existing Adapter
No adapter file exists for this platform yet. Research from scratch and produce a comprehensive platform profile. Your findings will be the primary source of truth for the conversion engine.
## Components Being Ported
{component_summary from Step 1}
## Research Focus Areas
Investigate these areas thoroughly — they directly affect conversion quality:
1. **Plugin directory structure**: Where plugins live, file naming conventions, required files
2. **Configuration format**: YAML, JSON, TOML, or other formats for plugin metadata
3. **Tool/capability equivalents**: What built-in tools does the platform provide? Map against Claude Code's tools (Read, Write, Edit, Glob, Grep, Bash, Task, WebSearch, WebFetch, AskUserQuestion)
4. **Model configuration**: How are AI models specified? Does the platform support model tiers or selection?
5. **Lifecycle hooks**: Does the platform have pre/post execution hooks? Event system?
6. **Composition mechanism**: How do plugins load or reference other plugins? Is there an equivalent to skill composition via prompt injection?
7. **Path resolution**: Equivalent of ${CLAUDE_PLUGIN_ROOT} for resolving plugin-relative paths
8. **Agent/subagent support**: Can plugins spawn sub-tasks or sub-agents? How?
9. **User interaction**: How do plugins prompt the user for input during execution?
10. **MCP integration**: Does the platform support Model Context Protocol servers?
Return a structured platform profile following your output format specification. Include confidence levels for every finding."Retry logic: If the Task tool call fails (agent error, timeout, or empty response), retry once automatically. If the retry also fails, proceed to the research failure handling in Step 5.
When the research agent returns its platform profile:
The conversion engine uses a merged knowledge base called CONVERSION_KNOWLEDGE that combines the adapter file and research findings with clear precedence rules:
| Category | Precedence Rule |
|---|---|
| Tool name mappings | Adapter takes precedence for established mappings; research fills gaps where adapter has null or no entry |
| Model tier mappings | Adapter takes precedence; research supplements with version-specific notes |
| Platform version info | Research always takes precedence (it is fresher) |
| Directory structure | Research takes precedence if it differs from adapter (platform may have changed) |
| Composition patterns | Research takes precedence if it provides more detail; adapter is baseline |
| Lifecycle hooks | Research takes precedence if it finds new hook types; adapter is baseline |
| Known limitations | Union of both — all limitations from adapter AND research are tracked |
| Path resolution | Research takes precedence if adapter mapping is marked as uncertain |
When adapter and research agree on a mapping, mark it as confidence: high. When only one source provides a mapping, mark it as confidence: medium. When adapter and research disagree, mark it as confidence: conflict and flag for user resolution in Step 4.
Initialize STALENESS_STATUS to track the result for migration guide logging:
STALENESS_STATUS = {
checked: false,
adapter_version: null,
adapter_platform_version: null,
research_platform_version: null,
is_stale: false,
is_unversioned: false,
research_has_version: false,
stale_mappings: [],
new_features: [],
user_decision: null,
error: null
}Wrap the entire staleness check in error-safe logic. If any step below fails (parsing error, unexpected format, etc.), set STALENESS_STATUS.error to a description of the failure, log a warning ("Staleness check encountered an error: {error}. Proceeding with conversion."), and continue to Step 4. The staleness check must never block the conversion workflow.
If ADAPTER exists:
a. Read adapter version fields from the Adapter Version section of the adapter file:
adapter_version (the adapter file's own version, e.g., 1.0.0)target_platform_version (the platform version the adapter was written for, e.g., 0.0.55)STALENESS_STATUS.checked = trueSTALENESS_STATUS.is_unversioned = true and log a warning: "Adapter file for {TARGET_PLATFORM} has no version fields. Staleness cannot be determined — proceeding with adapter as-is."b. Check if research reported a platform version:
STALENESS_STATUS.research_has_version = false and log a note: "Research did not report a specific platform version for {TARGET_PLATFORM}. Skipping version comparison."STALENESS_STATUS.research_has_version = true and store it in STALENESS_STATUS.research_platform_versionc. Compare versions (only if both adapter and research have version information):
target_platform_version equals research_platform_version exactly, versions match — STALENESS_STATUS.is_stale = false0.0.55, 1.2.3), compare numerically. If research version is newer, set STALENESS_STATUS.is_stale = true0.0.55 but research reports v0.0.55, 0.0.55-beta, or a date-based version), attempt normalization:v or V prefix-alpha, -beta, -rc.1, etc.) for comparisonSTALENESS_STATUS.is_stale = true (err on the side of warning) and note: "Version format mismatch — adapter: '{adapter_version}', research: '{research_version}'. Treating as potentially stale."d. Collect stale mappings and new features (regardless of version match, if research provides an Adapter Comparison section):
STALE_MAPPINGS and store in STALENESS_STATUS.stale_mappingsNEW_FEATURES and store in STALENESS_STATUS.new_featuresADAPTER_STALE = true (content staleness despite version match)If any mappings in CONVERSION_KNOWLEDGE are marked as confidence: conflict:
Conflicts found between adapter file and research findings:
1. {mapping_name}:
- Adapter says: {adapter_value}
- Research says: {research_value} (source: {url})
- Impact: {which components are affected}
2. {mapping_name}:
... AskUserQuestion:
questions:
- header: "Research Conflict"
question: "The adapter file and fresh research disagree on {mapping_name}. Which should be used for conversion?"
options:
- label: "Use research finding"
description: "{research_value} — from {source}, researched today"
- label: "Use adapter mapping"
description: "{adapter_value} — from adapter file v{adapter_version}"
- label: "Skip this mapping"
description: "Flag as unresolved in the gap report"
multiSelect: falseCONVERSION_KNOWLEDGE:confidence: high, mark source: researchconfidence: high, mark source: adapterconfidence: unresolved, will appear in the gap reportIf there are no conflicts, skip this step entirely.
Display a summary of the research findings and merged knowledge to the user:
## Platform Research: {TARGET_PLATFORM}
**Documentation quality:** {research_summary.documentation_quality}
**Sources consulted:** {research_summary.sources_consulted}
**Overall confidence:** {research_summary.overall_confidence}
### Key Findings
- **Plugin format:** {file_format description}
- **Tool equivalents:** {matched_count}/{total_count} Claude Code tools have equivalents
- **Model support:** {model_tier_summary}
- **Composition:** {composition_patterns summary}
- **Hooks:** {lifecycle_hooks summary — supported/not supported/partial}
- **Agent spawning:** {agent/subagent support summary}
- **User interaction:** {user interaction mechanism summary}
### Knowledge Sources for Conversion
- Adapter file mappings: {count} mappings
- Research-supplemented mappings: {count} new/updated mappings
- Conflicts resolved: {count}
- Unresolved gaps: {count}
{If STALENESS_STATUS.is_unversioned = true:}
### Adapter Version Notice
NOTE: The adapter file for {TARGET_PLATFORM} has no version fields. Staleness cannot be determined. The adapter will be used as-is, supplemented by research findings.
{If STALENESS_STATUS.research_has_version = false AND ADAPTER exists:}
### Research Version Notice
NOTE: Research did not report a specific platform version for {TARGET_PLATFORM}. Version comparison was skipped. The adapter's target_platform_version ({STALENESS_STATUS.adapter_platform_version}) is assumed current.
{If STALENESS_STATUS.error:}
### Staleness Check Notice
NOTE: The staleness check encountered an error: {STALENESS_STATUS.error}. Conversion will proceed using available adapter and research data.
{If ADAPTER_STALE = true:}
### Adapter Staleness Warning
WARNING: The existing adapter file targets {TARGET_PLATFORM} v{STALENESS_STATUS.adapter_platform_version}, but research found the current version is v{STALENESS_STATUS.research_platform_version}.
Stale mappings detected:
{For each stale mapping in STALENESS_STATUS.stale_mappings:}
- {mapping_name}: adapter says "{old_value}", research found "{new_value}"
New platform features not in adapter:
{For each new feature in STALENESS_STATUS.new_features:}
- {feature_name}: {description}
{If no ADAPTER:}
### New Platform Profile
Research produced a fresh platform profile since no adapter file existed.
Consider saving a new adapter file from these findings after conversion.After displaying the summary, if ADAPTER_STALE = true, present the user with options via AskUserQuestion:
AskUserQuestion:
questions:
- header: "Adapter Staleness"
question: "The adapter file for {TARGET_PLATFORM} appears to be stale (adapter targets v{STALENESS_STATUS.adapter_platform_version}, research found v{STALENESS_STATUS.research_platform_version}). {stale_mapping_count} mappings may be outdated and {new_feature_count} new platform features were found. How would you like to proceed?"
options:
- label: "Proceed with existing adapter"
description: "Continue conversion — research findings will supplement and override stale adapter mappings via CONVERSION_KNOWLEDGE precedence rules"
- label: "Update adapter file first"
description: "Pause conversion to update the adapter file with research findings, then resume"
multiSelect: falseHandle each choice:
STALENESS_STATUS.user_decision = "proceed". Continue to Phase 5 using CONVERSION_KNOWLEDGE as already merged (research takes precedence for stale mappings per the precedence rules in Step 3). Log: "User chose to proceed with existing adapter. Research findings will supplement stale mappings during conversion."STALENESS_STATUS.user_decision = "update". Before continuing to Phase 5:${CLAUDE_PLUGIN_ROOT}/references/adapters/{TARGET_PLATFORM}.mdSTALE_MAPPINGS: update the mapping value in the adapter to match the research findingNEW_FEATURES: add the feature to the appropriate adapter sectiontarget_platform_version field to the research-reported versionlast_updated field to today's dateadapter_version patch number (e.g., 1.0.0 -> 1.0.1)changelog field describing the updateADAPTER and re-merge into CONVERSION_KNOWLEDGEIf STALENESS_STATUS.is_unversioned = true and stale mappings or new features were found, also present the AskUserQuestion above (since content can be stale even without version fields).
Record staleness status for migration guide:
Store STALENESS_STATUS for inclusion in the migration guide output (Phase 6). The migration guide should include a "Platform Adapter Status" section documenting:
If the research agent failed (both initial attempt and retry):
The platform research agent was unable to complete its investigation.
Reason: {error_message or "timeout/empty response"} AskUserQuestion:
questions:
- header: "Research Issue"
question: "The platform research agent encountered an issue: {brief_reason}. How would you like to proceed?"
options:
- label: "Retry research"
description: "Launch the research agent again (attempt {retry_count + 1})"
- label: "Use adapter only"
description: "Proceed with existing adapter file mappings (no fresh research)"
- label: "Cancel"
description: "Exit the porter"
multiSelect: falseADAPTER exists: Set RESEARCH_FINDINGS = null. Set CONVERSION_KNOWLEDGE to adapter mappings only with confidence: medium on all entries (no research validation). Inform the user: "Proceeding with adapter file mappings only. Conversion quality may be affected — adapter mappings have not been validated against current platform documentation." Continue to Phase 5.ADAPTER is null: Inform the user: "Cannot proceed — no adapter file exists and research failed. The conversion engine requires at least one source of platform knowledge." Offer: AskUserQuestion:
questions:
- header: "No Platform Knowledge Available"
question: "Neither an adapter file nor research findings are available. What would you like to do?"
options:
- label: "Retry research"
description: "Try the research agent one more time"
- label: "Cancel"
description: "Exit the porter — conversion cannot proceed without platform knowledge"
multiSelect: falseGoal: Offer an optional preview of expected conversion output -- showing the target file tree, per-component conversion status, expected issues, and estimated fidelity -- without writing any files or running the full conversion engine.
This phase consumes SELECTED_COMPONENTS, DEPENDENCY_GRAPH (from Phase 3), and CONVERSION_KNOWLEDGE (from Phase 4). It does not modify any of these structures.
If `DRY_RUN = true` (set via --dry-run flag): Skip the offer and proceed directly to Step 2 (preview is mandatory).
If `DRY_RUN = false`: Use AskUserQuestion to offer the preview as an optional step:
AskUserQuestion:
questions:
- header: "Dry-Run Preview"
question: "Would you like to preview the expected conversion output before proceeding? This shows the target file tree, per-component status, and expected issues without writing any files."
options:
- label: "Run preview"
description: "Generate and display a dry-run preview of the conversion"
- label: "Skip preview and convert"
description: "Proceed directly to interactive conversion (Phase 5)"
multiSelect: falseIf "Skip preview and convert": Skip the rest of Phase 4.5 and proceed directly to Phase 5.
If "Run preview": Continue to Step 2.
Generate the preview by analyzing CONVERSION_KNOWLEDGE mappings against each component in SELECTED_COMPONENTS. This step predicts conversion outcomes without running the full conversion engine.
Wrap the entire preview generation in error-safe logic. If any step below fails (parsing error, unexpected format, etc.), log a warning ("Preview generation encountered an error: {error}. You can still proceed with conversion."), skip to Step 4 (user decision), and offer the option to proceed with conversion despite the preview failure.
#### 2a: Build Target File Tree
For each component in SELECTED_COMPONENTS, predict the output file path using CONVERSION_KNOWLEDGE:
claude/{group}/skills/{name}/SKILL.md to target path using directory structure mappings:CONVERSION_KNOWLEDGE for skill_file_pattern in directory structure mappingsskill_file_pattern is defined (e.g., {name}/SKILL.md): expand the pattern by replacing {name} with the skill name (applying naming_convention), then build path: {plugin_root}/{skill_dir}/{expanded_pattern} (e.g., .opencode/skills/deep-analysis/SKILL.md)skill_file_pattern: apply naming_convention and file_extension for a flat filename, then build path: {plugin_root}/{skill_dir}/{converted_filename}claude/{group}/agents/{name}.md to target path:{plugin_root}/{agent_dir}/{converted_filename}null or alternative): note the mapping strategy (e.g., "merged into skill" or "mapped to fixed type")claude/{group}/hooks/hooks.json to the target hooks format:{plugin_root}/{hook_dir}/{target_hook_filename}references/ directories:{plugin_root}/{reference_dir}/{filename}none or inline): note as "will be inlined into parent skill"claude/{group}/.mcp.json to the target MCP format:{plugin_root}/{config_dir}/{target_mcp_filename}Store the predicted paths as PREVIEW_FILE_TREE -- a list of entries, each containing: { source_path, target_path, component_type, component_name, mapping_strategy }.
#### 2b: Predict Per-Component Conversion Status
For each component, estimate its conversion status by analyzing how well CONVERSION_KNOWLEDGE covers the component's features:
allowed-tools and body, check how many have non-null mappings in tool name mappings${CLAUDE_PLUGIN_ROOT} patterns, check if composition mechanism is not noneAskUserQuestion references, check if a mapping existssupported (non-null mapping exists), partial (mapping is partial:* or workaround-dependent), and unsupported (mapping is null) features optimistic_score = ((supported + partial) * 1.0 + unsupported * 0.0) / total * 100
pessimistic_score = ((supported * 1.0) + (partial * 0.7) + (unsupported * 0.0)) / total * 100"Full" -- component is highly portable"Partial" -- component works but with limitations"Unsupported" -- significant gaps, may not be worthwhileStore predictions as PREVIEW_STATUS -- a list of entries, each containing: { component_name, component_type, group, supported_count, partial_count, unsupported_count, total_count, optimistic_score, pessimistic_score, predicted_status }.
#### 2c: Collect Expected Issues
For each component, identify expected issues from the mapping analysis:
null in tool name mappingsnull in frontmatter mappingsnone and component uses compositionnull in hook event mappingsAskUserQuestion and the mapping is nullGroup issues by severity:
Store as PREVIEW_ISSUES -- a list of entries, each containing: { component_name, feature, severity, reason, adapter_notes }.
#### 2d: Calculate Estimated Fidelity Range
Compute an overall estimated fidelity range across all components:
overall_optimistic = sum(component.optimistic_score * component.weight) / sum(component.weight)
overall_pessimistic = sum(component.pessimistic_score * component.weight) / sum(component.weight)Where weight is the same as Phase 5 Step 9:
skill = 3, agent = 2, hooks = 1, reference = 1, mcp = 1Round both scores to the nearest integer.
Present the preview in a structured format.
Large plugin group handling: If SELECTED_COMPONENTS has more than 20 components, summarize the file tree by grouping files by directory rather than listing every individual file. Show the directory structure with file counts per directory instead.
## Dry-Run Preview: {TARGET_PLATFORM}
**Components:** {SELECTED_COMPONENTS.length}
**Estimated fidelity:** {overall_pessimistic}% - {overall_optimistic}%
### Target File Tree
{If 20 or fewer components:}
{target_root}/
{For each entry in PREVIEW_FILE_TREE, sorted by target_path:}
{target_path} <- {source_path} [{mapping_strategy}]
{If more than 20 components:}
{target_root}/
{skill_dir}/ ({skill_count} files)
{agent_dir}/ ({agent_count} files)
{hook_dir}/ ({hook_count} files)
{reference_dir}/ ({ref_count} files)
{config_dir}/ ({config_count} files)
### Per-Component Conversion Status
| Component | Type | Group | Status | Fidelity Estimate | Issues |
|-----------|------|-------|--------|-------------------|--------|
{For each entry in PREVIEW_STATUS:}
| {component_name} | {component_type} | {group} | {predicted_status} | {pessimistic_score}% - {optimistic_score}% | {issue_count} |
{If all components have predicted_status == "Unsupported":}
### Warning: All Components Unsupported
All selected components are predicted to have significant conversion gaps on {TARGET_PLATFORM}. The overall estimated fidelity is {overall_pessimistic}% - {overall_optimistic}%. Conversion may not produce useful output. Consider:
- Selecting a different target platform
- Selecting fewer, simpler components
- Reviewing the gap details below before proceeding
### Expected Issues
{If PREVIEW_ISSUES is empty:}
No significant issues detected. All features have direct or partial mappings.
{If PREVIEW_ISSUES is not empty:}
{Group by severity, show critical first:}
**Critical ({critical_count}):**
{For each critical issue:}
- **{component_name}**: `{feature}` -- {reason}{If adapter_notes: " ({adapter_notes})"}
**Functional ({functional_count}):**
{For each functional issue:}
- **{component_name}**: `{feature}` -- {reason}
**Cosmetic ({cosmetic_count}):**
{For each cosmetic issue:}
- **{component_name}**: `{feature}` -- {reason}
### Estimated Fidelity
**Overall:** {overall_pessimistic}% - {overall_optimistic}% (weighted by component complexity)
The fidelity range reflects the best-case (all partial mappings succeed fully) and worst-case (partial mappings contribute reduced fidelity) scenarios. The actual score will be determined during interactive conversion when incompatibilities are resolved.After displaying the preview, present the user with a decision via AskUserQuestion.
If `DRY_RUN = true` (invoked with --dry-run flag):
AskUserQuestion:
questions:
- header: "Dry-Run Complete"
question: "The dry-run preview is complete. No files were written. Would you like to proceed with the actual conversion?"
options:
- label: "Proceed with conversion"
description: "Continue to Phase 5 (Interactive Conversion) and convert the selected components"
- label: "Done"
description: "Exit the porter -- preview only"
multiSelect: falseIf `DRY_RUN = false` (preview was offered and accepted):
AskUserQuestion:
questions:
- header: "Preview Complete"
question: "Preview complete. Proceed with interactive conversion?"
options:
- label: "Proceed with conversion"
description: "Continue to Phase 5 and convert the selected components"
- label: "Cancel"
description: "Exit the porter"
multiSelect: falseHandle choices:
DRY_RUN = false (in case it was true). Continue to Phase 5. Porter exited after dry-run preview. No files were written.
Components analyzed: {count}
Estimated fidelity: {overall_pessimistic}% - {overall_optimistic}%Goal: Convert each selected component using a wave-based agent team, where each port-converter agent (Sonnet) handles a single component in an isolated context. The orchestrator (this skill, Opus) manages session files, spawns agent waves, resolves incompatibilities between waves, and consolidates results.
This phase consumes SELECTED_COMPONENTS, CONVERSION_ORDER, DEPENDENCY_GRAPH, and CONVERSION_KNOWLEDGE from Phases 2-4.
Create the session directory and write shared files that converter agents will read.
#### 1a: Create Session Directory
mkdir -p .claude/sessions/__port_live__/resultsIf `.claude/sessions/__port_live__/` already exists (stale session from a previous interrupted run):
mv .claude/sessions/__port_live__ .claude/sessions/port-interrupted-{YYYYMMDD}-{HHMMSS}mkdir -p .claude/sessions/__port_live__/results#### 1b: Serialize Conversion Knowledge
Write CONVERSION_KNOWLEDGE to .claude/sessions/__port_live__/conversion_knowledge.md following the format specified in ${CLAUDE_PLUGIN_ROOT}/skills/port-plugin/references/session-format.md.
The file contains all 9 adapter mapping sections (tool names, model tiers, skill frontmatter, agent frontmatter, hook events, directory structure, composition mechanism, path resolution, known limitations) serialized as markdown tables and key-value sections.
This file is written once and not modified during conversion.
#### 1c: Serialize Dependency Graph
Write DEPENDENCY_GRAPH to .claude/sessions/__port_live__/dependency_graph.md following the session format reference.
Include: conversion order with wave assignments, dependency edges, circular references (if any), external dependencies, and classification counts.
This file is written once and not modified during conversion.
#### 1d: Initialize Resolution Cache
Write an empty resolution cache to .claude/sessions/__port_live__/resolution_cache.md:
# Resolution Cache
## Cached Decisions
| Group Key | Decision Type | Workaround Applied | First Component | Apply Globally |
|-----------|-------------|-------------------|-----------------|---------------|This file grows between waves as the orchestrator resolves incompatibilities.
#### 1e: Compute Wave Assignments
Using CONVERSION_ORDER from Phase 3's topological sort, assign each component to a wave:
Cap each wave at 5 concurrent agents (matching the execute-tasks pattern).
If a wave has more than 5 components, split it into sub-waves of up to 5, maintaining the same dependency level.
#### 1f: Display Execution Plan
Present the wave-based execution plan:
## Phase 5: Wave-Based Conversion
**Session directory:** .claude/sessions/__port_live__/
**Total components:** {count}
**Waves:** {wave_count}
**Max concurrent agents per wave:** 5
### Execution Plan
| Wave | Components | Types |
|------|-----------|-------|
| 1 | {component_list} | {type_summary} |
| 2 | {component_list} | {type_summary} |
| ... | ... | ... |
### Session Files Written
- conversion_knowledge.md — {line_count} lines (merged adapter + research)
- dependency_graph.md — {line_count} lines (serialized graph)
- resolution_cache.md — initialized (empty)
- results/ — empty directory for agent outputProcess components wave by wave. For each wave, spawn port-converter agents in parallel, then resolve incompatibilities between waves.
#### 2a: Wave Loop
For each wave (starting with Wave 1):
Display wave start:
### Wave {n}/{total_waves}: {component_count} component(s)Spawn converter agents using the Task tool with up to 5 parallel calls in a single turn:
For each component in the current wave, launch a port-converter agent:
Task tool:
subagent_type: "agent-alchemy-plugin-tools:port-converter"
model: "sonnet"
prompt: |
Convert this component to {TARGET_PLATFORM} format.
## Component Details
- **Type:** {component.type}
- **Group:** {component.group}
- **Name:** {component.name}
- **Source path:** {component.path}
- **Component ID:** {component.type}-{component.group}-{component.name}
## Session Directory
{absolute_path_to}/.claude/sessions/__port_live__/
## Target Platform
{TARGET_PLATFORM}
## Converter Reference to Load
{If type == "agent": "${CLAUDE_PLUGIN_ROOT}/references/agent-converter.md"}
{If type == "hooks": "${CLAUDE_PLUGIN_ROOT}/references/hook-converter.md"}
{If type == "reference": "${CLAUDE_PLUGIN_ROOT}/references/reference-converter.md"}
{If type == "mcp": "${CLAUDE_PLUGIN_ROOT}/references/mcp-converter.md"}
{If type == "skill": "None — skill conversion logic is built into the agent"}
## Session Format Reference
Read ${CLAUDE_PLUGIN_ROOT}/skills/port-plugin/references/session-format.md for the result file format specification.CRITICAL: Launch all agents in a wave as parallel Task tool calls in a single message. Do not wait for one agent to finish before launching the next within the same wave. This is the key to context isolation — each agent gets its own context window.
Wait for all agents in the wave to complete before proceeding.
#### 2b: Collect Wave Results
After all agents in a wave return:
.claude/sessions/__port_live__/results/ for this wave's components: Read: .claude/sessions/__port_live__/results/result-{component-id}.mdStatus = error:CONVERTED_COMPONENTS with status = "error" and fidelity_score = 0{ component, feature: "entire_component", reason: "Conversion agent failed", severity: "critical" }#### 2c: Resolve Incompatibilities
After collecting all wave results, process unresolved incompatibilities:
.claude/sessions/__port_live__/resolution_cache.md. For each group:group_key found with apply_globally = true: auto-apply the cached decision to the result file's converted content by replacing the inline marker with the resolution. Record with resolution_mode: "cached".apply_globally = false: offer to reuse via AskUserQuestion (see below)${CLAUDE_PLUGIN_ROOT}/references/incompatibility-resolver.md:~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.