[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6CBHS9Z0AX33BR7HABYEJF8":3,"skill-same:01M6CBHS9Z0AX33BR7HABYEJF8":60},{"skillNo":4,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":21,"linkOnly":22,"markdownContent":23,"markdownBody":23,"frontMatter":24,"refName":25,"commitSha":26,"skillDirectory":27,"forksCount":13,"repositoryHtmlUrl":28,"repositoryHomepageUrl":6,"ownerHtmlUrl":29,"ownerBlogUrl":6,"ownerDescription":30,"updatedAt":31,"install":32,"resources":45},"01M6CBHS9Z0AX33BR7HABYEJF8","11-inventory-storage",null,"devwinsoft\u002Fdevian","devwinsoft","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F22570160?v=4","01M6C6RR8W05NH8NPKQNCE3Q72","maoshy","01M6C6RRDJ5TJJCZXRTY801YDH",0,"MIT","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian-unity\u002F50-mobile-package\u002F22-inventory-system\u002F11-inventory-storage\u002FSKILL.md",[],[18,19,20],"storage","inventory","11 inventory storage","2026-11-10 10:12:30",false,"# 11-inventory-storage\n\nStatus: ACTIVE\nAppliesTo: v10\n\nInventoryStorage는 InventoryManager의 **live runtime 데이터 컨테이너**이다.\n통화 잔고 \u002F 장비(AbilityItemEquip) \u002F 카드(AbilityItemCard) \u002F 재료(AbilityItemMaterial) \u002F 영웅(AbilityItemHero)을 통합 관리한다.\n\nInventoryStorage는 InventoryManager가 소유하며 `Devian.Samples.MobilePackage` asmdef에 속한다.\n외부 시스템의 public boundary는 `InventoryManager`이고, persistence boundary는 `InventorySnapshot`이다.\n\n---\n\n## 1. InventoryStorage\n\n코드는 실제 `InventoryStorage.cs` 참조. `sealed class`이다.\n\n- `CurrencyBalances` — 내부 `Dictionary\u003CCURRENCY_TYPE, long>` 기반 잔고 상태\n- `Equipments` — `Dictionary\u003Cstring, AbilityItemEquip>` (itemUid → 장비)\n- `Cards` — `Dictionary\u003Cstring, AbilityItemCard>` (item_id → 카드)\n- `Materials` — `Dictionary\u003Cstring, AbilityItemMaterial>` (item_id → 재료)\n- `Heroes` — `Dictionary\u003Cstring, AbilityItemHero>` (item_id → 영웅)\n- `Rentals` — `Dictionary\u003Cstring, long>` (item_id → expiresAtClientUtcMs)\n- `Passes` — `Dictionary\u003Cstring, bool>` (item_id → owned)\n- `GetEquip\u002FAddEquip\u002FRemoveEquip` — 장비 CRUD (key=itemUid)\n- `GetEquipsByItemId` — itemId로 인스턴스 목록 조회\n- `GetCard\u002FAddCard` — 카드 CRUD\n- `GetMaterial\u002FAddMaterial` — 재료 CRUD\n- `GetHero\u002FAddHero` — 영웅 CRUD\n- `SetRental(id, expiresAtClientUtcMs)` \u002F `GetRentalExpiry(id)` \u002F `HasActiveRental(id)` \u002F `GetRentalRemainingMs(id)` \u002F `RemoveRental(id)` — 렌탈 CRUD\n- `SetPass(id, owned)` \u002F `HasPass(id)` \u002F `RemovePass(id)` — 시즌패스 CRUD\n- `TreasureCurrent` — `InventoryTreasureCurrent` (Exp\u002FLevel, sealed POCO)\n- `TreasureCounts` — `Dictionary\u003CITEM_GRADE_TYPE, int>` (grade별 보유 chest count)\n- `GetTreasureCount(gradeType)` \u002F `AddTreasure(gradeType, amount)` \u002F `SetTreasureCount(gradeType, count)` — treasure count CRUD\n- `AddTreasureExp(amount)` — delegates to `TreasureCurrent.Exp`\n- `ResetTreasure(level, exp)` — delegates to `TreasureCurrent.Reset(...)`\n- `CopyFrom(source)` — validated temp state를 live storage에 복사\n- Pass 변경 알림 publish는 `InventoryManager`가 담당한다 (trigger 직접 노출 금지).\n- 초기 보상 지급 트리거는 `InventoryStorage`가 아니라 `RewardManager.FirstInitAsync()`에서 처리한다 (`FirstRewardSettings` source).\n- ~~`ToJson()`~~ — **삭제됨**. [21-savedata-system\u002F43-savedata-json-codec](..\u002F..\u002F21-savedata-system\u002F43-savedata-json-codec\u002FSKILL.md)의 `SaveDataJsonCodec`으로 이전.\n- ~~`FromJson(string json)`~~ — **삭제됨**. [21-savedata-system\u002F43-savedata-json-codec](..\u002F..\u002F21-savedata-system\u002F43-savedata-json-codec\u002FSKILL.md)의 `SaveDataJsonCodec`으로 이전.\n\n### 장비 장착\n\n장비 장착\u002F해제의 저장 로직은 `AbilityItemHero._SetEquip\u002F_RemoveEquip`이 담당한다.\n- 이 경로는 hero loadout metadata와 equip owner metadata만 갱신한다.\n- 이미 다른 hero에 장착된 equip 이동은 `InventoryStorage.Equip()`가 target hero의 장착 가능 여부를 먼저 확인한 뒤 기존 owner를 정리하고 위임하는 경로만 사용한다.\n- equip stat 계산은 `AbilityUnitHero._Equip\u002F_Unequip`에서 수행한다.\nInventoryStorage는 hero\u002Fequip 조회 + 위임하는 **편의 메서드**를 제공한다.\n\n- `RemoveEquip`은 장착 상태면 hero 슬롯 맵까지 함께 정리한 뒤 제거한다.\n- `Equip(heroId, equipSlot, equipUid)` \u002F `Unequip(heroId, equipSlot)` 편의 메서드를 제공한다.\n- slot key는 `EQUIP_SLOT_TYPE`이며 `NONE`은 유효한 장착 slot이 아니다.\n- SaveData inventory JSON의 `heroes[*].equips` key도 `EQUIP_SLOT_TYPE` enum name 문자열을 사용한다. 숫자 slot key serialize는 금지한다.\n- two-handed main 장착에 따른 `HAND_SUB` 자동 해제는 최종적으로 `AbilityItemHero._SetEquip()` 규칙에 따른다.\n\n---\n\n## 2. InventoryManager 관계\n\n| 타입 | 책임 |\n|---|---|\n| `InventoryManager` | InventoryStorage 소유, 타입별 Apply\u002FRevoke\u002FQuery API 제공 |\n| `InventoryStorage` | CurrencyBalances (`Dictionary\u003CCURRENCY_TYPE, long>`), Equipments (itemUid → AbilityItemEquip), Cards (item_id → AbilityItemCard), Materials (item_id → AbilityItemMaterial), Heroes (item_id → AbilityItemHero), SelectedHeroId (`string`), Rentals (item_id → expiresAtClientUtcMs), Passes (item_id → owned), TreasureCurrent (`InventoryTreasureCurrent`), TreasureCounts (ITEM_GRADE_TYPE → int) |\n| `AbilityItemEquip` | OwnerUnitId\u002FOwnerSlotType(별도 필드) + 능력치(UNIT_STAT_TYPE 기반) 관리 |\n| `AbilityItemCard` | 수량(`UNIT_STAT_TYPE.ITEM_AMOUNT`) + 능력치(UNIT_STAT_TYPE 기반) 관리 |\n| `AbilityItemMaterial` | 수량(`UNIT_STAT_TYPE.ITEM_AMOUNT`) + 능력치(UNIT_STAT_TYPE 기반) 관리 |\n| `AbilityItemHero` | 수량(`UNIT_STAT_TYPE.ITEM_AMOUNT`) + 영웅 장비 슬롯(`Dict\u003CEQUIP_SLOT_TYPE, AbilityItemEquip>`) 관리 |\n\n- `InventoryManager`가 `InventoryStorage`를 소유한다 (싱글톤 등록 안 함).\n- 장비는 `itemUid`(GUID)를 pk로 관리한다. 같은 `item_id`에 여러 인스턴스가 존재할 수 있다.\n- `ApplyCard(item_id, amount)` \u002F `AddCardAmount(item_id, delta)` — `_storage.Cards`에 AbilityItemCard를 추가하고 `AddAmount(delta)`로 수량을 증가시킨다. 음수 delta는 허용하지 않는다.\n- `ApplyMaterial(item_id, amount)` \u002F `AddMaterialAmount(item_id, delta)` — `_storage.Materials`에 AbilityItemMaterial을 추가하고 `AddAmount(delta)`로 수량을 증가시킨다. 음수 delta는 허용하지 않는다.\n- `ApplyEquip(item_id)` — 새 `itemUid`(GUID)로 AbilityItemEquip을 생성하여 `_storage.Equipments`에 추가한다.\n- `ApplyHero(item_id, amount)` \u002F `AddHeroAmount(item_id, delta)` — `_storage.Heroes`에 AbilityItemHero를 추가하고 `AddAmount(delta)`로 수량을 증가시킨다. 음수 delta는 허용하지 않는다.\n- `SelectedHeroId` — 현재 선택된 owned hero의 itemId raw state. 존재하지 않는 hero가 제거되면 함께 비운다.\n- `UpgradeEquipItem(baseItemUid, materialItemUid0, materialItemUid1)` — manager boundary에서 equip upgrade recipe를 검증하고 source 3개를 제거한 뒤 upgraded equip 1개를 추가한다. storage는 개별 `RemoveEquip\u002FAddEquip`만 수행하고, manager는 생성된 upgraded `AbilityItemEquip` runtime을 반환한다.\n- `ApplyRental(item_id, durationMs)` — `_storage.SetRental(id, max(currentExpiry, now)+duration)`로 로컬 만료 시각을 설정\u002F연장한다.\n- `SetPassOwnership(item_id)` — `_storage.SetPass(id, true)`로 소유권을 설정한다.\n- `ApplyTreasure(gradeType, amount)` — `_storage.AddTreasure(gradeType, amount)`로 chest count를 누적한다.\n- publish는 `InventoryStorage`가 아니라 `InventoryManager`가 담당한다.\n  - `PASS_OWNERSHIP_CHANGED`\n  - `CURRENCY_CHANGED`\n  - `ITEM_EQUIP_CHANGED`\n  - `ITEM_CARD_CHANGED`\n  - `ITEM_MATERIAL_CHANGED`\n  - `ITEM_HERO_CHANGED`\n  - `ITEM_EQUIP_LIST_CHANGED`\n  - `ITEM_CARD_LIST_CHANGED`\n  - `ITEM_MATERIAL_LIST_CHANGED`\n  - `ITEM_HERO_LIST_CHANGED`\n  - `RENTAL_CHANGED`\n  - `TREASURE_STATE_CHANGED`\n  - `INVENTORY_SNAPSHOT_CHANGED`\n\n- `InventoryManager`가 public query boundary를 제공한다.\n- 외부 시스템은 `EquippedItems\u002FUnequippedItems\u002FUnownedEquipItems\u002FGetEquipments\u002FGetCards\u002FGetMaterials\u002FGetHeroes\u002FGetRentals\u002FGetPasses\u002FGetRentalRemainingMs\u002FHasPass\u002FHasActiveRental` 같은 manager helper를 사용한다.\n- 신규 코드에서 `InventoryStorage` 직접 조회\u002F변경은 금지한다.\n- load\u002Fimport\u002Fclear는 temp `InventorySnapshot`을 만든 뒤 `InventoryManager.ReplaceState\u002FClearState`로 반영한다.\n- 카드\u002F재료\u002F영웅은 일반 revoke\u002Fapply 경로에서 amount가 0이 되면 storage에서 제거한다.\n- 단, card\u002Fhero level up 결과로 amount가 0이 된 runtime은 level\u002Fequip 상태 보존을 위해 storage\u002Fsnapshot에 남을 수 있다.\n- selected hero raw state는 `InventoryStorage`\u002F`InventorySnapshot`\u002FSaveData inventory JSON(`selectedHeroItemId`)에 저장한다. `SelectedHero` runtime은 manager 파생 view다.\n- 장착\u002F미장착 장비 목록은 `InventoryStorage`가 아니라 `InventoryManager`가 `_storage.Equipments`로부터 재구성하는 파생 runtime view다.\n- 미보유 장비 목록은 `InventoryStorage`에 저장하지 않는다. `InventoryManager`가 `TB_ITEM_EQUIP.GetAll()`과 owned equip `item_id` 집합을 비교해서 만드는 파생 table-row view다.\n\n---\n\n## 3. Implementation Location (3-path mirror)\n\n### 파일 위치 (MobilePackage 샘플)\n\n```\nMobilePackage\u002FRuntime\u002FInventory\u002F\n├── InventoryManager.cs          (10-inventory-manager)\n├── InventoryStorage.cs\n├── InventorySnapshot.cs         (save\u002Fload DTO)\n├── InventoryStaminaController.cs (14-inventory-stamina-controller)\n└── InventoryMessageTrigger.cs   (16-inventory-message-trigger)\n```\n\n3-path mirror ([정책](..\u002F..\u002F..\u002F04-package-policy\u002FSKILL.md)):\n- UPM (정본): `framework-cs\u002Fupm\u002Fcom.devian.foundation\u002FSamples~\u002FMobilePackage\u002FRuntime\u002FInventory\u002F`\n- Packages (sync): `framework-cs\u002Fapps\u002FUnityExample\u002FPackages\u002Fcom.devian.foundation\u002FSamples~\u002FMobilePackage\u002FRuntime\u002FInventory\u002F`\n- Assets\u002FSamples (import): `framework-cs\u002Fapps\u002FUnityExample\u002FAssets\u002FSamples\u002FDevian Foundation\u002F{version}\u002FMobilePackage\u002FRuntime\u002FInventory\u002F`\n\nNOTE: `ItemData` 클래스는 `AbilityItemEquip`에 통합되어 삭제되었다. `BagItems`는 `Equipments`로 리네임되었다.\n\n---\n\n## 4. asmdef\n\n`Devian.Samples.MobilePackage.asmdef`에 포함된 참조:\n- `Devian.Domain.Game` (UNIT_STAT_TYPE — AbilityItemEquip → AbilityBase 경유)\n\n---\n\n## 5. namespace\n\n```csharp\nnamespace Devian\n```\n\n(Samples 정책: 단일 namespace `Devian`)\n\n---\n\n## 6. Hard Rules\n\n- InventoryStorage는 **sealed POCO 클래스**이다 (MonoBehaviour 금지).\n- 장비는 `itemUid`(GUID)를 pk로 관리한다. 같은 `item_id`에 여러 인스턴스가 존재할 수 있다.\n- currency 잔고는 `InventoryStorage` 내부 딕셔너리와 helper(`GetCurrencyAmount\u002FTryAddCurrency`)로 직접 관리한다.\n- `Equipments` key = `itemUid` (string). value = `AbilityItemEquip`.\n- `Cards` key = `item_id` (string). value = `AbilityItemCard`.\n- `Materials` key = `item_id` (string). value = `AbilityItemMaterial`.\n- `Heroes` key = `item_id` (string). value = `AbilityItemHero`.\n- `Rentals` key = `item_id` (string key). value = `long` expiresAtClientUtcMs.\n- `Passes` key = `item_id` (string key). value = `bool` owned.\n- `TreasureCurrent` = `InventoryTreasureCurrent` (sealed POCO, Exp\u002FLevel).\n- `TreasureCounts` key = `ITEM_GRADE_TYPE` (NONE 제외). value = `int` (보유 chest count).\n- treasure count와 exp는 음수 불가 (0 이하 clamp).\n- treasure current 상태는 grade별 분리 없이 단일 Exp\u002FLevel만 사용한다.\n- 장비 장착\u002F해제의 핵심 로직은 `AbilityItemHero`가 담당한다. InventoryStorage는 편의 메서드(`Equip`\u002F`Unequip`)로 위임한다.\n- InventoryStorage는 InventoryManager가 소유한다 (싱글톤 등록 안 함).\n- \"Sample\" 접두사 금지 (정책).\n\n---\n\n## 7. JSON 직렬화 — SaveDataJsonCodec으로 이전됨\n\n> **변경**: `ToJson()` \u002F `FromJson()` 메서드는 **삭제**되었다.\n> 직렬화 책임은 [21-savedata-system\u002F43-savedata-json-codec](..\u002F..\u002F21-savedata-system\u002F43-savedata-json-codec\u002FSKILL.md)의 **`SaveDataJsonCodec`**가 담당한다.\n\nInventoryStorage는 live runtime helper를 제공한다.\n`SaveDataJsonCodec`은 live storage를 직접 직렬화하지 않고 `InventorySnapshot`을 사용한다.\n`InventoryManager.CreateSnapshot()`이 live storage를 snapshot으로 투영하고, `InventoryManager.ReplaceState()`가 snapshot을 validate\u002Fbuild 후 live storage로 적용한다.\n\nJSON 스키마: [03-ssot](..\u002F03-ssot\u002FSKILL.md) 참조.\n`SaveDataJsonCodec` 설계: [21-savedata-system\u002F43-savedata-json-codec](..\u002F..\u002F21-savedata-system\u002F43-savedata-json-codec\u002FSKILL.md) 참조.\n\n---\n\n## 8. Related\n\n- [12-game-ability](..\u002F..\u002F..\u002F21-game-package\u002F12-game-ability\u002FSKILL.md) — AbilityBase, AbilityItemEquip, AbilityItemCard, AbilityItemMaterial, AbilityItemHero, AbilityUnitHero\n- [13-game-stat-type](..\u002F..\u002F..\u002F..\u002Fdevian\u002F21-domain-game\u002F13-game-stat-type\u002FSKILL.md) — UNIT_STAT_TYPE enum\n- [10-inventory-manager](..\u002F10-inventory-manager\u002FSKILL.md) — InventoryManager (InventoryStorage 소유자, 수량 SSOT)\n- [12-inventory-wallet](..\u002F12-inventory-wallet\u002FSKILL.md) — Currency State Flattening (legacy wallet wrapper 제거)\n- [03-ssot](..\u002F03-ssot\u002FSKILL.md) — Inventory State\u002FApply Rules\n- [00-overview](..\u002F00-overview\u002FSKILL.md) — Inventory System 개요\n- [21-savedata-system\u002F43-savedata-json-codec](..\u002F..\u002F21-savedata-system\u002F43-savedata-json-codec\u002FSKILL.md) — SaveData JSON 직렬화 담당\n",{},"main","8cb5bd877a3a7004867d2f55697cb9e4ff2f1734","skills\u002Fdevian-unity\u002F50-mobile-package\u002F22-inventory-system\u002F11-inventory-storage","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian","https:\u002F\u002Fgithub.com\u002Fdevwinsoft","Kim Hyong Joon","2026-04-12 18:44:10",{"source":7,"skillName":5,"commands":33,"prompt":43,"zipAvailable":44},[34,37,40],{"tool":35,"command":36},"npx","npx skills add devwinsoft\u002Fdevian --skill 11-inventory-storage",{"tool":38,"command":39},"bunx","bunx skills add devwinsoft\u002Fdevian --skill 11-inventory-storage",{"tool":41,"command":42},"pnpm","pnpm dlx skills add devwinsoft\u002Fdevian --skill 11-inventory-storage","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian-unity\u002F50-mobile-package\u002F22-inventory-system\u002F11-inventory-storage\u002FSKILL.md",true,[46,52,56],{"path":47,"resourceType":48,"resourceTypeDesc":49,"sizeBytes":50,"sourceHtmlUrl":51},"ERROR_CATALOG.md","REFERENCE","参考文档",17237,"https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fworkflow-automation\u002Fn8n\u002Fn8n-validation-expert\u002FERROR_CATALOG.md",{"path":53,"resourceType":48,"resourceTypeDesc":49,"sizeBytes":54,"sourceHtmlUrl":55},"FALSE_POSITIVES.md",14753,"https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fworkflow-automation\u002Fn8n\u002Fn8n-validation-expert\u002FFALSE_POSITIVES.md",{"path":57,"resourceType":48,"resourceTypeDesc":49,"sizeBytes":58,"sourceHtmlUrl":59},"README.md",7956,"https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fworkflow-automation\u002Fn8n\u002Fn8n-validation-expert\u002FREADME.md",[61,67,75,83,92,99],{"skillNo":62,"name":63,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":64,"categories":65,"keywords":66,"publishedAt":21},"01M6CBX3K2JJV38ANHVGCAT8A0","devian","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002FSKILL.md",[],[63],{"skillNo":68,"name":69,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":70,"categories":71,"keywords":72,"publishedAt":21},"01M6CBX210BFMM8AMN770J9WY8","90-project-archive","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F90-project-archive\u002FSKILL.md",[],[73,74],"archive","90 project archive",{"skillNo":76,"name":77,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":78,"categories":79,"keywords":80,"publishedAt":21},"01M6CBWZSGRR9R3YJZB3177667","30-env-management","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F30-env-management\u002FSKILL.md",[],[81,82],"management","30 env management",{"skillNo":84,"name":85,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":86,"categories":87,"keywords":88,"publishedAt":21},"01M6CBWYD3TGWBQV7V35551THF","19-page-push-send","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F20-operation\u002F19-page-push-send\u002FSKILL.md",[],[89,90,91],"push","page","19 page push send",{"skillNo":93,"name":94,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":95,"categories":96,"keywords":97,"publishedAt":21},"01M6CBWWRQ05A7604GHJKTPJTF","18-page-savedata","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F20-operation\u002F18-page-savedata\u002FSKILL.md",[],[90,98],"18 page savedata",{"skillNo":100,"name":101,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":102,"categories":103,"keywords":104,"publishedAt":21},"01M6CBWVH4S2VF1V7B2GBTESCT","17-page-deobfuscate","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F20-operation\u002F17-page-deobfuscate\u002FSKILL.md",[],[90,105],"17 page deobfuscate"]