| name | mastermind-export |
| description | Mastermind export — full org portability. Export an org to a compressed archive (tar.gz or zip) with file tree selection, and import from an archive with collision strategy and adapter override. Mirrors Paperclip's CompanyExport/CompanyImport pages. |
| type | domain-skill |
| default_mode | confirm |
Mastermind Export
This skill is invoked by mastermind:export or directly via /mastermind:export.
Inputs
brain_context: BRAIN CONTEXT block (injected by command, or loaded below if standalone)
org_name: org to export/import (required)
action: export | import | preview | list-archives
output_path: destination path for exported archive (optional, default: .monomind/exports/<org>-<ts>.tar.gz)
input_path: path to archive file to import (required for import)
collision_strategy: merge | overwrite | skip (default: merge — for import)
adapter_override: adapter type to substitute during import (optional: claude-local | gemini-local | codex-local)
include: comma-separated sections to include (config,goals,routines,projects,members,adapters,secrets-refs,activity — default: all except secrets-refs)
dry_run: true | false — for import, preview changes without writing (default false)
caller: command | master
Sections Available for Export
| Section | Files Included | Default |
|---|
config | <org>.json | ✓ |
goals | <org>-goals.json | ✓ |
routines | <org>-routines.json | ✓ |
projects | <org>-projects.json | ✓ |
issues | <org>-issues.json | ✓ |
members | <org>-members.json | ✓ |
adapters | <org>-adapters.json | ✓ |
environments | <org>-environments.json | ✓ |
workspaces | <org>-workspaces.json | ✓ |
threads | <org>-threads.json | ✓ |
budgets | <org>-budgets.json | ✓ |
activity | <org>-activity.jsonl (last 500 events) | ✓ |
secrets-refs | secret reference names only (NO values) | opt-in |
SECURITY: Secret values are NEVER exported. Only masked reference names may be included if secrets-refs is explicitly listed in include.
Collision Strategies (import)
| Strategy | Behavior |
|---|
merge | New records added; existing records with same ID kept as-is |
overwrite | New records replace existing ones with same ID |
skip | Skip entire section if any file already exists in org |
Step 0 — Brain Load (standalone only)
If caller is not "command", load brain context following mastermind-protocol/SKILL.md Brain Load Procedure with namespace: ops.
Step 1 — Validate Org
orgFile=".monomind/orgs/${org_name}.json"
[ ! -f "$orgFile" ] && { echo "ERROR: Org '${org_name}' not found."; exit 1; }
exportsDir=".monomind/exports"
mkdir -p "$exportsDir"
ts=$(date -u +%Y%m%dT%H%M%SZ)
defaultOutput="${exportsDir}/${org_name}-${ts}.tar.gz"
outputPath="${output_path:-$defaultOutput}"
Step 2 — Execute Action
preview
Show what would be included in an export without writing anything:
echo "EXPORT PREVIEW — org: $org_name"
echo "────────────────────────────────────────────────────────"
allSections="config goals routines projects issues members adapters environments workspaces threads budgets activity"
includeSections="${include:-$allSections}"
for section in $allSections; do
echo "$includeSections" | grep -qw "$section" || continue
case "$section" in
config) srcFile=".monomind/orgs/${org_name}.json" ;;
goals) srcFile=".monomind/orgs/${org_name}-goals.json" ;;
routines) srcFile=".monomind/orgs/${org_name}-routines.json" ;;
projects) srcFile=".monomind/orgs/${org_name}-projects.json" ;;
issues) srcFile=".monomind/orgs/${org_name}-issues.json" ;;
members) srcFile=".monomind/orgs/${org_name}-members.json" ;;
adapters) srcFile=".monomind/orgs/${org_name}-adapters.json" ;;
environments) srcFile=".monomind/orgs/${org_name}-environments.json" ;;
workspaces) srcFile=".monomind/orgs/${org_name}-workspaces.json" ;;
threads) srcFile=".monomind/orgs/${org_name}-threads.json" ;;
budgets) srcFile=".monomind/orgs/${org_name}-budgets.json" ;;
activity) srcFile=".monomind/orgs/${org_name}-activity.jsonl" ;;
secrets-refs) srcFile=".monomind/orgs/.secrets/${org_name}/" ;;
esac
if [ -f "$srcFile" ] || [ -d "$srcFile" ]; then
size=$(du -sh "$srcFile" 2>/dev/null | cut -f1 || echo "?")
echo " ✓ $section ($srcFile) [$size]"
else
echo " - $section ($srcFile) [not found — will be skipped]"
fi
done
echo ""
echo "Output would be: $outputPath"
echo "Run with --action export to proceed."
export
echo "Exporting org '$org_name'…"
allSections="config goals routines projects issues members adapters environments workspaces threads budgets activity"
includeSections="${include:-$allSections}"
tmpDir=$(mktemp -d)
orgExportDir="${tmpDir}/${org_name}"
mkdir -p "$orgExportDir"
fileCount=0
for section in $allSections; do
echo "$includeSections" | grep -qw "$section" || continue
case "$section" in
config) srcFile=".monomind/orgs/${org_name}.json" ; dstName="${org_name}.json" ;;
goals) srcFile=".monomind/orgs/${org_name}-goals.json" ; dstName="${org_name}-goals.json" ;;
routines) srcFile=".monomind/orgs/${org_name}-routines.json" ; dstName="${org_name}-routines.json" ;;
projects) srcFile=".monomind/orgs/${org_name}-projects.json" ; dstName="${org_name}-projects.json" ;;
issues) srcFile=".monomind/orgs/${org_name}-issues.json" ; dstName="${org_name}-issues.json" ;;
members) srcFile=".monomind/orgs/${org_name}-members.json" ; dstName="${org_name}-members.json" ;;
adapters) srcFile=".monomind/orgs/${org_name}-adapters.json" ; dstName="${org_name}-adapters.json" ;;
environments) srcFile=".monomind/orgs/${org_name}-environments.json" ; dstName="${org_name}-environments.json" ;;
workspaces) srcFile=".monomind/orgs/${org_name}-workspaces.json" ; dstName="${org_name}-workspaces.json" ;;
threads) srcFile=".monomind/orgs/${org_name}-threads.json" ; dstName="${org_name}-threads.json" ;;
budgets) srcFile=".monomind/orgs/${org_name}-budgets.json" ; dstName="${org_name}-budgets.json" ;;
activity) srcFile=".monomind/orgs/${org_name}-activity.jsonl" ; dstName="${org_name}-activity.jsonl" ;;
secrets-refs)
secretsDir=".monomind/orgs/.secrets/${org_name}"
if [ -d "$secretsDir" ]; then
refsList=$(ls "$secretsDir" 2>/dev/null | jq -Rsc 'split("\n") | map(select(. != ""))')
echo "{\"secret_refs\":$refsList,\"note\":\"values_not_exported\"}" > "${orgExportDir}/${org_name}-secret-refs.json"
echo " ✓ secrets-refs (reference names only)"
fileCount=$((fileCount + 1))
fi
continue ;;
esac
if [ -f "$srcFile" ]; then
if [ "$section" = "environments" ]; then
jq '.environments = [(.environments // [])[] | del(.key_material,.private_key,.ssh_key,.password)]' \
"$srcFile" > "${orgExportDir}/${dstName}"
elif [ "$section" = "activity" ]; then
tail -500 "$srcFile" > "${orgExportDir}/${dstName}"
else
cp "$srcFile" "${orgExportDir}/${dstName}"
fi
echo " ✓ $section"
fileCount=$((fileCount + 1))
else
echo " - $section (skipped — file not found)"
fi
done
jq -cn \
--arg org "$org_name" \
--arg ts "$ts" \
--arg sections "$includeSections" \
--argjson files "$fileCount" \
'{"org":$org,"exported_at":$ts,"sections":($sections|split(" ")),"file_count":$files,"version":"1.0"}' \
> "${orgExportDir}/manifest.json"
tar -czf "$outputPath" -C "$tmpDir" "$org_name" 2>/dev/null
rm -rf "$tmpDir"
echo ""
echo "Export complete: $outputPath"
echo " Files: $fileCount | Sections: $includeSections"
echo " Size: $(du -sh "$outputPath" 2>/dev/null | cut -f1)"
import
[ -z "$input_path" ] && { echo "ERROR: --input-path required."; exit 1; }
[ ! -f "$input_path" ] && { echo "ERROR: Archive not found: $input_path"; exit 1; }
collision="${collision_strategy:-merge}"
case "$collision" in merge|overwrite|skip) : ;; *)
echo "ERROR: --collision-strategy must be merge, overwrite, or skip"; exit 1 ;;
esac
dryRun="${dry_run:-false}"
[ "$dryRun" = "true" ] && echo "DRY RUN — no changes will be written."
echo "IMPORT — org: $org_name"
echo "Collision strategy: $collision"
[ -n "$adapter_override" ] && echo "Adapter override: $adapter_override"
echo "────────────────────────────────────────────────────────"
tmpDir=$(mktemp -d)
tar -xzf "$input_path" -C "$tmpDir" 2>/dev/null || { echo "ERROR: Failed to extract archive."; rm -rf "$tmpDir"; exit 1; }
archiveOrgDir=$(find "$tmpDir" -name "manifest.json" -maxdepth 3 | head -1 | xargs dirname 2>/dev/null)
[ -z "$archiveOrgDir" ] && { echo "ERROR: No manifest.json found in archive. Invalid export?"; rm -rf "$tmpDir"; exit 1; }
archiveOrg=$(jq -r '.org // ""' "${archiveOrgDir}/manifest.json")
echo "Archive org: $archiveOrg → importing as: $org_name"
echo ""
imported=0
skipped=0
for srcFile in "${archiveOrgDir}"/*.json "${archiveOrgDir}"/*.jsonl; do
[ -f "$srcFile" ] || continue
dstName=$(basename "$srcFile" | sed "s/^${archiveOrg}/${org_name}/")
dstPath=".monomind/orgs/${dstName}"
[[ "$dstName" == "manifest.json" ]] && continue
[[ "$dstName" == *secret-refs* ]] && echo " SKIP: secret refs (never auto-imported)" && continue
if [ -n "$adapter_override" ] && [[ "$dstName" == *-adapters.json ]]; then
echo " OVERRIDE: applying adapter_override=$adapter_override to adapters file"
fi
if [ -f "$dstPath" ]; then
case "$collision" in
skip)
echo " SKIP: $dstName (exists, strategy=skip)"
skipped=$((skipped + 1))
continue ;;
merge)
echo " MERGE: $dstName"
if [ "$dryRun" = "false" ]; then
if [[ "$dstPath" == *.jsonl ]]; then
cat "$srcFile" >> "$dstPath"
else
python3 -c "
import json, sys
with open('$dstPath') as f: existing = json.load(f)
with open('$srcFile') as f: incoming = json.load(f)
for k, v in incoming.items():
if isinstance(v, list) and isinstance(existing.get(k), list):
existIds = {i.get('id') for i in existing[k] if isinstance(i,dict)}
existing[k] += [i for i in v if isinstance(i,dict) and i.get('id') not in existIds]
elif k not in existing:
existing[k] = v
with open('$dstPath', 'w') as f: json.dump(existing, f, indent=2)
" 2>/dev/null || cp "$srcFile" "$dstPath"
fi
fi ;;
overwrite)
echo " OVERWRITE: $dstName"
[ "$dryRun" = "false" ] && cp "$srcFile" "$dstPath" ;;
esac
else
echo " CREATE: $dstName (new)"
[ "$dryRun" = "false" ] && cp "$srcFile" "$dstPath"
fi
imported=$((imported + 1))
done
rm -rf "$tmpDir"
echo ""
[ "$dryRun" = "true" ] && echo "DRY RUN complete — $imported file(s) would be imported, $skipped skipped." \
|| echo "Import complete — $imported file(s) imported, $skipped skipped."
list-archives
echo "EXPORT ARCHIVES — org: $org_name"
echo "────────────────────────────────────────────────────────"
found=0
for f in ".monomind/exports/${org_name}-"*.tar.gz ".monomind/exports/${org_name}-"*.zip; do
[ -f "$f" ] || continue
size=$(du -sh "$f" 2>/dev/null | cut -f1 || echo "?")
ts=$(stat -f "%Sm" -t "%Y-%m-%d %H:%M" "$f" 2>/dev/null || stat -c "%y" "$f" 2>/dev/null | cut -c1-16 || echo "?")
printf " %-50s %8s %s\n" "$(basename "$f")" "$size" "$ts"
found=$((found + 1))
done
[ "$found" -eq 0 ] && echo " No archives found. Run --action export to create one."
echo ""
echo "Total: $found archive(s) in .monomind/exports/"
Step 3 — Return Output
domain: ops
status: complete
action: <action>
org: <org_name>
output_path: <path if export>
files_processed: <N>
collision_strategy: <strategy if import>
Step 4 — Brain Write (standalone only)
If caller is not "command", follow mastermind-protocol/SKILL.md Brain Write Procedure for domain ops.