sophhub 0.4.19 → 0.4.21
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.
- package/README.md +199 -187
- package/agents/ai-cs-admin/.config.json +51 -51
- package/agents/ai-cs-admin/AGENTS.md +293 -293
- package/agents/ai-cs-admin/HEARTBEAT.md +18 -18
- package/agents/ai-cs-qa/.config.json +47 -47
- package/agents/ai-cs-qa/BOOTSTRAP.md +22 -22
- package/agents/ai-cs-qa/scripts/setup_links.sh +39 -39
- package/agents/beauty/.config.json +17 -17
- package/agents/beauty/AGENTS.md +234 -234
- package/agents/beauty/BOOTSTRAP.md +55 -55
- package/agents/beauty/HEARTBEAT.md +5 -5
- package/agents/beauty/IDENTITY.md +5 -5
- package/agents/beauty/MEMORY.md +44 -44
- package/agents/beauty/SOUL.md +64 -64
- package/agents/beauty/TOOLS.md +160 -160
- package/agents/beauty/USER.md +114 -114
- package/agents/intern-admin/.config.json +60 -60
- package/agents/intern-admin/AGENTS.md +267 -267
- package/agents/intern-admin/BOOTSTRAP.md +21 -21
- package/agents/intern-admin/HEARTBEAT.md +3 -3
- package/agents/intern-admin/IDENTITY.md +6 -6
- package/agents/intern-admin/MEMORY.md +21 -21
- package/agents/intern-admin/SOUL.md +23 -23
- package/agents/intern-admin/TOOLS.md +93 -93
- package/agents/intern-admin/USER.md +16 -16
- package/agents/intern-admin/scripts/init_workspace.sh +27 -27
- package/agents/intern-qa/.config.json +46 -46
- package/agents/intern-qa/AGENTS.md +303 -303
- package/agents/intern-qa/BOOTSTRAP.md +16 -16
- package/agents/intern-qa/HEARTBEAT.md +3 -3
- package/agents/intern-qa/IDENTITY.md +6 -6
- package/agents/intern-qa/MEMORY.md +22 -22
- package/agents/intern-qa/SOUL.md +24 -24
- package/agents/intern-qa/TOOLS.md +24 -24
- package/agents/intern-qa/USER.md +27 -27
- package/agents/intern-qa/scripts/setup_links.sh +54 -54
- package/agents/parent-toddler/.config.json +37 -37
- package/agents/parent-toddler/AGENTS.md +51 -51
- package/agents/parent-toddler/BOOTSTRAP.md +55 -55
- package/agents/parent-toddler/HEARTBEAT.md +5 -5
- package/agents/parent-toddler/IDENTITY.md +5 -5
- package/agents/parent-toddler/MEMORY.md +22 -22
- package/agents/parent-toddler/SOUL.md +35 -35
- package/agents/parent-toddler/TOOLS.md +31 -31
- package/agents/parent-toddler/USER.md +44 -44
- package/agents/vip-admin/.config.json +51 -51
- package/agents/vip-admin/AGENTS.md +314 -314
- package/agents/vip-admin/BOOTSTRAP.md +21 -21
- package/agents/vip-admin/HEARTBEAT.md +19 -19
- package/agents/vip-admin/IDENTITY.md +6 -6
- package/agents/vip-admin/MEMORY.md +30 -30
- package/agents/vip-admin/SOUL.md +25 -25
- package/agents/vip-admin/TOOLS.md +108 -108
- package/agents/vip-admin/USER.md +31 -31
- package/agents/vip-qa/.config.json +58 -58
- package/agents/vip-qa/AGENTS.md +319 -319
- package/agents/vip-qa/BOOTSTRAP.md +73 -73
- package/agents/vip-qa/HEARTBEAT.md +23 -23
- package/agents/vip-qa/IDENTITY.md +7 -7
- package/agents/vip-qa/MEMORY.md +23 -23
- package/agents/vip-qa/SOUL.md +34 -34
- package/agents/vip-qa/TOOLS.md +41 -41
- package/agents/vip-qa/USER.md +16 -16
- package/agents/vip-qa/scripts/setup_links.sh +39 -39
- package/bin/sophhub.js +25 -25
- package/package.json +35 -33
- package/skills/agent-install/skill.json +34 -34
- package/skills/agent-install/src/SKILL.md +240 -240
- package/skills/agent-install/src/pyproject.toml +6 -6
- package/skills/agent-install/src/scripts/backup_agent.py +120 -120
- package/skills/agent-install/src/scripts/check_installed.py +479 -479
- package/skills/agent-install/src/scripts/common.py +568 -568
- package/skills/agent-install/src/scripts/copy_agent_files.py +59 -59
- package/skills/agent-install/src/scripts/list_agents.py +285 -285
- package/skills/agent-install/src/scripts/resolve_install_params.py +90 -90
- package/skills/agent-install/src/scripts/update_agent_md.py +76 -76
- package/skills/agent-install/src/scripts/update_openclaw.py +193 -193
- package/skills/agent-install/src/scripts/verify_download.py +148 -148
- package/skills/aippt/skill.json +20 -20
- package/skills/aippt/src/SKILL.md +235 -235
- package/skills/aippt/src/pyproject.toml +8 -8
- package/skills/aippt/src/scripts/auth.py +122 -122
- package/skills/aippt/src/scripts/ppt.py +361 -361
- package/skills/aippt/src/scripts/provider_docmee.py +299 -299
- package/skills/beauty-salon-inventory/skill.json +16 -16
- package/skills/beauty-salon-inventory/src/SKILL.md +69 -69
- package/skills/beauty-salon-inventory/src/scripts/init_salon_inventory.py +39 -39
- package/skills/beauty-salon-inventory/src/scripts/init_salon_inventory.sh +4 -4
- package/skills/beauty-salon-inventory/src/scripts/salon_inventory_cli.py +244 -244
- package/skills/beauty-salon-marketing/skill.json +10 -10
- package/skills/beauty-salon-marketing/src/SKILL.md +36 -36
- package/skills/beauty-salon-marketing/src/playbooks/beauty-salon-festival.md +19 -19
- package/skills/beauty-salon-marketing/src/playbooks/beauty-salon-segment.md +18 -18
- package/skills/beauty-salon-marketing/src/scripts/beauty_marketing_cli.py +99 -99
- package/skills/beauty-salon-marketing/src/scripts/member_segment.py +114 -114
- package/skills/beauty-salon-member-appointment/skill.json +10 -10
- package/skills/beauty-salon-member-appointment/src/SKILL.md +36 -36
- package/skills/beauty-salon-member-appointment/src/pyproject.toml +9 -9
- package/skills/beauty-salon-member-appointment/src/scripts/run_e2e_smoke.py +160 -160
- package/skills/beauty-salon-member-appointment/src/src/member_appt_cli/__init__.py +1 -1
- package/skills/beauty-salon-member-appointment/src/src/member_appt_cli/__main__.py +4 -4
- package/skills/beauty-salon-member-appointment/src/src/member_appt_cli/cli.py +921 -921
- package/skills/beauty-salon-member-appointment/src/src/member_appt_cli/db.py +30 -30
- package/skills/beauty-salon-membership/skill.json +20 -20
- package/skills/beauty-salon-membership/src/SKILL.md +67 -67
- package/skills/beauty-salon-product-service/skill.json +12 -12
- package/skills/beauty-salon-product-service/src/SKILL.md +42 -42
- package/skills/beauty-salon-product-service/src/pyproject.toml +9 -9
- package/skills/beauty-salon-product-service/src/src/product_service_cli/__init__.py +1 -1
- package/skills/beauty-salon-product-service/src/src/product_service_cli/__main__.py +4 -4
- package/skills/beauty-salon-product-service/src/src/product_service_cli/cli.py +329 -329
- package/skills/beauty-salon-product-service/src/src/product_service_cli/db.py +29 -29
- package/skills/beauty-salon-staff/skill.json +10 -10
- package/skills/beauty-salon-staff/src/SKILL.md +37 -37
- package/skills/beauty-salon-staff/src/pyproject.toml +9 -9
- package/skills/beauty-salon-staff/src/src/staff_cli/__init__.py +1 -1
- package/skills/beauty-salon-staff/src/src/staff_cli/__main__.py +4 -4
- package/skills/beauty-salon-staff/src/src/staff_cli/cli.py +479 -479
- package/skills/beauty-salon-staff/src/src/staff_cli/db.py +28 -28
- package/skills/beauty-salon-suite/skill.json +13 -13
- package/skills/beauty-salon-suite/src/SKILL.md +18 -18
- package/skills/beauty-salon-suite/src/beauty_db/__init__.py +2 -2
- package/skills/beauty-salon-suite/src/beauty_db/db.py +249 -249
- package/skills/beauty-salon-traffic/skill.json +20 -20
- package/skills/beauty-salon-traffic/src/SKILL.md +84 -84
- package/skills/bing-image-search/skill.json +20 -20
- package/skills/bing-image-search/src/SKILL.md +105 -105
- package/skills/bot-api-status/skill.json +44 -44
- package/skills/bot-api-status/src/SKILL.md +99 -99
- package/skills/bot-api-status/src/pyproject.toml +5 -5
- package/skills/bot-api-status/src/scripts/secret.py +496 -496
- package/skills/bot-secret/skill.json +35 -35
- package/skills/bot-secret/src/SKILL.md +51 -51
- package/skills/bot-secret/src/pyproject.toml +5 -5
- package/skills/bot-secret/src/scripts/secret.py +120 -120
- package/skills/cake-flower-holiday-campaign/skill.json +20 -20
- package/skills/cake-flower-holiday-campaign/src/SKILL.md +68 -68
- package/skills/cake-flower-order-sop/skill.json +20 -20
- package/skills/cake-flower-order-sop/src/SKILL.md +65 -65
- package/skills/claw-agent-get-send/skill.json +32 -32
- package/skills/claw-agent-get-send/src/SKILL.md +43 -43
- package/skills/claw-agent-get-send/src/pyproject.toml +5 -5
- package/skills/claw-agent-get-send/src/scripts/appia_claw.py +379 -379
- package/skills/compact-context/skill.json +20 -20
- package/skills/compact-context/src/SKILL.md +133 -133
- package/skills/compact-context/src/scripts/check.sh +381 -381
- package/skills/compact-context/src/scripts/set-keep-recent.mjs +1337 -1337
- package/skills/compact-context/src/scripts/setup.sh +96 -96
- package/skills/consensus/skill.json +20 -20
- package/skills/consensus/src/SKILL.md +93 -93
- package/skills/deepwiki/skill.json +20 -20
- package/skills/deepwiki/src/SKILL.md +45 -45
- package/skills/deepwiki/src/_meta.json +5 -5
- package/skills/deepwiki/src/scripts/deepwiki.js +135 -135
- package/skills/didi-ride/skill.json +20 -20
- package/skills/didi-ride/src/SKILL.md +309 -309
- package/skills/didi-ride/src/_meta.json +5 -5
- package/skills/didi-ride/src/assets/PREFERENCE.md +58 -58
- package/skills/didi-ride/src/package.json +15 -15
- package/skills/didi-ride/src/references/api_references.md +171 -171
- package/skills/didi-ride/src/references/error_handling.md +68 -68
- package/skills/didi-ride/src/references/setup.md +73 -73
- package/skills/didi-ride/src/references/workflow.md +150 -150
- package/skills/feishu-bitable/skill.json +20 -20
- package/skills/feishu-bitable/src/CHECKLIST.md +149 -149
- package/skills/feishu-bitable/src/README.md +177 -177
- package/skills/feishu-bitable/src/SKILL.md +113 -113
- package/skills/feishu-bitable/src/_meta.json +5 -5
- package/skills/feishu-bitable/src/api.js +380 -380
- package/skills/feishu-bitable/src/bin/cli.js +283 -283
- package/skills/feishu-bitable/src/description.md +142 -142
- package/skills/feishu-bitable/src/examples/create-records.json +51 -51
- package/skills/feishu-bitable/src/examples/create-table.json +63 -63
- package/skills/feishu-bitable/src/package-lock.json +324 -324
- package/skills/feishu-bitable/src/package.json +32 -32
- package/skills/feishu-bitable/src/publish-config.json +13 -13
- package/skills/feishu-bitable/src/test-simple.js +60 -60
- package/skills/feishu-bitable/src/utils.js +260 -260
- package/skills/feishu-notes-assistant-universal/skill.json +20 -20
- package/skills/feishu-notes-assistant-universal/src/README.md +55 -55
- package/skills/feishu-notes-assistant-universal/src/SKILL.md +159 -159
- package/skills/feishu-notes-assistant-universal/src/scripts/_resolve_lark_cli.py +58 -58
- package/skills/feishu-notes-assistant-universal/src/scripts/openclaw_meeting_minutes.py +462 -462
- package/skills/feishu-notes-assistant-universal/src/scripts/openclaw_notes_crud.py +547 -547
- package/skills/feishu-notes-assistant-universal/src/scripts/openclaw_notes_crud_test.py +181 -181
- package/skills/feishu-notes-assistant-universal/src/scripts/run_meeting_minutes.py +80 -80
- package/skills/feishu-notes-assistant-universal/src/scripts/run_meeting_minutes.sh +5 -5
- package/skills/feishu-notes-assistant-universal/src/scripts/run_note_crud.py +32 -32
- package/skills/feishu-notes-assistant-universal/src/scripts/run_note_crud.sh +5 -5
- package/skills/flight-booking/skill.json +36 -36
- package/skills/flight-booking/src/SKILL.md +288 -288
- package/skills/flight-booking/src/scripts/flight_booking.py +1237 -1237
- package/skills/flyai/skill.json +20 -20
- package/skills/flyai/src/SKILL.md +119 -119
- package/skills/flyai/src/references/fliggy-fast-search.md +53 -53
- package/skills/flyai/src/references/search-flight.md +89 -89
- package/skills/flyai/src/references/search-hotels.md +57 -57
- package/skills/flyai/src/references/search-poi.md +48 -48
- package/skills/google-maps/skill.json +20 -20
- package/skills/google-maps/src/SKILL.md +237 -237
- package/skills/google-maps/src/_meta.json +5 -5
- package/skills/google-maps/src/lib/map_helper.py +912 -912
- package/skills/image-classify/skill.json +42 -42
- package/skills/image-classify/src/SKILL.md +368 -368
- package/skills/image-classify/src/references/config.json +4 -4
- package/skills/image-classify/src/scripts/face_search.py +1276 -1276
- package/skills/image-description/skill.json +34 -34
- package/skills/image-description/src/SKILL.md +33 -33
- package/skills/image-description/src/pyproject.toml +8 -8
- package/skills/image-description/src/scripts/ana_image.py +112 -112
- package/skills/image-identify-world/skill.json +20 -20
- package/skills/image-identify-world/src/SKILL.md +40 -40
- package/skills/image-identify-world/src/pyproject.toml +8 -8
- package/skills/image-identify-world/src/scripts/identify_world.py +115 -115
- package/skills/insurance-policy-review/skill.json +27 -27
- package/skills/insurance-policy-review/src/SKILL.md +75 -75
- package/skills/insurance-sales-playbook/skill.json +20 -20
- package/skills/insurance-sales-playbook/src/SKILL.md +58 -58
- package/skills/inventory-management/skill.json +20 -20
- package/skills/inventory-management/src/SKILL.md +241 -241
- package/skills/inventory-management/src/scripts/inventory.py +1844 -1844
- package/skills/large-task-router/skill.json +20 -20
- package/skills/large-task-router/src/SKILL.md +79 -79
- package/skills/large-task-router/src/templates/plan.md +74 -74
- package/skills/lawding-contract-review/skill.json +20 -20
- package/skills/lawding-contract-review/src/SKILL.md +284 -284
- package/skills/lawding-contract-review/src/references/legal-language-library.md +1385 -1385
- package/skills/lawding-contract-review/src/scripts/build_reminders.py +471 -471
- package/skills/lawding-contract-review/src/scripts/register_contract_cron.py +457 -457
- package/skills/md2pdf-converter/skill.json +20 -20
- package/skills/md2pdf-converter/src/SKILL.md +244 -244
- package/skills/md2pdf-converter/src/_meta.json +5 -5
- package/skills/md2pdf-converter/src/scripts/generate_emoji_mapping.py +74 -74
- package/skills/md2pdf-converter/src/scripts/md2pdf-local.sh +291 -291
- package/skills/notes-hub-assistant/skill.json +20 -20
- package/skills/notes-hub-assistant/src/SKILL.md +233 -233
- package/skills/notes-hub-assistant/src/scripts/_resolve_lark_cli.py +48 -48
- package/skills/notes-hub-assistant/src/scripts/openclaw_meeting_minutes.py +473 -473
- package/skills/notes-hub-assistant/src/scripts/openclaw_notes_crud.py +596 -596
- package/skills/notes-hub-assistant/src/scripts/openclaw_wolai_notes_crud.py +364 -364
- package/skills/notes-hub-assistant/src/scripts/run_meeting_minutes.py +79 -79
- package/skills/notes-hub-assistant/src/scripts/run_note_crud.py +37 -37
- package/skills/notes-hub-assistant/src/scripts/run_notionbot.py +36 -36
- package/skills/notes-hub-assistant/src/scripts/run_wolai_note_crud.py +27 -27
- package/skills/schedule-reminder/skill.json +20 -20
- package/skills/schedule-reminder/src/SKILL.md +619 -619
- package/skills/schedule-reminder/src/schedule_template.md +68 -68
- package/skills/schedule-reminder/src/scripts/append_event.py +204 -204
- package/skills/schedule-reminder/src/scripts/create_reminders.sh +163 -163
- package/skills/schedule-reminder/src/scripts/daily_activate.sh +175 -175
- package/skills/schedule-reminder/src/scripts/parse_schedule.py +704 -704
- package/skills/schedule-reminder/src/scripts/setup.sh +242 -242
- package/skills/schedule-reminder/src//347/224/250/346/210/267/346/214/207/345/215/227.md +311 -311
- package/skills/sessions-analysis/skill.json +34 -34
- package/skills/sessions-analysis/src/SKILL.md +81 -81
- package/skills/sessions-analysis/src/pyproject.toml +5 -5
- package/skills/sessions-analysis/src/scripts/ana_logs.py +205 -205
- package/skills/share-skill/skill.json +20 -20
- package/skills/share-skill/src/SKILL.md +261 -261
- package/skills/share-skill/src/scripts/share_skill_to_friend.py +1031 -1031
- package/skills/skill-creator/skill.json +20 -20
- package/skills/skill-creator/src/SKILL.md +370 -370
- package/skills/skill-creator/src/license.txt +202 -202
- package/skills/skill-creator/src/scripts/init_skill.py +378 -378
- package/skills/skill-creator/src/scripts/package_skill.py +111 -111
- package/skills/skill-creator/src/scripts/quick_validate.py +101 -101
- package/skills/skillhub/skill.json +27 -27
- package/skills/skillhub/src/SKILL.md +121 -121
- package/skills/sophnet-age-appearance/skill.json +20 -20
- package/skills/sophnet-age-appearance/src/SKILL.md +83 -83
- package/skills/sophnet-age-appearance/src/pyproject.toml +10 -10
- package/skills/sophnet-age-appearance/src/scripts/age_appearance.py +395 -395
- package/skills/sophnet-age-appearance/src/scripts/age_face_crop.py +313 -313
- package/skills/sophnet-bot-client/skill.json +20 -20
- package/skills/sophnet-bot-client/src/SKILL.md +255 -255
- package/skills/sophnet-bot-client/src/pyproject.toml +13 -13
- package/skills/sophnet-bot-client/src/scripts/bot_client_proxy.py +165 -165
- package/skills/sophnet-bot-client/src/scripts/bot_client_safe.sh +29 -29
- package/skills/sophnet-bot-client/src/scripts/bot_client_setup.py +502 -502
- package/skills/sophnet-bot-client/src/tests/test_bot_client_proxy.py +255 -255
- package/skills/sophnet-bot-client/src/tests/test_bot_client_setup.py +679 -679
- package/skills/sophnet-bot-client/src/uv.lock +8 -8
- package/skills/sophnet-customer-management/skill.json +20 -20
- package/skills/sophnet-customer-management/src/SKILL.md +270 -270
- package/skills/sophnet-customer-management/src/pyproject.toml +15 -15
- package/skills/sophnet-customer-management/src/src/customer_mgmt_cli/__init__.py +2 -2
- package/skills/sophnet-customer-management/src/src/customer_mgmt_cli/__main__.py +5 -5
- package/skills/sophnet-customer-management/src/src/customer_mgmt_cli/cli.py +67 -67
- package/skills/sophnet-customer-management/src/src/customer_mgmt_cli/commands/__init__.py +2 -2
- package/skills/sophnet-customer-management/src/src/customer_mgmt_cli/commands/customer.py +60 -60
- package/skills/sophnet-customer-management/src/src/customer_mgmt_cli/commands/export_file.py +18 -18
- package/skills/sophnet-customer-management/src/src/customer_mgmt_cli/commands/import_file.py +15 -15
- package/skills/sophnet-customer-management/src/src/customer_mgmt_cli/commands/reminder.py +26 -26
- package/skills/sophnet-customer-management/src/src/customer_mgmt_cli/commands/schema.py +28 -28
- package/skills/sophnet-customer-management/src/src/customer_mgmt_cli/config.py +54 -54
- package/skills/sophnet-customer-management/src/src/customer_mgmt_core/__init__.py +2 -2
- package/skills/sophnet-customer-management/src/src/customer_mgmt_core/exporter.py +85 -85
- package/skills/sophnet-customer-management/src/src/customer_mgmt_core/models.py +84 -84
- package/skills/sophnet-customer-management/src/src/customer_mgmt_core/normalizer.py +144 -144
- package/skills/sophnet-customer-management/src/src/customer_mgmt_core/parser.py +241 -241
- package/skills/sophnet-customer-management/src/src/customer_mgmt_core/query.py +109 -109
- package/skills/sophnet-customer-management/src/src/customer_mgmt_core/reminder.py +121 -121
- package/skills/sophnet-customer-management/src/src/customer_mgmt_core/repository.py +397 -397
- package/skills/sophnet-customer-management/src/src/customer_mgmt_core/schema.py +106 -106
- package/skills/sophnet-customer-management/src/src/customer_mgmt_core/service.py +565 -565
- package/skills/sophnet-customer-management/src/uv.lock +48 -48
- package/skills/sophnet-customized-marketing/skill.json +28 -28
- package/skills/sophnet-customized-marketing/src/SKILL.md +144 -144
- package/skills/sophnet-customized-marketing/src/playbooks/campaign-planning.md +187 -187
- package/skills/sophnet-customized-marketing/src/playbooks/content-generation.md +124 -124
- package/skills/sophnet-customized-marketing/src/playbooks/marketing-calendar.md +59 -59
- package/skills/sophnet-customized-marketing/src/playbooks/multi-channel-bundle.md +94 -94
- package/skills/sophnet-customized-marketing/src/playbooks/poster-generation.md +182 -182
- package/skills/sophnet-customized-marketing/src/playbooks/style-profile-workflow.md +103 -103
- package/skills/sophnet-customized-marketing/src/pyproject.toml +8 -8
- package/skills/sophnet-customized-marketing/src/references/campaign-mechanics.md +168 -168
- package/skills/sophnet-customized-marketing/src/references/content-safety.md +26 -26
- package/skills/sophnet-customized-marketing/src/references/marketing-date-checklist.md +99 -99
- package/skills/sophnet-customized-marketing/src/references/platform-writing-guidelines.md +88 -88
- package/skills/sophnet-customized-marketing/src/references/quality-checklist.md +44 -44
- package/skills/sophnet-customized-marketing/src/scripts/generate_poster.py +572 -572
- package/skills/sophnet-customized-marketing/src/scripts/style_profile.py +215 -215
- package/skills/sophnet-dailynews/skill.json +20 -20
- package/skills/sophnet-dailynews/src/SKILL.md +179 -179
- package/skills/sophnet-dailynews/src/cache.json +150 -150
- package/skills/sophnet-dailynews/src/sources.json +230 -230
- package/skills/sophnet-docx/skill.json +20 -20
- package/skills/sophnet-docx/src/SKILL.md +463 -463
- package/skills/sophnet-docx/src/package-lock.json +208 -208
- package/skills/sophnet-docx/src/package.json +16 -16
- package/skills/sophnet-docx/src/pyproject.toml +11 -11
- package/skills/sophnet-docx/src/scripts/__init__.py +1 -1
- package/skills/sophnet-docx/src/scripts/accept_changes.py +135 -135
- package/skills/sophnet-docx/src/scripts/comment.py +318 -318
- package/skills/sophnet-docx/src/scripts/ensure_uv_env.sh +68 -68
- package/skills/sophnet-docx/src/scripts/office/helpers/merge_runs.py +199 -199
- package/skills/sophnet-docx/src/scripts/office/helpers/simplify_redlines.py +197 -197
- package/skills/sophnet-docx/src/scripts/office/pack.py +159 -159
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -1499
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -146
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -1085
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -11
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -3081
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -23
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -185
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -287
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -1676
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -28
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -144
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -174
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -25
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -18
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -59
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -56
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -195
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -582
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -25
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -4439
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -570
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -509
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -12
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -108
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -96
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -3646
- package/skills/sophnet-docx/src/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -116
- package/skills/sophnet-docx/src/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -42
- package/skills/sophnet-docx/src/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -50
- package/skills/sophnet-docx/src/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -49
- package/skills/sophnet-docx/src/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -33
- package/skills/sophnet-docx/src/scripts/office/schemas/mce/mc.xsd +75 -75
- package/skills/sophnet-docx/src/scripts/office/schemas/microsoft/wml-2010.xsd +560 -560
- package/skills/sophnet-docx/src/scripts/office/schemas/microsoft/wml-2012.xsd +67 -67
- package/skills/sophnet-docx/src/scripts/office/schemas/microsoft/wml-2018.xsd +14 -14
- package/skills/sophnet-docx/src/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -20
- package/skills/sophnet-docx/src/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -13
- package/skills/sophnet-docx/src/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -4
- package/skills/sophnet-docx/src/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -8
- package/skills/sophnet-docx/src/scripts/office/soffice.py +183 -183
- package/skills/sophnet-docx/src/scripts/office/unpack.py +132 -132
- package/skills/sophnet-docx/src/scripts/office/validate.py +111 -111
- package/skills/sophnet-docx/src/scripts/office/validators/__init__.py +15 -15
- package/skills/sophnet-docx/src/scripts/office/validators/base.py +847 -847
- package/skills/sophnet-docx/src/scripts/office/validators/docx.py +446 -446
- package/skills/sophnet-docx/src/scripts/office/validators/pptx.py +275 -275
- package/skills/sophnet-docx/src/scripts/office/validators/redlining.py +247 -247
- package/skills/sophnet-docx/src/scripts/templates/comments.xml +3 -3
- package/skills/sophnet-docx/src/scripts/templates/commentsExtended.xml +3 -3
- package/skills/sophnet-docx/src/scripts/templates/commentsExtensible.xml +3 -3
- package/skills/sophnet-docx/src/scripts/templates/commentsIds.xml +3 -3
- package/skills/sophnet-docx/src/scripts/templates/people.xml +3 -3
- package/skills/sophnet-docx/src/scripts/upload_file.sh +96 -96
- package/skills/sophnet-docx/src/uv.lock +320 -320
- package/skills/sophnet-face-search/skill.json +20 -20
- package/skills/sophnet-face-search/src/SKILL.md +115 -115
- package/skills/sophnet-face-search/src/pyproject.toml +11 -11
- package/skills/sophnet-face-search/src/scripts/face_search.py +335 -335
- package/skills/sophnet-face-search/src/uv.lock +508 -508
- package/skills/sophnet-id-photo/skill.json +20 -20
- package/skills/sophnet-id-photo/src/SKILL.md +107 -107
- package/skills/sophnet-id-photo/src/pyproject.toml +10 -10
- package/skills/sophnet-id-photo/src/scripts/id_photo.py +540 -540
- package/skills/sophnet-id-photo/src/scripts/id_photo_compliance.py +215 -215
- package/skills/sophnet-id-photo/src/scripts/id_photo_face_crop.py +313 -313
- package/skills/sophnet-image-edit/skill.json +20 -20
- package/skills/sophnet-image-edit/src/SKILL.md +140 -140
- package/skills/sophnet-image-edit/src/pyproject.toml +9 -9
- package/skills/sophnet-image-edit/src/scripts/edit_and_preview.sh +68 -68
- package/skills/sophnet-image-edit/src/scripts/edit_image.py +279 -279
- package/skills/sophnet-image-edit/src/uv.lock +234 -234
- package/skills/sophnet-image-generate/skill.json +20 -20
- package/skills/sophnet-image-generate/src/SKILL.md +62 -62
- package/skills/sophnet-image-generate/src/pyproject.toml +9 -9
- package/skills/sophnet-image-generate/src/scripts/generate_image.py +156 -156
- package/skills/sophnet-image-generate/src/uv.lock +234 -234
- package/skills/sophnet-image-ocr/skill.json +20 -20
- package/skills/sophnet-image-ocr/src/SKILL.md +167 -167
- package/skills/sophnet-image-ocr/src/pyproject.toml +13 -13
- package/skills/sophnet-image-ocr/src/scripts/ocr.py +225 -225
- package/skills/sophnet-image-ocr/src/uv.lock +234 -234
- package/skills/sophnet-infinite-talk/skill.json +20 -20
- package/skills/sophnet-infinite-talk/src/SKILL.md +140 -140
- package/skills/sophnet-infinite-talk/src/pyproject.toml +9 -9
- package/skills/sophnet-infinite-talk/src/scripts/gen.py +172 -172
- package/skills/sophnet-oss/skill.json +27 -27
- package/skills/sophnet-oss/src/SKILL.md +118 -118
- package/skills/sophnet-oss/src/pyproject.toml +8 -8
- package/skills/sophnet-oss/src/scripts/upload_file.py +43 -43
- package/skills/sophnet-pdf/skill.json +20 -20
- package/skills/sophnet-pdf/src/SKILL.md +413 -413
- package/skills/sophnet-pdf/src/forms.md +297 -297
- package/skills/sophnet-pdf/src/pyproject.toml +14 -14
- package/skills/sophnet-pdf/src/reference.md +611 -611
- package/skills/sophnet-pdf/src/scripts/check_bounding_boxes.py +65 -65
- package/skills/sophnet-pdf/src/scripts/check_fillable_fields.py +11 -11
- package/skills/sophnet-pdf/src/scripts/convert_pdf_to_images.py +33 -33
- package/skills/sophnet-pdf/src/scripts/create_validation_image.py +37 -37
- package/skills/sophnet-pdf/src/scripts/enhance_tutorial.py +557 -557
- package/skills/sophnet-pdf/src/scripts/ensure_uv_env.sh +68 -68
- package/skills/sophnet-pdf/src/scripts/extract_form_field_info.py +122 -122
- package/skills/sophnet-pdf/src/scripts/extract_form_structure.py +115 -115
- package/skills/sophnet-pdf/src/scripts/extract_pdf_content.py +34 -34
- package/skills/sophnet-pdf/src/scripts/fill_fillable_fields.py +98 -98
- package/skills/sophnet-pdf/src/scripts/fill_pdf_form_with_annotations.py +107 -107
- package/skills/sophnet-pdf/src/scripts/upload_file.sh +88 -88
- package/skills/sophnet-pdf/src/uv.lock +537 -537
- package/skills/sophnet-qa-install/skill.json +27 -27
- package/skills/sophnet-qa-install/src/SKILL.md +210 -210
- package/skills/sophnet-qa-install/src/pyproject.toml +6 -6
- package/skills/sophnet-qa-install/src/scripts/backup_md.py +35 -35
- package/skills/sophnet-qa-install/src/scripts/check_installed.py +143 -143
- package/skills/sophnet-qa-install/src/scripts/update_config.py +142 -142
- package/skills/sophnet-qa-install/src/scripts/update_md.py +73 -73
- package/skills/sophnet-schedule/skill.json +20 -20
- package/skills/sophnet-schedule/src/ARCHITECTURE.md +321 -321
- package/skills/sophnet-schedule/src/IMPROVEMENTS.md +145 -145
- package/skills/sophnet-schedule/src/SKILL.md +1050 -1050
- package/skills/sophnet-schedule/src/_meta.json +6 -6
- package/skills/sophnet-schedule/src/api/models.py +245 -245
- package/skills/sophnet-schedule/src/apps/add_event.py +237 -237
- package/skills/sophnet-schedule/src/apps/check_reminders.py +112 -112
- package/skills/sophnet-schedule/src/apps/check_roc.py +246 -246
- package/skills/sophnet-schedule/src/apps/generate_daily_plan.py +342 -342
- package/skills/sophnet-schedule/src/apps/import_events.py +216 -216
- package/skills/sophnet-schedule/src/apps/monitor_calendar_changes.py +140 -140
- package/skills/sophnet-schedule/src/apps/register_tasks.py +169 -169
- package/skills/sophnet-schedule/src/apps/sync_roc_to_gcal.py +174 -174
- package/skills/sophnet-schedule/src/compat.py +66 -66
- package/skills/sophnet-schedule/src/config/reminder_rules.yaml +96 -96
- package/skills/sophnet-schedule/src/config/roc_events.yaml +44 -44
- package/skills/sophnet-schedule/src/config/settings.py +133 -133
- package/skills/sophnet-schedule/src/config/task_registry.yaml +92 -92
- package/skills/sophnet-schedule/src/docs/FRONTEND_INTEGRATION_GUIDE.md +437 -437
- package/skills/sophnet-schedule/src/gcal/client.py +374 -374
- package/skills/sophnet-schedule/src/gcal/models.py +91 -91
- package/skills/sophnet-schedule/src/requirements.txt +6 -6
- package/skills/sophnet-schedule/src/scripts/setup_gcal_token.py +85 -85
- package/skills/sophnet-schedule/src/server.py +669 -669
- package/skills/sophnet-schedule/src/services/calendar_backend.py +139 -139
- package/skills/sophnet-schedule/src/services/conflict_detector.py +96 -96
- package/skills/sophnet-schedule/src/services/datetime_utils.py +117 -117
- package/skills/sophnet-schedule/src/services/event_classifier.py +100 -100
- package/skills/sophnet-schedule/src/services/event_diff.py +160 -160
- package/skills/sophnet-schedule/src/services/google_integration.py +500 -500
- package/skills/sophnet-schedule/src/services/job_store.py +100 -100
- package/skills/sophnet-schedule/src/services/local_event_store.py +266 -266
- package/skills/sophnet-schedule/src/services/reminder_planner.py +116 -116
- package/skills/sophnet-schedule/src/services/runtime_utils.py +31 -31
- package/skills/sophnet-schedule/src/services/table_parser.py +286 -286
- package/skills/sophnet-schedule/src/services/task_builder.py +167 -167
- package/skills/sophnet-schedule/src/services/time_window.py +72 -72
- package/skills/sophnet-sticker-edit/skill.json +27 -27
- package/skills/sophnet-sticker-edit/src/SKILL.md +80 -80
- package/skills/sophnet-sticker-edit/src/pyproject.toml +9 -9
- package/skills/sophnet-sticker-edit/src/scripts/edit_sticker_image.py +403 -403
- package/skills/sophnet-stock/skill.json +20 -20
- package/skills/sophnet-stock/src/App-Plan.md +442 -442
- package/skills/sophnet-stock/src/README.md +214 -214
- package/skills/sophnet-stock/src/SKILL.md +236 -236
- package/skills/sophnet-stock/src/TODO.md +394 -394
- package/skills/sophnet-stock/src/_meta.json +5 -5
- package/skills/sophnet-stock/src/docs/ARCHITECTURE.md +408 -408
- package/skills/sophnet-stock/src/docs/CONCEPT.md +233 -233
- package/skills/sophnet-stock/src/docs/HOT_SCANNER.md +288 -288
- package/skills/sophnet-stock/src/docs/README.md +95 -95
- package/skills/sophnet-stock/src/docs/USAGE.md +465 -465
- package/skills/sophnet-stock/src/scripts/analyze_stock.py +2565 -2565
- package/skills/sophnet-stock/src/scripts/dividends.py +365 -365
- package/skills/sophnet-stock/src/scripts/hot_scanner.py +582 -582
- package/skills/sophnet-stock/src/scripts/portfolio.py +548 -548
- package/skills/sophnet-stock/src/scripts/rumor_scanner.py +342 -342
- package/skills/sophnet-stock/src/scripts/test_stock_analysis.py +409 -409
- package/skills/sophnet-stock/src/scripts/watchlist.py +336 -336
- package/skills/sophnet-training-install/skill.json +27 -27
- package/skills/sophnet-training-install/src/SKILL.md +211 -211
- package/skills/sophnet-training-install/src/pyproject.toml +6 -6
- package/skills/sophnet-training-install/src/scripts/backup_md.py +35 -35
- package/skills/sophnet-training-install/src/scripts/check_installed.py +144 -144
- package/skills/sophnet-training-install/src/scripts/update_config.py +142 -142
- package/skills/sophnet-training-install/src/scripts/update_md.py +73 -73
- package/skills/sophnet-tts/skill.json +20 -20
- package/skills/sophnet-tts/src/SKILL.md +79 -79
- package/skills/sophnet-tts/src/pyproject.toml +9 -9
- package/skills/sophnet-tts/src/scripts/gen_tts.py +130 -130
- package/skills/sophnet-video-generate/skill.json +37 -37
- package/skills/sophnet-video-generate/src/SKILL.md +117 -117
- package/skills/sophnet-video-generate/src/scripts/gen_video.py +321 -321
- package/skills/sophnet-xlsx/skill.json +20 -20
- package/skills/sophnet-xlsx/src/SKILL.md +399 -399
- package/skills/sophnet-xlsx/src/pyproject.toml +11 -11
- package/skills/sophnet-xlsx/src/scripts/ensure_uv_env.sh +68 -68
- package/skills/sophnet-xlsx/src/scripts/office/helpers/merge_runs.py +199 -199
- package/skills/sophnet-xlsx/src/scripts/office/helpers/simplify_redlines.py +197 -197
- package/skills/sophnet-xlsx/src/scripts/office/pack.py +159 -159
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -1499
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -146
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -1085
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -11
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -3081
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -23
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -185
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -287
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -1676
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -28
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -144
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -174
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -25
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -18
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -59
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -56
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -195
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -582
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -25
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -4439
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -570
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -509
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -12
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -108
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -96
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -3646
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -116
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -42
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -50
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -49
- package/skills/sophnet-xlsx/src/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -33
- package/skills/sophnet-xlsx/src/scripts/office/schemas/mce/mc.xsd +75 -75
- package/skills/sophnet-xlsx/src/scripts/office/schemas/microsoft/wml-2010.xsd +560 -560
- package/skills/sophnet-xlsx/src/scripts/office/schemas/microsoft/wml-2012.xsd +67 -67
- package/skills/sophnet-xlsx/src/scripts/office/schemas/microsoft/wml-2018.xsd +14 -14
- package/skills/sophnet-xlsx/src/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -20
- package/skills/sophnet-xlsx/src/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -13
- package/skills/sophnet-xlsx/src/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -4
- package/skills/sophnet-xlsx/src/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -8
- package/skills/sophnet-xlsx/src/scripts/office/soffice.py +183 -183
- package/skills/sophnet-xlsx/src/scripts/office/unpack.py +132 -132
- package/skills/sophnet-xlsx/src/scripts/office/validate.py +111 -111
- package/skills/sophnet-xlsx/src/scripts/office/validators/__init__.py +15 -15
- package/skills/sophnet-xlsx/src/scripts/office/validators/base.py +847 -847
- package/skills/sophnet-xlsx/src/scripts/office/validators/docx.py +446 -446
- package/skills/sophnet-xlsx/src/scripts/office/validators/pptx.py +275 -275
- package/skills/sophnet-xlsx/src/scripts/office/validators/redlining.py +247 -247
- package/skills/sophnet-xlsx/src/scripts/recalc.py +184 -184
- package/skills/sophnet-xlsx/src/scripts/upload_file.sh +96 -96
- package/skills/sophnet-xlsx/src/uv.lock +319 -319
- package/skills/ui-ux-pro-max/skill.json +20 -20
- package/skills/ui-ux-pro-max/src/SKILL.md +377 -377
- package/skills/ui-ux-pro-max/src/data/icons.csv +101 -101
- package/skills/ui-ux-pro-max/src/data/react-performance.csv +45 -45
- package/skills/ui-ux-pro-max/src/data/stacks/astro.csv +54 -54
- package/skills/ui-ux-pro-max/src/data/stacks/jetpack-compose.csv +53 -53
- package/skills/ui-ux-pro-max/src/data/stacks/nuxt-ui.csv +51 -51
- package/skills/ui-ux-pro-max/src/data/stacks/nuxtjs.csv +59 -59
- package/skills/ui-ux-pro-max/src/data/stacks/shadcn.csv +61 -61
- package/skills/ui-ux-pro-max/src/data/typography.csv +57 -57
- package/skills/ui-ux-pro-max/src/data/ui-reasoning.csv +101 -101
- package/skills/ui-ux-pro-max/src/data/web-interface.csv +31 -31
- package/skills/ui-ux-pro-max/src/scripts/core.py +253 -253
- package/skills/ui-ux-pro-max/src/scripts/design_system.py +1067 -1067
- package/skills/video-understand/skill.json +20 -20
- package/skills/video-understand/src/SKILL.md +79 -79
- package/skills/video-understand/src/scripts/video_understand.py +204 -204
- package/skills/weather/skill.json +19 -19
- package/skills/weather/src/SKILL.md +112 -112
- package/skills/web-scraper/skill.json +20 -20
- package/skills/web-scraper/src/SKILL.md +101 -101
- package/skills/web-scraper/src/scripts/scrape.py +270 -270
- package/skills/website-builder/skill.json +20 -20
- package/skills/website-builder/src/SKILL.md +266 -266
- package/skills/website-builder/src/scripts/deploy_site.sh +46 -46
- package/skills/wechat-article-publisher/skill.json +20 -20
- package/skills/wechat-article-publisher/src/SKILL.md +60 -60
- package/skills/wechat-article-publisher/src/config.json +6 -6
- package/skills/wechat-article-publisher/src/pyproject.toml +12 -12
- package/skills/wechat-article-publisher/src/scripts/publish_wechat.py +825 -825
- package/skills/xiaohongshu/skill.json +20 -20
- package/skills/xiaohongshu/src/SKILL.md +91 -91
- package/skills/xiaohongshu/src/_meta.json +5 -5
- package/skills/xiaohongshu/src/assets/card.html +216 -216
- package/skills/xiaohongshu/src/assets/cover.html +82 -82
- package/skills/xiaohongshu/src/assets/example.md +84 -84
- package/skills/xiaohongshu/src/assets/styles.css +318 -318
- package/skills/xiaohongshu/src/scripts/render_xhs_v2.py +737 -737
- package/skills/xiaohongshu/src/scripts/sign_server.py +158 -158
- package/skills/xiaohongshu/src/scripts/stealth.min.js +6 -6
- package/skills/xiaohongshu/src/scripts/xhs_tool.py +186 -186
- package/skills/xiaohongshu/src/workflow.py +185 -185
- package/src/commands/agent.js +112 -112
- package/src/commands/download.js +101 -101
- package/src/commands/info.js +58 -58
- package/src/commands/list.js +71 -71
- package/src/utils/agents.js +36 -36
- package/src/utils/config.js +22 -22
- package/src/utils/paths.js +31 -31
- package/src/utils/versions.js +57 -57
|
@@ -1,397 +1,397 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
import sqlite3
|
|
5
|
-
import uuid
|
|
6
|
-
from contextlib import contextmanager
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
from typing import Any, Iterator
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
SCHEMA_SQL = """
|
|
12
|
-
CREATE TABLE IF NOT EXISTS customers (
|
|
13
|
-
customer_id TEXT PRIMARY KEY,
|
|
14
|
-
name TEXT NOT NULL,
|
|
15
|
-
primary_phone TEXT,
|
|
16
|
-
birthday TEXT,
|
|
17
|
-
tags_json TEXT NOT NULL DEFAULT '[]',
|
|
18
|
-
notes TEXT,
|
|
19
|
-
created_at TEXT NOT NULL,
|
|
20
|
-
updated_at TEXT NOT NULL
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
CREATE TABLE IF NOT EXISTS customer_field_defs (
|
|
24
|
-
field_key TEXT PRIMARY KEY,
|
|
25
|
-
label TEXT NOT NULL,
|
|
26
|
-
value_type TEXT NOT NULL,
|
|
27
|
-
entity_type TEXT NOT NULL DEFAULT 'customer',
|
|
28
|
-
is_required INTEGER NOT NULL DEFAULT 0,
|
|
29
|
-
source TEXT NOT NULL DEFAULT 'user_confirmed',
|
|
30
|
-
status TEXT NOT NULL DEFAULT 'active',
|
|
31
|
-
aliases_json TEXT NOT NULL DEFAULT '[]',
|
|
32
|
-
created_at TEXT NOT NULL,
|
|
33
|
-
updated_at TEXT NOT NULL
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
CREATE TABLE IF NOT EXISTS customer_field_values (
|
|
37
|
-
entity_type TEXT NOT NULL DEFAULT 'customer',
|
|
38
|
-
entity_id TEXT NOT NULL,
|
|
39
|
-
field_key TEXT NOT NULL,
|
|
40
|
-
value TEXT,
|
|
41
|
-
normalized_value TEXT,
|
|
42
|
-
display_value TEXT,
|
|
43
|
-
updated_at TEXT NOT NULL,
|
|
44
|
-
PRIMARY KEY (entity_type, entity_id, field_key)
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
CREATE TABLE IF NOT EXISTS business_records (
|
|
48
|
-
record_id TEXT PRIMARY KEY,
|
|
49
|
-
customer_id TEXT NOT NULL,
|
|
50
|
-
record_type TEXT NOT NULL,
|
|
51
|
-
status TEXT,
|
|
52
|
-
record_date TEXT,
|
|
53
|
-
amount TEXT,
|
|
54
|
-
title TEXT,
|
|
55
|
-
raw_payload_json TEXT NOT NULL DEFAULT '{}',
|
|
56
|
-
created_at TEXT NOT NULL,
|
|
57
|
-
updated_at TEXT NOT NULL
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
CREATE TABLE IF NOT EXISTS reminders (
|
|
61
|
-
reminder_id TEXT PRIMARY KEY,
|
|
62
|
-
customer_id TEXT NOT NULL,
|
|
63
|
-
reminder_type TEXT NOT NULL,
|
|
64
|
-
title TEXT NOT NULL,
|
|
65
|
-
trigger_at TEXT,
|
|
66
|
-
cron_expr TEXT NOT NULL,
|
|
67
|
-
message TEXT NOT NULL,
|
|
68
|
-
recurring INTEGER NOT NULL DEFAULT 0,
|
|
69
|
-
scheduled INTEGER NOT NULL DEFAULT 0,
|
|
70
|
-
external_task_name TEXT,
|
|
71
|
-
created_at TEXT NOT NULL
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
CREATE TABLE IF NOT EXISTS import_jobs (
|
|
75
|
-
import_job_id TEXT PRIMARY KEY,
|
|
76
|
-
source_file TEXT NOT NULL,
|
|
77
|
-
mode TEXT NOT NULL,
|
|
78
|
-
summary_json TEXT NOT NULL,
|
|
79
|
-
created_at TEXT NOT NULL
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
CREATE INDEX IF NOT EXISTS idx_customers_name ON customers(name);
|
|
83
|
-
CREATE INDEX IF NOT EXISTS idx_customers_phone ON customers(primary_phone);
|
|
84
|
-
CREATE INDEX IF NOT EXISTS idx_field_values_lookup ON customer_field_values(field_key, normalized_value);
|
|
85
|
-
CREATE INDEX IF NOT EXISTS idx_records_customer ON business_records(customer_id);
|
|
86
|
-
CREATE INDEX IF NOT EXISTS idx_reminders_customer ON reminders(customer_id);
|
|
87
|
-
"""
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
class SQLiteRepository:
|
|
91
|
-
def __init__(self, db_path: str | Path):
|
|
92
|
-
self.db_path = Path(db_path)
|
|
93
|
-
self.db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
94
|
-
|
|
95
|
-
@contextmanager
|
|
96
|
-
def connect(self) -> Iterator[sqlite3.Connection]:
|
|
97
|
-
conn = sqlite3.connect(self.db_path)
|
|
98
|
-
conn.row_factory = sqlite3.Row
|
|
99
|
-
try:
|
|
100
|
-
yield conn
|
|
101
|
-
conn.commit()
|
|
102
|
-
finally:
|
|
103
|
-
conn.close()
|
|
104
|
-
|
|
105
|
-
def init_db(self) -> None:
|
|
106
|
-
with self.connect() as conn:
|
|
107
|
-
conn.executescript(SCHEMA_SQL)
|
|
108
|
-
|
|
109
|
-
def save_import_job(self, source_file: str, mode: str, summary: dict[str, Any], created_at: str) -> str:
|
|
110
|
-
job_id = f"imp_{uuid.uuid4().hex[:12]}"
|
|
111
|
-
with self.connect() as conn:
|
|
112
|
-
conn.execute(
|
|
113
|
-
"""
|
|
114
|
-
INSERT INTO import_jobs (import_job_id, source_file, mode, summary_json, created_at)
|
|
115
|
-
VALUES (?, ?, ?, ?, ?)
|
|
116
|
-
""",
|
|
117
|
-
(job_id, source_file, mode, json.dumps(summary, ensure_ascii=False), created_at),
|
|
118
|
-
)
|
|
119
|
-
return job_id
|
|
120
|
-
|
|
121
|
-
def list_field_defs(self, entity_type: str = "customer") -> list[dict[str, Any]]:
|
|
122
|
-
with self.connect() as conn:
|
|
123
|
-
rows = conn.execute(
|
|
124
|
-
"""
|
|
125
|
-
SELECT * FROM customer_field_defs
|
|
126
|
-
WHERE entity_type = ?
|
|
127
|
-
ORDER BY label COLLATE NOCASE
|
|
128
|
-
""",
|
|
129
|
-
(entity_type,),
|
|
130
|
-
).fetchall()
|
|
131
|
-
results = []
|
|
132
|
-
for row in rows:
|
|
133
|
-
item = dict(row)
|
|
134
|
-
item["aliases"] = json.loads(item.pop("aliases_json"))
|
|
135
|
-
results.append(item)
|
|
136
|
-
return results
|
|
137
|
-
|
|
138
|
-
def upsert_field_def(self, field_def: dict[str, Any], now: str) -> None:
|
|
139
|
-
aliases_json = json.dumps(field_def.get("aliases", []), ensure_ascii=False)
|
|
140
|
-
with self.connect() as conn:
|
|
141
|
-
conn.execute(
|
|
142
|
-
"""
|
|
143
|
-
INSERT INTO customer_field_defs
|
|
144
|
-
(field_key, label, value_type, entity_type, is_required, source, status, aliases_json, created_at, updated_at)
|
|
145
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
146
|
-
ON CONFLICT(field_key) DO UPDATE SET
|
|
147
|
-
label = excluded.label,
|
|
148
|
-
value_type = excluded.value_type,
|
|
149
|
-
entity_type = excluded.entity_type,
|
|
150
|
-
is_required = excluded.is_required,
|
|
151
|
-
source = excluded.source,
|
|
152
|
-
status = excluded.status,
|
|
153
|
-
aliases_json = excluded.aliases_json,
|
|
154
|
-
updated_at = excluded.updated_at
|
|
155
|
-
""",
|
|
156
|
-
(
|
|
157
|
-
field_def["field_key"],
|
|
158
|
-
field_def["label"],
|
|
159
|
-
field_def["value_type"],
|
|
160
|
-
field_def.get("entity_type", "customer"),
|
|
161
|
-
int(bool(field_def.get("is_required", False))),
|
|
162
|
-
field_def.get("source", "user_confirmed"),
|
|
163
|
-
field_def.get("status", "active"),
|
|
164
|
-
aliases_json,
|
|
165
|
-
now,
|
|
166
|
-
now,
|
|
167
|
-
),
|
|
168
|
-
)
|
|
169
|
-
|
|
170
|
-
def upsert_customer(self, payload: dict[str, Any], now: str) -> tuple[str, bool]:
|
|
171
|
-
customer_id = payload.get("customer_id")
|
|
172
|
-
existing = self.find_customer_for_identity(
|
|
173
|
-
name=payload["name"],
|
|
174
|
-
primary_phone=payload.get("primary_phone"),
|
|
175
|
-
birthday=payload.get("birthday"),
|
|
176
|
-
)
|
|
177
|
-
created = False
|
|
178
|
-
if existing:
|
|
179
|
-
customer_id = existing["customer_id"]
|
|
180
|
-
if not customer_id:
|
|
181
|
-
customer_id = f"cus_{uuid.uuid4().hex[:12]}"
|
|
182
|
-
created = True
|
|
183
|
-
|
|
184
|
-
with self.connect() as conn:
|
|
185
|
-
conn.execute(
|
|
186
|
-
"""
|
|
187
|
-
INSERT INTO customers
|
|
188
|
-
(customer_id, name, primary_phone, birthday, tags_json, notes, created_at, updated_at)
|
|
189
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
190
|
-
ON CONFLICT(customer_id) DO UPDATE SET
|
|
191
|
-
name = excluded.name,
|
|
192
|
-
primary_phone = COALESCE(excluded.primary_phone, customers.primary_phone),
|
|
193
|
-
birthday = COALESCE(excluded.birthday, customers.birthday),
|
|
194
|
-
tags_json = excluded.tags_json,
|
|
195
|
-
notes = COALESCE(excluded.notes, customers.notes),
|
|
196
|
-
updated_at = excluded.updated_at
|
|
197
|
-
""",
|
|
198
|
-
(
|
|
199
|
-
customer_id,
|
|
200
|
-
payload["name"],
|
|
201
|
-
payload.get("primary_phone"),
|
|
202
|
-
payload.get("birthday"),
|
|
203
|
-
json.dumps(payload.get("tags", []), ensure_ascii=False),
|
|
204
|
-
payload.get("notes"),
|
|
205
|
-
now,
|
|
206
|
-
now,
|
|
207
|
-
),
|
|
208
|
-
)
|
|
209
|
-
return customer_id, created
|
|
210
|
-
|
|
211
|
-
def find_customer_for_identity(
|
|
212
|
-
self,
|
|
213
|
-
*,
|
|
214
|
-
name: str,
|
|
215
|
-
primary_phone: str | None = None,
|
|
216
|
-
birthday: str | None = None,
|
|
217
|
-
) -> dict[str, Any] | None:
|
|
218
|
-
with self.connect() as conn:
|
|
219
|
-
if primary_phone:
|
|
220
|
-
row = conn.execute(
|
|
221
|
-
"""
|
|
222
|
-
SELECT * FROM customers
|
|
223
|
-
WHERE name = ? AND primary_phone = ?
|
|
224
|
-
LIMIT 1
|
|
225
|
-
""",
|
|
226
|
-
(name, primary_phone),
|
|
227
|
-
).fetchone()
|
|
228
|
-
if row:
|
|
229
|
-
return self._customer_row_to_dict(row)
|
|
230
|
-
if birthday:
|
|
231
|
-
row = conn.execute(
|
|
232
|
-
"""
|
|
233
|
-
SELECT * FROM customers
|
|
234
|
-
WHERE name = ? AND birthday = ?
|
|
235
|
-
LIMIT 1
|
|
236
|
-
""",
|
|
237
|
-
(name, birthday),
|
|
238
|
-
).fetchone()
|
|
239
|
-
if row:
|
|
240
|
-
return self._customer_row_to_dict(row)
|
|
241
|
-
row = conn.execute(
|
|
242
|
-
"""
|
|
243
|
-
SELECT * FROM customers
|
|
244
|
-
WHERE name = ?
|
|
245
|
-
LIMIT 1
|
|
246
|
-
""",
|
|
247
|
-
(name,),
|
|
248
|
-
).fetchone()
|
|
249
|
-
return self._customer_row_to_dict(row) if row else None
|
|
250
|
-
|
|
251
|
-
def get_customer(self, customer_id: str) -> dict[str, Any] | None:
|
|
252
|
-
with self.connect() as conn:
|
|
253
|
-
row = conn.execute("SELECT * FROM customers WHERE customer_id = ?", (customer_id,)).fetchone()
|
|
254
|
-
return self._customer_row_to_dict(row) if row else None
|
|
255
|
-
|
|
256
|
-
def list_customers(self) -> list[dict[str, Any]]:
|
|
257
|
-
with self.connect() as conn:
|
|
258
|
-
rows = conn.execute(
|
|
259
|
-
"SELECT * FROM customers ORDER BY updated_at DESC, name COLLATE NOCASE"
|
|
260
|
-
).fetchall()
|
|
261
|
-
return [self._customer_row_to_dict(row) for row in rows]
|
|
262
|
-
|
|
263
|
-
def delete_customer(self, customer_id: str) -> bool:
|
|
264
|
-
with self.connect() as conn:
|
|
265
|
-
deleted = conn.execute("DELETE FROM customers WHERE customer_id = ?", (customer_id,)).rowcount
|
|
266
|
-
conn.execute(
|
|
267
|
-
"DELETE FROM customer_field_values WHERE entity_type = 'customer' AND entity_id = ?",
|
|
268
|
-
(customer_id,),
|
|
269
|
-
)
|
|
270
|
-
conn.execute("DELETE FROM business_records WHERE customer_id = ?", (customer_id,))
|
|
271
|
-
conn.execute("DELETE FROM reminders WHERE customer_id = ?", (customer_id,))
|
|
272
|
-
return deleted > 0
|
|
273
|
-
|
|
274
|
-
def upsert_field_value(
|
|
275
|
-
self,
|
|
276
|
-
*,
|
|
277
|
-
entity_type: str,
|
|
278
|
-
entity_id: str,
|
|
279
|
-
field_key: str,
|
|
280
|
-
value: str | None,
|
|
281
|
-
normalized_value: str | None,
|
|
282
|
-
display_value: str | None,
|
|
283
|
-
now: str,
|
|
284
|
-
) -> None:
|
|
285
|
-
with self.connect() as conn:
|
|
286
|
-
conn.execute(
|
|
287
|
-
"""
|
|
288
|
-
INSERT INTO customer_field_values
|
|
289
|
-
(entity_type, entity_id, field_key, value, normalized_value, display_value, updated_at)
|
|
290
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
291
|
-
ON CONFLICT(entity_type, entity_id, field_key) DO UPDATE SET
|
|
292
|
-
value = excluded.value,
|
|
293
|
-
normalized_value = excluded.normalized_value,
|
|
294
|
-
display_value = excluded.display_value,
|
|
295
|
-
updated_at = excluded.updated_at
|
|
296
|
-
""",
|
|
297
|
-
(entity_type, entity_id, field_key, value, normalized_value, display_value, now),
|
|
298
|
-
)
|
|
299
|
-
|
|
300
|
-
def list_field_values(self, entity_type: str = "customer") -> list[dict[str, Any]]:
|
|
301
|
-
with self.connect() as conn:
|
|
302
|
-
rows = conn.execute(
|
|
303
|
-
"""
|
|
304
|
-
SELECT * FROM customer_field_values
|
|
305
|
-
WHERE entity_type = ?
|
|
306
|
-
ORDER BY updated_at DESC
|
|
307
|
-
""",
|
|
308
|
-
(entity_type,),
|
|
309
|
-
).fetchall()
|
|
310
|
-
return [dict(row) for row in rows]
|
|
311
|
-
|
|
312
|
-
def create_business_record(self, payload: dict[str, Any], now: str) -> str:
|
|
313
|
-
record_id = f"rec_{uuid.uuid4().hex[:12]}"
|
|
314
|
-
with self.connect() as conn:
|
|
315
|
-
conn.execute(
|
|
316
|
-
"""
|
|
317
|
-
INSERT INTO business_records
|
|
318
|
-
(record_id, customer_id, record_type, status, record_date, amount, title, raw_payload_json, created_at, updated_at)
|
|
319
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
320
|
-
""",
|
|
321
|
-
(
|
|
322
|
-
record_id,
|
|
323
|
-
payload["customer_id"],
|
|
324
|
-
payload["record_type"],
|
|
325
|
-
payload.get("status"),
|
|
326
|
-
payload.get("record_date"),
|
|
327
|
-
payload.get("amount"),
|
|
328
|
-
payload.get("title"),
|
|
329
|
-
json.dumps(payload.get("raw_payload", {}), ensure_ascii=False),
|
|
330
|
-
now,
|
|
331
|
-
now,
|
|
332
|
-
),
|
|
333
|
-
)
|
|
334
|
-
return record_id
|
|
335
|
-
|
|
336
|
-
def list_business_records(self, customer_id: str | None = None) -> list[dict[str, Any]]:
|
|
337
|
-
with self.connect() as conn:
|
|
338
|
-
if customer_id:
|
|
339
|
-
rows = conn.execute(
|
|
340
|
-
"SELECT * FROM business_records WHERE customer_id = ? ORDER BY updated_at DESC",
|
|
341
|
-
(customer_id,),
|
|
342
|
-
).fetchall()
|
|
343
|
-
else:
|
|
344
|
-
rows = conn.execute(
|
|
345
|
-
"SELECT * FROM business_records ORDER BY updated_at DESC"
|
|
346
|
-
).fetchall()
|
|
347
|
-
results = []
|
|
348
|
-
for row in rows:
|
|
349
|
-
item = dict(row)
|
|
350
|
-
item["raw_payload"] = json.loads(item.pop("raw_payload_json"))
|
|
351
|
-
results.append(item)
|
|
352
|
-
return results
|
|
353
|
-
|
|
354
|
-
def replace_reminders_for_customer(self, customer_id: str, reminder_type: str, reminders: list[dict[str, Any]], now: str) -> None:
|
|
355
|
-
with self.connect() as conn:
|
|
356
|
-
conn.execute(
|
|
357
|
-
"DELETE FROM reminders WHERE customer_id = ? AND reminder_type = ?",
|
|
358
|
-
(customer_id, reminder_type),
|
|
359
|
-
)
|
|
360
|
-
for reminder in reminders:
|
|
361
|
-
reminder_id = f"rmd_{uuid.uuid4().hex[:12]}"
|
|
362
|
-
conn.execute(
|
|
363
|
-
"""
|
|
364
|
-
INSERT INTO reminders
|
|
365
|
-
(reminder_id, customer_id, reminder_type, title, trigger_at, cron_expr, message, recurring, scheduled, external_task_name, created_at)
|
|
366
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
367
|
-
""",
|
|
368
|
-
(
|
|
369
|
-
reminder_id,
|
|
370
|
-
customer_id,
|
|
371
|
-
reminder_type,
|
|
372
|
-
reminder["title"],
|
|
373
|
-
reminder.get("trigger_at"),
|
|
374
|
-
reminder["cron_expr"],
|
|
375
|
-
reminder["message"],
|
|
376
|
-
int(bool(reminder.get("recurring", False))),
|
|
377
|
-
int(bool(reminder.get("scheduled", False))),
|
|
378
|
-
reminder.get("external_task_name"),
|
|
379
|
-
now,
|
|
380
|
-
),
|
|
381
|
-
)
|
|
382
|
-
|
|
383
|
-
def list_reminders(self, customer_id: str | None = None) -> list[dict[str, Any]]:
|
|
384
|
-
with self.connect() as conn:
|
|
385
|
-
if customer_id:
|
|
386
|
-
rows = conn.execute(
|
|
387
|
-
"SELECT * FROM reminders WHERE customer_id = ? ORDER BY created_at DESC",
|
|
388
|
-
(customer_id,),
|
|
389
|
-
).fetchall()
|
|
390
|
-
else:
|
|
391
|
-
rows = conn.execute("SELECT * FROM reminders ORDER BY created_at DESC").fetchall()
|
|
392
|
-
return [dict(row) for row in rows]
|
|
393
|
-
|
|
394
|
-
def _customer_row_to_dict(self, row: sqlite3.Row) -> dict[str, Any]:
|
|
395
|
-
item = dict(row)
|
|
396
|
-
item["tags"] = json.loads(item.pop("tags_json"))
|
|
397
|
-
return item
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import sqlite3
|
|
5
|
+
import uuid
|
|
6
|
+
from contextlib import contextmanager
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, Iterator
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
SCHEMA_SQL = """
|
|
12
|
+
CREATE TABLE IF NOT EXISTS customers (
|
|
13
|
+
customer_id TEXT PRIMARY KEY,
|
|
14
|
+
name TEXT NOT NULL,
|
|
15
|
+
primary_phone TEXT,
|
|
16
|
+
birthday TEXT,
|
|
17
|
+
tags_json TEXT NOT NULL DEFAULT '[]',
|
|
18
|
+
notes TEXT,
|
|
19
|
+
created_at TEXT NOT NULL,
|
|
20
|
+
updated_at TEXT NOT NULL
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
CREATE TABLE IF NOT EXISTS customer_field_defs (
|
|
24
|
+
field_key TEXT PRIMARY KEY,
|
|
25
|
+
label TEXT NOT NULL,
|
|
26
|
+
value_type TEXT NOT NULL,
|
|
27
|
+
entity_type TEXT NOT NULL DEFAULT 'customer',
|
|
28
|
+
is_required INTEGER NOT NULL DEFAULT 0,
|
|
29
|
+
source TEXT NOT NULL DEFAULT 'user_confirmed',
|
|
30
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
31
|
+
aliases_json TEXT NOT NULL DEFAULT '[]',
|
|
32
|
+
created_at TEXT NOT NULL,
|
|
33
|
+
updated_at TEXT NOT NULL
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
CREATE TABLE IF NOT EXISTS customer_field_values (
|
|
37
|
+
entity_type TEXT NOT NULL DEFAULT 'customer',
|
|
38
|
+
entity_id TEXT NOT NULL,
|
|
39
|
+
field_key TEXT NOT NULL,
|
|
40
|
+
value TEXT,
|
|
41
|
+
normalized_value TEXT,
|
|
42
|
+
display_value TEXT,
|
|
43
|
+
updated_at TEXT NOT NULL,
|
|
44
|
+
PRIMARY KEY (entity_type, entity_id, field_key)
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
CREATE TABLE IF NOT EXISTS business_records (
|
|
48
|
+
record_id TEXT PRIMARY KEY,
|
|
49
|
+
customer_id TEXT NOT NULL,
|
|
50
|
+
record_type TEXT NOT NULL,
|
|
51
|
+
status TEXT,
|
|
52
|
+
record_date TEXT,
|
|
53
|
+
amount TEXT,
|
|
54
|
+
title TEXT,
|
|
55
|
+
raw_payload_json TEXT NOT NULL DEFAULT '{}',
|
|
56
|
+
created_at TEXT NOT NULL,
|
|
57
|
+
updated_at TEXT NOT NULL
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
CREATE TABLE IF NOT EXISTS reminders (
|
|
61
|
+
reminder_id TEXT PRIMARY KEY,
|
|
62
|
+
customer_id TEXT NOT NULL,
|
|
63
|
+
reminder_type TEXT NOT NULL,
|
|
64
|
+
title TEXT NOT NULL,
|
|
65
|
+
trigger_at TEXT,
|
|
66
|
+
cron_expr TEXT NOT NULL,
|
|
67
|
+
message TEXT NOT NULL,
|
|
68
|
+
recurring INTEGER NOT NULL DEFAULT 0,
|
|
69
|
+
scheduled INTEGER NOT NULL DEFAULT 0,
|
|
70
|
+
external_task_name TEXT,
|
|
71
|
+
created_at TEXT NOT NULL
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
CREATE TABLE IF NOT EXISTS import_jobs (
|
|
75
|
+
import_job_id TEXT PRIMARY KEY,
|
|
76
|
+
source_file TEXT NOT NULL,
|
|
77
|
+
mode TEXT NOT NULL,
|
|
78
|
+
summary_json TEXT NOT NULL,
|
|
79
|
+
created_at TEXT NOT NULL
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
CREATE INDEX IF NOT EXISTS idx_customers_name ON customers(name);
|
|
83
|
+
CREATE INDEX IF NOT EXISTS idx_customers_phone ON customers(primary_phone);
|
|
84
|
+
CREATE INDEX IF NOT EXISTS idx_field_values_lookup ON customer_field_values(field_key, normalized_value);
|
|
85
|
+
CREATE INDEX IF NOT EXISTS idx_records_customer ON business_records(customer_id);
|
|
86
|
+
CREATE INDEX IF NOT EXISTS idx_reminders_customer ON reminders(customer_id);
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class SQLiteRepository:
|
|
91
|
+
def __init__(self, db_path: str | Path):
|
|
92
|
+
self.db_path = Path(db_path)
|
|
93
|
+
self.db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
94
|
+
|
|
95
|
+
@contextmanager
|
|
96
|
+
def connect(self) -> Iterator[sqlite3.Connection]:
|
|
97
|
+
conn = sqlite3.connect(self.db_path)
|
|
98
|
+
conn.row_factory = sqlite3.Row
|
|
99
|
+
try:
|
|
100
|
+
yield conn
|
|
101
|
+
conn.commit()
|
|
102
|
+
finally:
|
|
103
|
+
conn.close()
|
|
104
|
+
|
|
105
|
+
def init_db(self) -> None:
|
|
106
|
+
with self.connect() as conn:
|
|
107
|
+
conn.executescript(SCHEMA_SQL)
|
|
108
|
+
|
|
109
|
+
def save_import_job(self, source_file: str, mode: str, summary: dict[str, Any], created_at: str) -> str:
|
|
110
|
+
job_id = f"imp_{uuid.uuid4().hex[:12]}"
|
|
111
|
+
with self.connect() as conn:
|
|
112
|
+
conn.execute(
|
|
113
|
+
"""
|
|
114
|
+
INSERT INTO import_jobs (import_job_id, source_file, mode, summary_json, created_at)
|
|
115
|
+
VALUES (?, ?, ?, ?, ?)
|
|
116
|
+
""",
|
|
117
|
+
(job_id, source_file, mode, json.dumps(summary, ensure_ascii=False), created_at),
|
|
118
|
+
)
|
|
119
|
+
return job_id
|
|
120
|
+
|
|
121
|
+
def list_field_defs(self, entity_type: str = "customer") -> list[dict[str, Any]]:
|
|
122
|
+
with self.connect() as conn:
|
|
123
|
+
rows = conn.execute(
|
|
124
|
+
"""
|
|
125
|
+
SELECT * FROM customer_field_defs
|
|
126
|
+
WHERE entity_type = ?
|
|
127
|
+
ORDER BY label COLLATE NOCASE
|
|
128
|
+
""",
|
|
129
|
+
(entity_type,),
|
|
130
|
+
).fetchall()
|
|
131
|
+
results = []
|
|
132
|
+
for row in rows:
|
|
133
|
+
item = dict(row)
|
|
134
|
+
item["aliases"] = json.loads(item.pop("aliases_json"))
|
|
135
|
+
results.append(item)
|
|
136
|
+
return results
|
|
137
|
+
|
|
138
|
+
def upsert_field_def(self, field_def: dict[str, Any], now: str) -> None:
|
|
139
|
+
aliases_json = json.dumps(field_def.get("aliases", []), ensure_ascii=False)
|
|
140
|
+
with self.connect() as conn:
|
|
141
|
+
conn.execute(
|
|
142
|
+
"""
|
|
143
|
+
INSERT INTO customer_field_defs
|
|
144
|
+
(field_key, label, value_type, entity_type, is_required, source, status, aliases_json, created_at, updated_at)
|
|
145
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
146
|
+
ON CONFLICT(field_key) DO UPDATE SET
|
|
147
|
+
label = excluded.label,
|
|
148
|
+
value_type = excluded.value_type,
|
|
149
|
+
entity_type = excluded.entity_type,
|
|
150
|
+
is_required = excluded.is_required,
|
|
151
|
+
source = excluded.source,
|
|
152
|
+
status = excluded.status,
|
|
153
|
+
aliases_json = excluded.aliases_json,
|
|
154
|
+
updated_at = excluded.updated_at
|
|
155
|
+
""",
|
|
156
|
+
(
|
|
157
|
+
field_def["field_key"],
|
|
158
|
+
field_def["label"],
|
|
159
|
+
field_def["value_type"],
|
|
160
|
+
field_def.get("entity_type", "customer"),
|
|
161
|
+
int(bool(field_def.get("is_required", False))),
|
|
162
|
+
field_def.get("source", "user_confirmed"),
|
|
163
|
+
field_def.get("status", "active"),
|
|
164
|
+
aliases_json,
|
|
165
|
+
now,
|
|
166
|
+
now,
|
|
167
|
+
),
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
def upsert_customer(self, payload: dict[str, Any], now: str) -> tuple[str, bool]:
|
|
171
|
+
customer_id = payload.get("customer_id")
|
|
172
|
+
existing = self.find_customer_for_identity(
|
|
173
|
+
name=payload["name"],
|
|
174
|
+
primary_phone=payload.get("primary_phone"),
|
|
175
|
+
birthday=payload.get("birthday"),
|
|
176
|
+
)
|
|
177
|
+
created = False
|
|
178
|
+
if existing:
|
|
179
|
+
customer_id = existing["customer_id"]
|
|
180
|
+
if not customer_id:
|
|
181
|
+
customer_id = f"cus_{uuid.uuid4().hex[:12]}"
|
|
182
|
+
created = True
|
|
183
|
+
|
|
184
|
+
with self.connect() as conn:
|
|
185
|
+
conn.execute(
|
|
186
|
+
"""
|
|
187
|
+
INSERT INTO customers
|
|
188
|
+
(customer_id, name, primary_phone, birthday, tags_json, notes, created_at, updated_at)
|
|
189
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
190
|
+
ON CONFLICT(customer_id) DO UPDATE SET
|
|
191
|
+
name = excluded.name,
|
|
192
|
+
primary_phone = COALESCE(excluded.primary_phone, customers.primary_phone),
|
|
193
|
+
birthday = COALESCE(excluded.birthday, customers.birthday),
|
|
194
|
+
tags_json = excluded.tags_json,
|
|
195
|
+
notes = COALESCE(excluded.notes, customers.notes),
|
|
196
|
+
updated_at = excluded.updated_at
|
|
197
|
+
""",
|
|
198
|
+
(
|
|
199
|
+
customer_id,
|
|
200
|
+
payload["name"],
|
|
201
|
+
payload.get("primary_phone"),
|
|
202
|
+
payload.get("birthday"),
|
|
203
|
+
json.dumps(payload.get("tags", []), ensure_ascii=False),
|
|
204
|
+
payload.get("notes"),
|
|
205
|
+
now,
|
|
206
|
+
now,
|
|
207
|
+
),
|
|
208
|
+
)
|
|
209
|
+
return customer_id, created
|
|
210
|
+
|
|
211
|
+
def find_customer_for_identity(
|
|
212
|
+
self,
|
|
213
|
+
*,
|
|
214
|
+
name: str,
|
|
215
|
+
primary_phone: str | None = None,
|
|
216
|
+
birthday: str | None = None,
|
|
217
|
+
) -> dict[str, Any] | None:
|
|
218
|
+
with self.connect() as conn:
|
|
219
|
+
if primary_phone:
|
|
220
|
+
row = conn.execute(
|
|
221
|
+
"""
|
|
222
|
+
SELECT * FROM customers
|
|
223
|
+
WHERE name = ? AND primary_phone = ?
|
|
224
|
+
LIMIT 1
|
|
225
|
+
""",
|
|
226
|
+
(name, primary_phone),
|
|
227
|
+
).fetchone()
|
|
228
|
+
if row:
|
|
229
|
+
return self._customer_row_to_dict(row)
|
|
230
|
+
if birthday:
|
|
231
|
+
row = conn.execute(
|
|
232
|
+
"""
|
|
233
|
+
SELECT * FROM customers
|
|
234
|
+
WHERE name = ? AND birthday = ?
|
|
235
|
+
LIMIT 1
|
|
236
|
+
""",
|
|
237
|
+
(name, birthday),
|
|
238
|
+
).fetchone()
|
|
239
|
+
if row:
|
|
240
|
+
return self._customer_row_to_dict(row)
|
|
241
|
+
row = conn.execute(
|
|
242
|
+
"""
|
|
243
|
+
SELECT * FROM customers
|
|
244
|
+
WHERE name = ?
|
|
245
|
+
LIMIT 1
|
|
246
|
+
""",
|
|
247
|
+
(name,),
|
|
248
|
+
).fetchone()
|
|
249
|
+
return self._customer_row_to_dict(row) if row else None
|
|
250
|
+
|
|
251
|
+
def get_customer(self, customer_id: str) -> dict[str, Any] | None:
|
|
252
|
+
with self.connect() as conn:
|
|
253
|
+
row = conn.execute("SELECT * FROM customers WHERE customer_id = ?", (customer_id,)).fetchone()
|
|
254
|
+
return self._customer_row_to_dict(row) if row else None
|
|
255
|
+
|
|
256
|
+
def list_customers(self) -> list[dict[str, Any]]:
|
|
257
|
+
with self.connect() as conn:
|
|
258
|
+
rows = conn.execute(
|
|
259
|
+
"SELECT * FROM customers ORDER BY updated_at DESC, name COLLATE NOCASE"
|
|
260
|
+
).fetchall()
|
|
261
|
+
return [self._customer_row_to_dict(row) for row in rows]
|
|
262
|
+
|
|
263
|
+
def delete_customer(self, customer_id: str) -> bool:
|
|
264
|
+
with self.connect() as conn:
|
|
265
|
+
deleted = conn.execute("DELETE FROM customers WHERE customer_id = ?", (customer_id,)).rowcount
|
|
266
|
+
conn.execute(
|
|
267
|
+
"DELETE FROM customer_field_values WHERE entity_type = 'customer' AND entity_id = ?",
|
|
268
|
+
(customer_id,),
|
|
269
|
+
)
|
|
270
|
+
conn.execute("DELETE FROM business_records WHERE customer_id = ?", (customer_id,))
|
|
271
|
+
conn.execute("DELETE FROM reminders WHERE customer_id = ?", (customer_id,))
|
|
272
|
+
return deleted > 0
|
|
273
|
+
|
|
274
|
+
def upsert_field_value(
|
|
275
|
+
self,
|
|
276
|
+
*,
|
|
277
|
+
entity_type: str,
|
|
278
|
+
entity_id: str,
|
|
279
|
+
field_key: str,
|
|
280
|
+
value: str | None,
|
|
281
|
+
normalized_value: str | None,
|
|
282
|
+
display_value: str | None,
|
|
283
|
+
now: str,
|
|
284
|
+
) -> None:
|
|
285
|
+
with self.connect() as conn:
|
|
286
|
+
conn.execute(
|
|
287
|
+
"""
|
|
288
|
+
INSERT INTO customer_field_values
|
|
289
|
+
(entity_type, entity_id, field_key, value, normalized_value, display_value, updated_at)
|
|
290
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
291
|
+
ON CONFLICT(entity_type, entity_id, field_key) DO UPDATE SET
|
|
292
|
+
value = excluded.value,
|
|
293
|
+
normalized_value = excluded.normalized_value,
|
|
294
|
+
display_value = excluded.display_value,
|
|
295
|
+
updated_at = excluded.updated_at
|
|
296
|
+
""",
|
|
297
|
+
(entity_type, entity_id, field_key, value, normalized_value, display_value, now),
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
def list_field_values(self, entity_type: str = "customer") -> list[dict[str, Any]]:
|
|
301
|
+
with self.connect() as conn:
|
|
302
|
+
rows = conn.execute(
|
|
303
|
+
"""
|
|
304
|
+
SELECT * FROM customer_field_values
|
|
305
|
+
WHERE entity_type = ?
|
|
306
|
+
ORDER BY updated_at DESC
|
|
307
|
+
""",
|
|
308
|
+
(entity_type,),
|
|
309
|
+
).fetchall()
|
|
310
|
+
return [dict(row) for row in rows]
|
|
311
|
+
|
|
312
|
+
def create_business_record(self, payload: dict[str, Any], now: str) -> str:
|
|
313
|
+
record_id = f"rec_{uuid.uuid4().hex[:12]}"
|
|
314
|
+
with self.connect() as conn:
|
|
315
|
+
conn.execute(
|
|
316
|
+
"""
|
|
317
|
+
INSERT INTO business_records
|
|
318
|
+
(record_id, customer_id, record_type, status, record_date, amount, title, raw_payload_json, created_at, updated_at)
|
|
319
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
320
|
+
""",
|
|
321
|
+
(
|
|
322
|
+
record_id,
|
|
323
|
+
payload["customer_id"],
|
|
324
|
+
payload["record_type"],
|
|
325
|
+
payload.get("status"),
|
|
326
|
+
payload.get("record_date"),
|
|
327
|
+
payload.get("amount"),
|
|
328
|
+
payload.get("title"),
|
|
329
|
+
json.dumps(payload.get("raw_payload", {}), ensure_ascii=False),
|
|
330
|
+
now,
|
|
331
|
+
now,
|
|
332
|
+
),
|
|
333
|
+
)
|
|
334
|
+
return record_id
|
|
335
|
+
|
|
336
|
+
def list_business_records(self, customer_id: str | None = None) -> list[dict[str, Any]]:
|
|
337
|
+
with self.connect() as conn:
|
|
338
|
+
if customer_id:
|
|
339
|
+
rows = conn.execute(
|
|
340
|
+
"SELECT * FROM business_records WHERE customer_id = ? ORDER BY updated_at DESC",
|
|
341
|
+
(customer_id,),
|
|
342
|
+
).fetchall()
|
|
343
|
+
else:
|
|
344
|
+
rows = conn.execute(
|
|
345
|
+
"SELECT * FROM business_records ORDER BY updated_at DESC"
|
|
346
|
+
).fetchall()
|
|
347
|
+
results = []
|
|
348
|
+
for row in rows:
|
|
349
|
+
item = dict(row)
|
|
350
|
+
item["raw_payload"] = json.loads(item.pop("raw_payload_json"))
|
|
351
|
+
results.append(item)
|
|
352
|
+
return results
|
|
353
|
+
|
|
354
|
+
def replace_reminders_for_customer(self, customer_id: str, reminder_type: str, reminders: list[dict[str, Any]], now: str) -> None:
|
|
355
|
+
with self.connect() as conn:
|
|
356
|
+
conn.execute(
|
|
357
|
+
"DELETE FROM reminders WHERE customer_id = ? AND reminder_type = ?",
|
|
358
|
+
(customer_id, reminder_type),
|
|
359
|
+
)
|
|
360
|
+
for reminder in reminders:
|
|
361
|
+
reminder_id = f"rmd_{uuid.uuid4().hex[:12]}"
|
|
362
|
+
conn.execute(
|
|
363
|
+
"""
|
|
364
|
+
INSERT INTO reminders
|
|
365
|
+
(reminder_id, customer_id, reminder_type, title, trigger_at, cron_expr, message, recurring, scheduled, external_task_name, created_at)
|
|
366
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
367
|
+
""",
|
|
368
|
+
(
|
|
369
|
+
reminder_id,
|
|
370
|
+
customer_id,
|
|
371
|
+
reminder_type,
|
|
372
|
+
reminder["title"],
|
|
373
|
+
reminder.get("trigger_at"),
|
|
374
|
+
reminder["cron_expr"],
|
|
375
|
+
reminder["message"],
|
|
376
|
+
int(bool(reminder.get("recurring", False))),
|
|
377
|
+
int(bool(reminder.get("scheduled", False))),
|
|
378
|
+
reminder.get("external_task_name"),
|
|
379
|
+
now,
|
|
380
|
+
),
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
def list_reminders(self, customer_id: str | None = None) -> list[dict[str, Any]]:
|
|
384
|
+
with self.connect() as conn:
|
|
385
|
+
if customer_id:
|
|
386
|
+
rows = conn.execute(
|
|
387
|
+
"SELECT * FROM reminders WHERE customer_id = ? ORDER BY created_at DESC",
|
|
388
|
+
(customer_id,),
|
|
389
|
+
).fetchall()
|
|
390
|
+
else:
|
|
391
|
+
rows = conn.execute("SELECT * FROM reminders ORDER BY created_at DESC").fetchall()
|
|
392
|
+
return [dict(row) for row in rows]
|
|
393
|
+
|
|
394
|
+
def _customer_row_to_dict(self, row: sqlite3.Row) -> dict[str, Any]:
|
|
395
|
+
item = dict(row)
|
|
396
|
+
item["tags"] = json.loads(item.pop("tags_json"))
|
|
397
|
+
return item
|