unityflow 0.9.0__tar.gz → 0.9.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. {unityflow-0.9.0/src/unityflow.egg-info → unityflow-0.9.2}/PKG-INFO +1 -1
  2. {unityflow-0.9.0 → unityflow-0.9.2}/pyproject.toml +1 -1
  3. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/asset_resolver.py +93 -25
  4. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/cli.py +87 -49
  5. {unityflow-0.9.0 → unityflow-0.9.2/src/unityflow.egg-info}/PKG-INFO +1 -1
  6. {unityflow-0.9.0 → unityflow-0.9.2}/LICENSE +0 -0
  7. {unityflow-0.9.0 → unityflow-0.9.2}/README.md +0 -0
  8. {unityflow-0.9.0 → unityflow-0.9.2}/setup.cfg +0 -0
  9. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/__init__.py +0 -0
  10. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/animation/__init__.py +0 -0
  11. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/animation/cli.py +0 -0
  12. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/animation/models.py +0 -0
  13. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/animation/mutate.py +0 -0
  14. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/animation/parser.py +0 -0
  15. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/animation/query.py +0 -0
  16. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/animation/writer.py +0 -0
  17. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/animator/__init__.py +0 -0
  18. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/animator/cli.py +0 -0
  19. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/animator/models.py +0 -0
  20. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/animator/parser.py +0 -0
  21. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/animator/query.py +0 -0
  22. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/animator/writer.py +0 -0
  23. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/asset_tracker.py +0 -0
  24. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/bridge/__init__.py +0 -0
  25. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/bridge/config.py +0 -0
  26. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/bridge/server.py +0 -0
  27. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/bridge/unity_client.py +0 -0
  28. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/builtin_schema.py +0 -0
  29. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/data/__init__.py +0 -0
  30. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/data/builtin_schemas.json +0 -0
  31. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/data/class_ids.json +0 -0
  32. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/dll_inspector.py +0 -0
  33. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/fast_parser.py +0 -0
  34. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/fbx_loader.py +0 -0
  35. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/formats.py +0 -0
  36. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/git_utils.py +0 -0
  37. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/hierarchy.py +0 -0
  38. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/merge.py +0 -0
  39. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/meta_generator.py +0 -0
  40. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/normalizer.py +0 -0
  41. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/parser.py +0 -0
  42. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/query.py +0 -0
  43. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/script_parser.py +0 -0
  44. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/semantic_diff.py +0 -0
  45. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/semantic_merge.py +0 -0
  46. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/sprite.py +0 -0
  47. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow/validator.py +0 -0
  48. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow.egg-info/SOURCES.txt +0 -0
  49. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow.egg-info/dependency_links.txt +0 -0
  50. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow.egg-info/entry_points.txt +0 -0
  51. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow.egg-info/requires.txt +0 -0
  52. {unityflow-0.9.0 → unityflow-0.9.2}/src/unityflow.egg-info/top_level.txt +0 -0
  53. {unityflow-0.9.0 → unityflow-0.9.2}/tests/test_asset_resolver.py +0 -0
  54. {unityflow-0.9.0 → unityflow-0.9.2}/tests/test_asset_tracker.py +0 -0
  55. {unityflow-0.9.0 → unityflow-0.9.2}/tests/test_bridge.py +0 -0
  56. {unityflow-0.9.0 → unityflow-0.9.2}/tests/test_cli.py +0 -0
  57. {unityflow-0.9.0 → unityflow-0.9.2}/tests/test_dll_inspector.py +0 -0
  58. {unityflow-0.9.0 → unityflow-0.9.2}/tests/test_fbx_loader.py +0 -0
  59. {unityflow-0.9.0 → unityflow-0.9.2}/tests/test_formats.py +0 -0
  60. {unityflow-0.9.0 → unityflow-0.9.2}/tests/test_hierarchy.py +0 -0
  61. {unityflow-0.9.0 → unityflow-0.9.2}/tests/test_incremental.py +0 -0
  62. {unityflow-0.9.0 → unityflow-0.9.2}/tests/test_llm_helpers.py +0 -0
  63. {unityflow-0.9.0 → unityflow-0.9.2}/tests/test_merge.py +0 -0
  64. {unityflow-0.9.0 → unityflow-0.9.2}/tests/test_meta_generator.py +0 -0
  65. {unityflow-0.9.0 → unityflow-0.9.2}/tests/test_normalizer.py +0 -0
  66. {unityflow-0.9.0 → unityflow-0.9.2}/tests/test_parser.py +0 -0
  67. {unityflow-0.9.0 → unityflow-0.9.2}/tests/test_query.py +0 -0
  68. {unityflow-0.9.0 → unityflow-0.9.2}/tests/test_script_parser.py +0 -0
  69. {unityflow-0.9.0 → unityflow-0.9.2}/tests/test_semantic_diff.py +0 -0
  70. {unityflow-0.9.0 → unityflow-0.9.2}/tests/test_semantic_merge.py +0 -0
  71. {unityflow-0.9.0 → unityflow-0.9.2}/tests/test_validator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unityflow
3
- Version: 0.9.0
3
+ Version: 0.9.2
4
4
  Summary: Unity workflow automation - edit, diff, and merge prefabs, scenes, and assets without the Unity Editor
5
5
  Author: unityflow contributors
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "unityflow"
7
- version = "0.9.0"
7
+ version = "0.9.2"
8
8
  description = "Unity workflow automation - edit, diff, and merge prefabs, scenes, and assets without the Unity Editor"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -621,35 +621,28 @@ def resolve_internal_reference(
621
621
  ) -> dict[str, int]:
622
622
  """Resolve an internal # reference to a fileID dict.
623
623
 
624
- Args:
625
- value: Internal reference string (e.g., "#Player/Child/Button")
626
- doc: UnityYAMLDocument instance
627
- hierarchy: Hierarchy instance built from the document
628
-
629
- Returns:
630
- Dict with fileID (e.g., {"fileID": 12345})
631
-
632
- Raises:
633
- ValueError: If the reference cannot be resolved
624
+ Resolution strategy:
625
+ 1. Try the full path as a GameObject path
626
+ 2. If not found, split last segment as component type on its parent
634
627
  """
635
- ref_path, component_type = parse_internal_reference(value)
628
+ raw_path = value[1:] if value.startswith("#") else value
636
629
 
637
- target_node = hierarchy.find(ref_path)
638
- if target_node is None:
639
- raise ValueError(f"Internal reference not found: {ref_path}")
630
+ target_node = hierarchy.find(raw_path)
631
+ if target_node is not None:
632
+ return {"fileID": target_node.file_id}
640
633
 
641
- if component_type:
642
- target_comp = None
643
- for comp in target_node.components:
644
- comp_name = comp.script_name or comp.class_name
645
- if comp_name == component_type:
646
- target_comp = comp
647
- break
648
- if target_comp is None:
649
- raise ValueError(f"Component '{component_type}' not found on '{ref_path}'")
650
- return {"fileID": target_comp.file_id}
634
+ parts = raw_path.rsplit("/", 1)
635
+ if len(parts) == 2:
636
+ parent_path, comp_name = parts
637
+ parent_node = hierarchy.find(parent_path)
638
+ if parent_node is not None:
639
+ for comp in parent_node.components:
640
+ name = comp.script_name or comp.class_name
641
+ if name == comp_name:
642
+ return {"fileID": comp.file_id}
643
+ raise ValueError(f"Component '{comp_name}' not found on '{parent_path}'")
651
644
 
652
- return {"fileID": target_node.file_id}
645
+ raise ValueError(f"Internal reference not found: {raw_path}")
653
646
 
654
647
 
655
648
  def resolve_value(
@@ -765,3 +758,78 @@ def get_asset_info(
765
758
  pass
766
759
 
767
760
  return info
761
+
762
+
763
+ def _is_reference_dict(value: Any) -> bool:
764
+ return isinstance(value, dict) and "fileID" in value
765
+
766
+
767
+ def _build_sprite_id_to_name(meta_path: Path) -> dict[int, str]:
768
+ from unityflow.sprite import parse_sprite_meta
769
+
770
+ info = parse_sprite_meta(meta_path)
771
+ if info is None or not info.is_multiple:
772
+ return {}
773
+ return {v: k for k, v in info.internal_id_table.items()}
774
+
775
+
776
+ def _humanize_single_reference(
777
+ ref: dict[str, Any],
778
+ guid_index: Any,
779
+ hierarchy: Any | None,
780
+ project_root: Path | None,
781
+ ) -> Any:
782
+ file_id = ref.get("fileID", 0)
783
+ guid = ref.get("guid", "")
784
+
785
+ if file_id == 0 and not guid:
786
+ return ""
787
+
788
+ if guid:
789
+ asset_path = guid_index.get_path(guid) if guid_index else None
790
+ if asset_path is None:
791
+ return ref
792
+
793
+ suffix = asset_path.suffix.lower()
794
+ is_sprite_ext = suffix in (".png", ".jpg", ".jpeg", ".tga", ".psd", ".tiff", ".gif", ".bmp")
795
+ if is_sprite_ext and file_id != 21300000 and project_root:
796
+ meta_path = project_root / str(asset_path) / ".." / (asset_path.name + ".meta")
797
+ meta_path = (project_root / str(asset_path)).with_suffix(asset_path.suffix + ".meta")
798
+ id_to_name = _build_sprite_id_to_name(meta_path)
799
+ sprite_name = id_to_name.get(file_id)
800
+ if sprite_name:
801
+ return f"@{asset_path}:{sprite_name}"
802
+
803
+ return f"@{asset_path}"
804
+
805
+ if file_id and hierarchy:
806
+ ref_node = hierarchy._nodes_by_file_id.get(file_id)
807
+ if ref_node:
808
+ return f"#{ref_node.path}"
809
+
810
+ for n in hierarchy.iter_all():
811
+ for c in n.components:
812
+ if c.file_id == file_id:
813
+ return f"#{n.path}/{c.script_name or c.class_name}"
814
+
815
+ return ref
816
+
817
+ return ref
818
+
819
+
820
+ def humanize_references(
821
+ value: Any,
822
+ guid_index: Any,
823
+ hierarchy: Any | None = None,
824
+ project_root: Path | None = None,
825
+ ) -> Any:
826
+ if _is_reference_dict(value):
827
+ return _humanize_single_reference(value, guid_index, hierarchy, project_root)
828
+
829
+ if isinstance(value, dict):
830
+ return {k: humanize_references(v, guid_index, hierarchy, project_root) for k, v in value.items()}
831
+
832
+ if isinstance(value, list):
833
+ return [humanize_references(item, guid_index, hierarchy, project_root) for item in value]
834
+
835
+ return value
@@ -986,6 +986,18 @@ def _build_disambiguation_message(
986
986
  return "\n".join(error_lines)
987
987
 
988
988
 
989
+ def _resolve_script_name(guid_index, script_guid: str, script_file_id: int | None = None) -> str | None:
990
+ resolved = guid_index.resolve_name(script_guid)
991
+ if resolved:
992
+ path = guid_index.get_path(script_guid)
993
+ if path and path.suffix.lower() == ".dll" and script_file_id is not None:
994
+ dll_name = guid_index.resolve_dll_class_name(script_guid, script_file_id)
995
+ if dll_name:
996
+ return dll_name
997
+ return resolved
998
+ return None
999
+
1000
+
989
1001
  def _resolve_component_path(
990
1002
  doc: UnityYAMLDocument,
991
1003
  path_spec: str,
@@ -1089,7 +1101,11 @@ def _resolve_component_path(
1089
1101
  comp_content = comp.get_content()
1090
1102
  if comp_content:
1091
1103
  script_ref = comp_content.get("m_Script", {})
1092
- script_guid = script_ref.get("guid", "") if isinstance(script_ref, dict) else ""
1104
+ if isinstance(script_ref, dict):
1105
+ script_guid = script_ref.get("guid", "")
1106
+ script_fid = script_ref.get("fileID")
1107
+ else:
1108
+ script_guid, script_fid = "", None
1093
1109
  if script_guid:
1094
1110
  for key, guid in PACKAGE_COMPONENT_GUIDS.items():
1095
1111
  if key.lower() == last_component_type_lower and script_guid.lower() == guid.lower():
@@ -1097,7 +1113,7 @@ def _resolve_component_path(
1097
1113
  matched = True
1098
1114
  break
1099
1115
  if not matched and guid_index:
1100
- resolved = guid_index.resolve_name(script_guid)
1116
+ resolved = _resolve_script_name(guid_index, script_guid, script_fid)
1101
1117
  if resolved and resolved.lower() == last_component_type_lower:
1102
1118
  matching_components.append(comp_id)
1103
1119
 
@@ -1178,7 +1194,11 @@ def _resolve_component_path(
1178
1194
  comp_content = comp.get_content()
1179
1195
  if comp_content:
1180
1196
  script_ref = comp_content.get("m_Script", {})
1181
- script_guid = script_ref.get("guid", "") if isinstance(script_ref, dict) else ""
1197
+ if isinstance(script_ref, dict):
1198
+ script_guid = script_ref.get("guid", "")
1199
+ script_fid = script_ref.get("fileID")
1200
+ else:
1201
+ script_guid, script_fid = "", None
1182
1202
  if script_guid:
1183
1203
  for key, guid in PACKAGE_COMPONENT_GUIDS.items():
1184
1204
  if key.lower() == component_type_lower and script_guid.lower() == guid.lower():
@@ -1186,7 +1206,7 @@ def _resolve_component_path(
1186
1206
  matched = True
1187
1207
  break
1188
1208
  if not matched and guid_index:
1189
- resolved = guid_index.resolve_name(script_guid)
1209
+ resolved = _resolve_script_name(guid_index, script_guid, script_fid)
1190
1210
  if resolved and resolved.lower() == component_type_lower:
1191
1211
  matching_components.append(comp_id)
1192
1212
 
@@ -1245,6 +1265,7 @@ def _handle_add_component(
1245
1265
  output: Path | None,
1246
1266
  project_root: Path | None,
1247
1267
  explicit_script_guid: str | None = None,
1268
+ before: str | None = None,
1248
1269
  ) -> None:
1249
1270
  from unityflow.asset_tracker import get_cached_guid_index
1250
1271
  from unityflow.formats import CLASS_NAME_TO_ID
@@ -1441,7 +1462,30 @@ def _handle_add_component(
1441
1462
  go_content = go_obj.get_content()
1442
1463
  if go_content:
1443
1464
  components = go_content.get("m_Component", [])
1444
- components.append({"component": {"fileID": new_file_id}})
1465
+ new_entry = {"component": {"fileID": new_file_id}}
1466
+ insert_idx = len(components)
1467
+ if before is not None:
1468
+ before_lower = before.lower()
1469
+ for idx, comp_ref in enumerate(components):
1470
+ cid = comp_ref.get("component", {}).get("fileID", 0)
1471
+ comp_obj = doc.get_by_file_id(cid)
1472
+ if comp_obj:
1473
+ comp_name = comp_obj.class_name.lower()
1474
+ if comp_name == before_lower:
1475
+ insert_idx = idx
1476
+ break
1477
+ if comp_obj.class_id == 114 and guid_index:
1478
+ comp_content = comp_obj.get_content()
1479
+ if comp_content:
1480
+ sr = comp_content.get("m_Script", {})
1481
+ if isinstance(sr, dict):
1482
+ sg = sr.get("guid", "")
1483
+ sf = sr.get("fileID")
1484
+ resolved = _resolve_script_name(guid_index, sg, sf) if sg else None
1485
+ if resolved and resolved.lower() == before_lower:
1486
+ insert_idx = idx
1487
+ break
1488
+ components.insert(insert_idx, new_entry)
1445
1489
  go_content["m_Component"] = components
1446
1490
 
1447
1491
  _normalize_and_save(doc, output_path, project_root)
@@ -1850,6 +1894,12 @@ def get_value_cmd(
1850
1894
  default=None,
1851
1895
  help="Script GUID for --add-component (for DLL-based scripts not discoverable by filename)",
1852
1896
  )
1897
+ @click.option(
1898
+ "--before",
1899
+ "before_component",
1900
+ default=None,
1901
+ help="Insert component before this component type (for --add-component ordering)",
1902
+ )
1853
1903
  def set_value_cmd(
1854
1904
  file: Path,
1855
1905
  set_path: str,
@@ -1864,6 +1914,7 @@ def set_value_cmd(
1864
1914
  remove_object: str | None,
1865
1915
  object_type: str,
1866
1916
  script_guid: str | None,
1917
+ before_component: str | None,
1867
1918
  ) -> None:
1868
1919
  """Set a value at a specific path in a Unity YAML file.
1869
1920
 
@@ -1991,7 +2042,9 @@ def set_value_cmd(
1991
2042
 
1992
2043
  # Handle --add-component
1993
2044
  if add_component is not None:
1994
- _handle_add_component(doc, set_path, add_component, output_path, output, project_root, script_guid)
2045
+ _handle_add_component(
2046
+ doc, set_path, add_component, output_path, output, project_root, script_guid, before_component
2047
+ )
1995
2048
  return
1996
2049
 
1997
2050
  # Handle --remove-component
@@ -2787,6 +2840,7 @@ def inspect_cmd(
2787
2840
  import json as json_module
2788
2841
 
2789
2842
  from unityflow import UnityYAMLDocument, build_hierarchy
2843
+ from unityflow.asset_resolver import humanize_references
2790
2844
  from unityflow.asset_tracker import find_unity_project_root, get_lazy_guid_index
2791
2845
 
2792
2846
  # Load document
@@ -2881,38 +2935,36 @@ def inspect_cmd(
2881
2935
  script_path = guid_index.resolve_path(comp.script_guid)
2882
2936
  if script_path:
2883
2937
  comp_data["script_path"] = str(script_path)
2884
- comp_data["properties"] = comp.data
2938
+ filtered = {
2939
+ k: v
2940
+ for k, v in comp.data.items()
2941
+ if k
2942
+ not in {
2943
+ "m_ObjectHideFlags",
2944
+ "m_CorrespondingSourceObject",
2945
+ "m_PrefabInstance",
2946
+ "m_PrefabAsset",
2947
+ "m_GameObject",
2948
+ "m_Script",
2949
+ }
2950
+ }
2951
+ comp_data["properties"] = humanize_references(filtered, guid_index, hier, resolved_project_root)
2885
2952
  result["components"].append(comp_data)
2886
2953
 
2887
2954
  click.echo(json_module.dumps(result, indent=2, default=str))
2888
2955
  else:
2889
- # Helper function to resolve file_id to path
2890
- def resolve_reference(file_id: int, guid: str = "") -> str:
2891
- """Resolve a reference to a human-readable path."""
2892
- if file_id == 0 and not guid:
2893
- return "None"
2894
-
2895
- # Try to resolve internal reference (same file)
2896
- if file_id and not guid:
2897
- ref_node = hier._nodes_by_file_id.get(file_id)
2898
- if ref_node:
2899
- return ref_node.path
2900
-
2901
- # Try to find component by file_id
2902
- for n in hier.iter_all():
2903
- for c in n.components:
2904
- if c.file_id == file_id:
2905
- return f"{n.path}/{c.script_name or c.class_name}"
2906
-
2907
- return f"(internal ref #{file_id})"
2908
-
2909
- # External reference (different asset)
2910
- if guid and guid_index:
2911
- asset_path = guid_index.get_path(guid)
2912
- if asset_path:
2913
- return f"@{asset_path}"
2914
-
2915
- return "(external ref)"
2956
+
2957
+ def format_value(value):
2958
+ resolved = humanize_references(value, guid_index, hier, resolved_project_root)
2959
+ if isinstance(resolved, str):
2960
+ return resolved if resolved else "None"
2961
+ if isinstance(resolved, dict):
2962
+ if "fileID" in resolved:
2963
+ return f"(unresolved ref fileID={resolved['fileID']})"
2964
+ return "{...}"
2965
+ if isinstance(resolved, list):
2966
+ return f"[{len(resolved)} items]"
2967
+ return str(resolved)
2916
2968
 
2917
2969
  # Text output - Inspector-like format
2918
2970
  click.echo(f"GameObject: {node.name}")
@@ -3009,21 +3061,7 @@ def inspect_cmd(
3009
3061
  }
3010
3062
  for key, value in comp.data.items():
3011
3063
  if key not in skip_keys:
3012
- # Format value for display
3013
- if isinstance(value, dict) and "fileID" in value:
3014
- # Reference field - resolve to path
3015
- file_id = value.get("fileID", 0)
3016
- guid = value.get("guid", "")
3017
- resolved = resolve_reference(file_id, guid)
3018
- click.echo(f" {key}: {resolved}")
3019
- elif isinstance(value, dict | list):
3020
- # Complex value - show abbreviated
3021
- if isinstance(value, list):
3022
- click.echo(f" {key}: [{len(value)} items]")
3023
- else:
3024
- click.echo(f" {key}: {{...}}")
3025
- else:
3026
- click.echo(f" {key}: {value}")
3064
+ click.echo(f" {key}: {format_value(value)}")
3027
3065
 
3028
3066
  click.echo()
3029
3067
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unityflow
3
- Version: 0.9.0
3
+ Version: 0.9.2
4
4
  Summary: Unity workflow automation - edit, diff, and merge prefabs, scenes, and assets without the Unity Editor
5
5
  Author: unityflow contributors
6
6
  License: MIT
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes