
Agent Import
- 169 installs
- 21 repo stars
- Updated August 3, 2026
- starchild-ai-agent/official-skills
Helps with ai & agent building tasks during AI-assisted development.
About
agent-import is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- agent-import
- AI & Agent Building
- AI-coding skill
Agent Import by the numbers
- 169 all-time installs (skills.sh)
- +10 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #3,144 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/starchild-ai-agent/official-skills --skill agent-importAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 169 |
|---|---|
| repo stars | ★ 21 |
| Last updated | August 3, 2026 |
| Repository | starchild-ai-agent/official-skills ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
Agent Import — Migration Bundle Loader
Download and load a migration bundle into this Starchild agent. The bundle is created by another agent using the agent-export skill.
Quick Start
When user provides a migration code and download token:
1. Run the import script to download & extract
2. Review the extracted data with the user
3. Apply each component using native toolsStep 1 — Download & Extract
The user will provide two values from the source agent: a CODE (8 chars) and a DOWNLOAD_TOKEN.
python3 skills/agent-import/scripts/download.py <CODE> <DOWNLOAD_TOKEN>The script downloads through the Fly internal network only (sc-agent-migration.internal). Public download is forbidden by relay policy. If the internal network is unreachable, import fails and must be retried from a Fly machine. The download token authorizes the download; it is single-use and expires with the code (1 hour TTL).
On success the script extracts the bundle to migration/ and prints a summary of what's included.
Step 2 — Review Contents
Read and summarize what the bundle contains before applying anything:
cat migration/manifest.json
cat migration/memory/agent.json 2>/dev/null
cat migration/memory/user.json 2>/dev/null
cat migration/identity/profile.json 2>/dev/null
cat migration/identity/soul.md 2>/dev/null
cat migration/user/settings.json 2>/dev/null
cat migration/tasks/tasks.json 2>/dev/null
cat migration/env/keys.json 2>/dev/null
find migration/files/ -type f 2>/dev/nullAlways show the user a summary and ask for confirmation before applying.
Step 3 — Apply Components
Apply each component using Starchild native tools. The order matters:
3a. User Settings (first — sets timezone/language for everything else)
Read migration/user/settings.json, then call:
user_settings(action="update", settings={
"name": "...",
"what_to_call": "...",
"timezone": "...",
"language": "..."
})Only include fields that are present in the JSON.
3b. Agent Identity
Read migration/identity/profile.json, then call:
agent_profile(action="update", profile={
"name": "...",
"vibe": "...",
"emoji": "...",
"creature": "..."
})3c. SOUL.md
If migration/identity/soul.md exists, read it and write to prompt/SOUL.md.
Merge with existing SOUL.md if it has content. Don't overwrite platform defaults blindly — integrate the personality bits.
3d. Memory — Agent
Read migration/memory/agent.json, for each entry call:
memory(action="add", target="memory", content="<entry>")⚠️ Memory has a 5000 char limit. If the bundle has many entries, prioritize the most useful ones. Check current usage with memory(action="read") first.
3e. Memory — User
Read migration/memory/user.json, for each entry call:
memory(action="add", target="user", content="<entry>")⚠️ User memory has a 3000 char limit. Prioritize preferences and corrections.
3f. Tasks
Read migration/tasks/tasks.json, for each task:
1. scheduled_task(action="register", title=..., schedule=..., description=..., channels=...) 2. Write the run.py script based on the description 3. Test it: bash("python3 tasks/{job_id}/run.py") 4. scheduled_task(action="activate", job_id=...)
Tasks need actual implementation — the description is a spec, not runnable code. Use your judgment to build each task's script.
3g. Environment Keys
Read migration/env/keys.json, then call:
request_env_input(env_vars=[...], reason="Migration from <source>")This prompts the user to enter values securely.
3h. Files
Copy files from migration/files/ to the workspace:
cp -r migration/files/* . 2>/dev/nullReview what's being copied and skip anything that would overwrite important existing files.
Step 4 — Cleanup
rm -rf migration/ migration-bundle.tar.gzError Handling
| Error | Cause | Fix |
|---|---|---|
401 unauthorized | Wrong or missing download token | Re-check the token from the source agent output |
404 not found | Code already used or never existed | Ask source agent to re-export |
410 expired | Code older than 1 hour | Ask source agent to re-export |
403 internal_only | Download attempted from non-Fly-internal network | Run import from a Fly machine / internal network |
429 rate limited | Too many failed attempts | Wait 1 hour |
| Invalid tar.gz | Corrupted upload | Re-export from source agent |
| No manifest.json | Invalid bundle structure | Bundle must have manifest.json at root |
#!/usr/bin/env python3
"""Download and extract a migration bundle from the relay.
Usage:
python3 download.py <CODE> <DOWNLOAD_TOKEN>
Both CODE and DOWNLOAD_TOKEN are printed by the source agent after export.
Download is internal-only: relay accepts requests from Fly internal network only.
"""
import json
import os
import sys
import tarfile
import tempfile
import urllib.request
from pathlib import Path
# Relay download is internal-only. Requests must originate from Fly internal
# IPv6 (fdaa::/16), otherwise relay returns 403.
RELAY_INTERNAL = "http://sc-agent-migration.internal:8080"
WORKSPACE = Path("/data/workspace")
EXTRACT_DIR = WORKSPACE / "migration"
# Max bundle size: 50MB
MAX_SIZE = 50 * 1024 * 1024
def _do_request(url: str, download_token: str, timeout: int) -> bytes:
"""Make a single GET request. Raises urllib.error.* on failure."""
req = urllib.request.Request(
url,
headers={"X-Download-Token": download_token},
method="GET",
)
with urllib.request.urlopen(req, timeout=timeout) as resp:
return resp.read(MAX_SIZE + 1)
def download_bundle(code: str, download_token: str) -> bytes:
"""Download bundle from relay using the download token.
Internal-only mode: relay accepts download requests from Fly internal
network only. The request must reach `sc-agent-migration.internal`.
"""
internal_url = f"{RELAY_INTERNAL}/paste/{code}"
try:
data = _do_request(internal_url, download_token, timeout=20)
print("(downloaded via Fly internal network)")
except urllib.error.HTTPError as e:
body = e.read().decode(errors="replace")
try:
msg = json.loads(body).get("message", body)
except Exception:
msg = body
if e.code == 401:
print(f"ERROR: Invalid download token — {msg}", file=sys.stderr)
elif e.code == 403:
print("ERROR: Relay is internal-only. Run import from a Fly machine.", file=sys.stderr)
elif e.code == 404:
print(f"ERROR: Code not found — expired or already used.", file=sys.stderr)
elif e.code == 410:
print(f"ERROR: Code expired — ask the source agent for a new export.", file=sys.stderr)
elif e.code == 429:
print(f"ERROR: Rate limited — too many failed attempts.", file=sys.stderr)
else:
print(f"ERROR: HTTP {e.code}: {msg}", file=sys.stderr)
sys.exit(1)
except (urllib.error.URLError, OSError) as e:
print(
"ERROR: Cannot reach Fly internal relay endpoint. "
"Run import inside a Fly machine network.",
file=sys.stderr,
)
print(f"DETAIL: {e}", file=sys.stderr)
sys.exit(1)
if len(data) > MAX_SIZE:
print(f"ERROR: Bundle too large ({len(data)} bytes, max {MAX_SIZE}).", file=sys.stderr)
sys.exit(1)
return data
def validate_and_extract(data: bytes) -> dict:
"""Validate tar.gz and extract to migration/."""
if EXTRACT_DIR.exists():
import shutil
shutil.rmtree(EXTRACT_DIR)
with tempfile.NamedTemporaryFile(suffix=".tar.gz", delete=False) as f:
f.write(data)
tmp_path = f.name
try:
with tarfile.open(tmp_path, "r:gz") as tar:
# Security: check for path traversal
for member in tar.getmembers():
if member.name.startswith("/") or ".." in member.name:
print(f"ERROR: Dangerous path in archive: {member.name}", file=sys.stderr)
sys.exit(1)
EXTRACT_DIR.mkdir(parents=True, exist_ok=True)
tar.extractall(EXTRACT_DIR, filter="data")
except tarfile.TarError as e:
print(f"ERROR: Invalid tar.gz archive: {e}", file=sys.stderr)
sys.exit(1)
finally:
os.unlink(tmp_path)
# Validate manifest
manifest_path = EXTRACT_DIR / "manifest.json"
if not manifest_path.exists():
print("ERROR: No manifest.json found in bundle root.", file=sys.stderr)
sys.exit(1)
try:
manifest = json.loads(manifest_path.read_text())
except json.JSONDecodeError as e:
print(f"ERROR: Invalid manifest.json: {e}", file=sys.stderr)
sys.exit(1)
if "version" not in manifest:
print("ERROR: manifest.json missing 'version' field.", file=sys.stderr)
sys.exit(1)
return manifest
def summarize(manifest: dict):
"""Print a summary of the extracted bundle."""
print(f"✅ Bundle downloaded and extracted to migration/")
print(f" Source: {manifest.get('source', 'unknown')}")
print(f" Version: {manifest.get('version')}")
if desc := manifest.get("description"):
print(f" Desc: {desc}")
print()
components = []
agent_mem = EXTRACT_DIR / "memory" / "agent.json"
if agent_mem.exists():
data = json.loads(agent_mem.read_text())
n = len(data.get("entries", []))
components.append(f" 📝 Agent memory: {n} entries")
user_mem = EXTRACT_DIR / "memory" / "user.json"
if user_mem.exists():
data = json.loads(user_mem.read_text())
n = len(data.get("entries", []))
components.append(f" 👤 User memory: {n} entries")
profile = EXTRACT_DIR / "identity" / "profile.json"
if profile.exists():
data = json.loads(profile.read_text())
fields = [k for k in ["name", "vibe", "emoji", "creature"] if data.get(k)]
components.append(f" 🎭 Identity: {', '.join(fields)}")
soul = EXTRACT_DIR / "identity" / "soul.md"
if soul.exists():
lines = len(soul.read_text().strip().splitlines())
components.append(f" 💫 Soul: {lines} lines")
settings = EXTRACT_DIR / "user" / "settings.json"
if settings.exists():
data = json.loads(settings.read_text())
fields = [k for k in ["name", "timezone", "language", "what_to_call"] if data.get(k)]
components.append(f" ⚙️ User settings: {', '.join(fields)}")
tasks = EXTRACT_DIR / "tasks" / "tasks.json"
if tasks.exists():
data = json.loads(tasks.read_text())
n = len(data.get("tasks", []))
components.append(f" ⏰ Tasks: {n} scheduled tasks")
env_keys = EXTRACT_DIR / "env" / "keys.json"
if env_keys.exists():
data = json.loads(env_keys.read_text())
n = len(data.get("keys", []))
components.append(f" 🔑 Env keys: {n} variables needed")
files_dir = EXTRACT_DIR / "files"
if files_dir.exists():
files = list(files_dir.rglob("*"))
file_count = sum(1 for f in files if f.is_file())
if file_count:
components.append(f" 📁 Files: {file_count} files")
if components:
print("Components found:")
print("\n".join(components))
else:
print("⚠️ Bundle contains only manifest — no data components found.")
def main():
if len(sys.argv) != 3:
print("Usage: python3 download.py <CODE> <DOWNLOAD_TOKEN>", file=sys.stderr)
print(" CODE: 8-character migration code", file=sys.stderr)
print(" DOWNLOAD_TOKEN: token returned at upload time", file=sys.stderr)
sys.exit(1)
code = sys.argv[1].strip().upper()
download_token = sys.argv[2].strip()
if len(code) != 8:
print("ERROR: Code must be exactly 8 characters.", file=sys.stderr)
sys.exit(1)
if not download_token:
print("ERROR: Download token cannot be empty.", file=sys.stderr)
sys.exit(1)
print(f"Downloading bundle (code: {code}) ...")
data = download_bundle(code, download_token)
print(f"Downloaded {len(data):,} bytes")
manifest = validate_and_extract(data)
summarize(manifest)
if __name__ == "__main__":
main()