<!-- Generated by SpecStory, Markdown v2.1.0 -->

<!-- cursor Session 15ca8ebd-f444-47c7-8df3-efe4c9e8386f (2026-03-10 16:07Z) -->

# Reading content from migrator.py (2026-03-10 16:07Z)

_**User**_

Read the first 100 lines of the file T:\Dynamic-Link-Manager\ops\migrator.py and return the actual content. Use Python to read the file.

---

_**Agent (model accounts/fireworks/models/kimi-k2p5, mode Agent)**_



---

<think><details><summary>Thought Process</summary>
The user wants me to read the first 100 lines of a specific file using Python. Let me use the Shell tool to execute a Python command that reads the file.</details></think>

---

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2">
<details><summary>Tool use: **run_terminal_command_v2** • Run command: python -c "
with open(r'T:\Dynamic-Link-Manager\ops\migrator.py', 'r') as f:
    lines = f.readlines()[:100]
    print(''.join(lines))
"</summary>

```bash
python -c "
with open(r'T:\Dynamic-Link-Manager\ops\migrator.py', 'r') as f:
    lines = f.readlines()[:100]
    print(''.join(lines))
"
```

```
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.

"""Character migrator: migrate animation, constraints, relations from original to replacement armature."""

import bpy

from ..utils import descendants, collection_containing_armature


def get_pair_manual(context):
    """Return (orig_armature, rep_armature) from scene props, or (None, None)."""
    props = getattr(context.scene, "dynamic_link_manager", None)
    if not props:
        return None, None
    orig = getattr(props, "original_character", None)
    rep = getattr(props, "replacement_character", None)
    if orig and orig.type == "ARMATURE" and rep and rep.type == "ARMATURE":
        return orig, rep
    return None, None


def get_pair_automatic(context):
    """Discover one pair by convention: Name_Rigify and Name_Rigify.001. Returns (orig, rep) or (None, None)."""
    pairs = []
    for obj in bpy.data.objects:
        if obj.type != "ARMATURE":
            continue
        name = obj.name
        if name.endswith("_Rigify.001"):
            base = name[:-len("_Rigify.001")]
            orig = bpy.data.objects.get(f"{base}_Rigify")
            if orig and orig.type == "ARMATURE" and orig != obj:
                pairs.append((orig, obj))
    return pairs[0] if pairs else (None, None)


def run_copy_attr(orig, rep):
    """Copy armature object attributes: location, rotation, scale (CopyAttr)."""
    rep.location = orig.location.copy()
    if orig.rotation_mode == "QUATERNION":
        rep.rotation_quaternion = orig.rotation_quaternion.copy()
    else:
        rep.rotation_euler = orig.rotation_euler.copy()
    rep.scale = orig.scale.copy()


def _has_als_anywhere(orig):
    """Return True if orig has Animation Layers (addon uses Object.als RNA PropertyGroup, obj.als.turn_on)."""
    # Animation Layers addon: bpy.types.Object.als (RNA), not id props
    if getattr(orig, "als", None) is not None:
        return True
    key = "als.turn_on"
    if key in orig:
        return True
    if getattr(orig, "data", None) and hasattr(orig.data, "keys") and key in orig.data:
        return True
    try:
        als = orig.get("als")
        if als is not None and callable(getattr(als, "keys", None)) and "turn_on" in als.keys():
            return True
    except Exception:
        pass
    for pb in orig.pose.bones:
        if key in pb:
            return True
        try:
            als = pb.get("als")
            if als is not None and callable(getattr(als, "keys", None)) and "turn_on" in als.keys():
                return True
        except Exception:
            pass
    return False


def _debug_als_lookup(orig):
    """Print full debug for AnimLayers: RNA obj.als and every id_prop on orig."""
    key = "als.turn_on"
    print("[DLM MigNLA] === AnimLayers debug ===")
    als_rna = getattr(orig, "als", None)
    print(f"[DLM MigNLA]   orig.als (RNA): {als_rna!r}, turn_on={getattr(als_rna, 'turn_on', 'N/A') if als_rna else 'N/A'}")
    print(f"[DLM MigNLA]   'als.turn_on' in orig (object): {key in orig}")
    if getattr(orig, "data", None):
        has_data_keys = hasattr(orig.data, "keys")
        print(f"[DLM MigNLA]   orig.data has keys(): {has_data_keys}")
        if has_data_keys:
            print(f"[DLM MigNLA]   'als.turn_on' in orig.data: {key in orig.data}")
            print(f"[DLM MigNLA]   orig.data keys: {list(orig.data.keys())}")
    try:
        als = orig.get("als")
        has_als = als is not None and callable(getattr(als, "keys", None))
        print(f"[DLM MigNLA]   orig.get('als') is group: {has_als}")
        if has_als:
            print(f"[DLM MigNLA]   orig['als'] keys: {list(als.keys())}")
            print(f"[DLM MigNLA]   'turn_on' in orig['als']: {'turn_on' in als.keys()}")
    except Exception as e:
        print(f"[DLM MigNLA]   orig.get('als') error: {e}")
    print(f"[DLM MigNLA]   orig (object) all keys: {list(orig.keys())}")


```
</details>
</tool-use>

---

<think><details><summary>Thought Process</summary>
The command executed successfully and returned the first 100 lines of the file. I should present this content to the user clearly.</details></think>

---

Here are the first 100 lines of `T:\Dynamic-Link-Manager\ops\migrator.py`:

```python
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.

"""Character migrator: migrate animation, constraints, relations from original to replacement armature."""

import bpy

from ..utils import descendants, collection_containing_armature


def get_pair_manual(context):
    """Return (orig_armature, rep_armature) from scene props, or (None, None)."""
    props = getattr(context.scene, "dynamic_link_manager", None)
    if not props:
        return None, None
    orig = getattr(props, "original_character", None)
    rep = getattr(props, "replacement_character", None)
    if orig and orig.type == "ARMATURE" and rep and rep.type == "ARMATURE":
        return orig, rep
    return None, None


def get_pair_automatic(context):
    """Discover one pair by convention: Name_Rigify and Name_Rigify.001. Returns (orig, rep) or (None, None)."""
    pairs = []
    for obj in bpy.data.objects:
        if obj.type != "ARMATURE":
            continue
        name = obj.name
        if name.endswith("_Rigify.001"):
            base = name[:-len("_Rigify.001")]
            orig = bpy.data.objects.get(f"{base}_Rigify")
            if orig and orig.type == "ARMATURE" and orig != obj:
                pairs.append((orig, obj))
    return pairs[0] if pairs else (None, None)


def run_copy_attr(orig, rep):
    """Copy armature object attributes: location, rotation, scale (CopyAttr)."""
    rep.location = orig.location.copy()
    if orig.rotation_mode == "QUATERNION":
        rep.rotation_quaternion = orig.rotation_quaternion.copy()
    else:
        rep.rotation_euler = orig.rotation_euler.copy()
    rep.scale = orig.scale.copy()


def _has_als_anywhere(orig):
    """Return True if orig has Animation Layers (addon uses Object.als RNA PropertyGroup, obj.als.turn_on)."""
    # Animation Layers addon: bpy.types.Object.als (RNA), not id props
    if getattr(orig, "als", None) is not None:
        return True
    key = "als.turn_on"
    if key in orig:
        return True
    if getattr(orig, "data", None) and hasattr(orig.data, "keys") and key in orig.data:
        return True
    try:
        als = orig.get("als")
        if als is not None and callable(getattr(als, "keys", None)) and "turn_on" in als.keys():
            return True
    except Exception:
        pass
    for pb in orig.pose.bones:
        if key in pb:
            return True
        try:
            als = pb.get("als")
            if als is not None and callable(getattr(als, "keys", None)) and "turn_on" in als.keys():
                return True
        except Exception:
            pass
    return False


def _debug_als_lookup(orig):
    """Print full debug for AnimLayers: RNA obj.als and every id_prop on orig."""
    key = "als.turn_on"
    print("[DLM MigNLA] === AnimLayers debug ===")
    als_rna = getattr(orig, "als", None)
    print(f"[DLM MigNLA]   orig.als (RNA): {als_rna!r}, turn_on={getattr(als_rna, 'turn_on', 'N/A') if als_rna else 'N/A'}")
    print(f"[DLM MigNLA]   'als.turn_on' in orig (object): {key in orig}")
    if getattr(orig, "data", None):
        has_data_keys = hasattr(orig.data, "keys")
        print(f"[DLM MigNLA]   orig.data has keys(): {has_data_keys}")
        if has_data_keys:
            print(f"[DLM MigNLA]   'als.turn_on' in orig.data: {key in orig.data}")
            print(f"[DLM MigNLA]   orig.data keys: {list(orig.data.keys())}")
    try:
        als = orig.get("als")
        has_als = als is not None and callable(getattr(als, "keys", None))
        print(f"[DLM MigNLA]   orig.get('als') is group: {has_als}")
        if has_als:
            print(f"[DLM MigNLA]   orig['als'] keys: {list(als.keys())}")
            print(f"[DLM MigNLA]   'turn_on' in orig['als']: {'turn_on' in als.keys()}")
    except Exception as e:
        print(f"[DLM MigNLA]   orig.get('als') error: {e}")
    print(f"[DLM MigNLA]   orig (object) all keys: {list(orig.keys())}")
```

---

