bps-kit 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (368) hide show
  1. package/package.json +1 -1
  2. package/templates/.agents/agents/backend-specialist.md +263 -0
  3. package/templates/.agents/agents/code-archaeologist.md +106 -0
  4. package/templates/.agents/agents/database-architect.md +226 -0
  5. package/templates/.agents/agents/debugger.md +225 -0
  6. package/templates/.agents/agents/devops-engineer.md +242 -0
  7. package/templates/.agents/agents/documentation-writer.md +104 -0
  8. package/templates/.agents/agents/explorer-agent.md +73 -0
  9. package/templates/.agents/agents/frontend-specialist.md +593 -0
  10. package/templates/.agents/agents/game-developer.md +162 -0
  11. package/templates/.agents/agents/mobile-developer.md +377 -0
  12. package/templates/.agents/agents/orchestrator.md +416 -0
  13. package/templates/.agents/agents/penetration-tester.md +188 -0
  14. package/templates/.agents/agents/performance-optimizer.md +187 -0
  15. package/templates/.agents/agents/product-manager.md +112 -0
  16. package/templates/.agents/agents/product-owner.md +95 -0
  17. package/templates/.agents/agents/project-planner.md +406 -0
  18. package/templates/.agents/agents/qa-automation-engineer.md +103 -0
  19. package/templates/.agents/agents/security-auditor.md +170 -0
  20. package/templates/.agents/agents/seo-specialist.md +111 -0
  21. package/templates/.agents/agents/test-engineer.md +158 -0
  22. package/templates/.agents/rules/GEMINI.md +219 -0
  23. package/templates/.agents/scripts/auto_preview.py +148 -0
  24. package/templates/.agents/scripts/checklist.py +217 -0
  25. package/templates/.agents/scripts/session_manager.py +120 -0
  26. package/templates/.agents/scripts/verify_all.py +327 -0
  27. package/templates/.agents/workflows/brainstorm.md +113 -0
  28. package/templates/.agents/workflows/create.md +59 -0
  29. package/templates/.agents/workflows/debug.md +103 -0
  30. package/templates/.agents/workflows/deploy.md +176 -0
  31. package/templates/.agents/workflows/enhance.md +63 -0
  32. package/templates/.agents/workflows/orchestrate.md +237 -0
  33. package/templates/.agents/workflows/plan.md +89 -0
  34. package/templates/.agents/workflows/preview.md +81 -0
  35. package/templates/.agents/workflows/setup-brain.md +39 -0
  36. package/templates/.agents/workflows/status.md +86 -0
  37. package/templates/.agents/workflows/test.md +144 -0
  38. package/templates/.agents/workflows/ui-ux-pro-max.md +296 -0
  39. package/templates/skills_normal/api-patterns/scripts/api_validator.py +211 -0
  40. package/templates/skills_normal/database-design/scripts/schema_validator.py +172 -0
  41. package/templates/skills_normal/frontend-design/scripts/accessibility_checker.py +183 -0
  42. package/templates/skills_normal/frontend-design/scripts/ux_audit.py +722 -0
  43. package/templates/skills_normal/git-pushing/scripts/smart_commit.sh +19 -0
  44. package/templates/skills_normal/lint-and-validate/scripts/lint_runner.py +184 -0
  45. package/templates/skills_normal/lint-and-validate/scripts/type_coverage.py +173 -0
  46. package/templates/skills_normal/performance-profiling/scripts/lighthouse_audit.py +76 -0
  47. package/templates/skills_normal/senior-fullstack/scripts/code_quality_analyzer.py +114 -0
  48. package/templates/skills_normal/senior-fullstack/scripts/fullstack_scaffolder.py +114 -0
  49. package/templates/skills_normal/senior-fullstack/scripts/project_scaffolder.py +114 -0
  50. package/templates/skills_normal/seo-fundamentals/scripts/seo_checker.py +219 -0
  51. package/templates/skills_normal/testing-patterns/scripts/test_runner.py +219 -0
  52. package/templates/skills_normal/vulnerability-scanner/scripts/security_scan.py +458 -0
  53. package/templates/vault/007/scripts/config.py +472 -0
  54. package/templates/vault/007/scripts/full_audit.py +1306 -0
  55. package/templates/vault/007/scripts/quick_scan.py +481 -0
  56. package/templates/vault/007/scripts/requirements.txt +26 -0
  57. package/templates/vault/007/scripts/scanners/__init__.py +0 -0
  58. package/templates/vault/007/scripts/scanners/dependency_scanner.py +1305 -0
  59. package/templates/vault/007/scripts/scanners/injection_scanner.py +1104 -0
  60. package/templates/vault/007/scripts/scanners/secrets_scanner.py +1008 -0
  61. package/templates/vault/007/scripts/score_calculator.py +693 -0
  62. package/templates/vault/agent-orchestrator/scripts/match_skills.py +329 -0
  63. package/templates/vault/agent-orchestrator/scripts/orchestrate.py +304 -0
  64. package/templates/vault/agent-orchestrator/scripts/requirements.txt +1 -0
  65. package/templates/vault/agent-orchestrator/scripts/scan_registry.py +508 -0
  66. package/templates/vault/ai-studio-image/scripts/config.py +613 -0
  67. package/templates/vault/ai-studio-image/scripts/generate.py +630 -0
  68. package/templates/vault/ai-studio-image/scripts/prompt_engine.py +424 -0
  69. package/templates/vault/ai-studio-image/scripts/requirements.txt +4 -0
  70. package/templates/vault/ai-studio-image/scripts/templates.py +349 -0
  71. package/templates/vault/android_ui_verification/scripts/verify_ui.sh +32 -0
  72. package/templates/vault/apify-audience-analysis/reference/scripts/run_actor.js +363 -0
  73. package/templates/vault/apify-brand-reputation-monitoring/reference/scripts/run_actor.js +363 -0
  74. package/templates/vault/apify-competitor-intelligence/reference/scripts/run_actor.js +363 -0
  75. package/templates/vault/apify-content-analytics/reference/scripts/run_actor.js +363 -0
  76. package/templates/vault/apify-ecommerce/reference/scripts/package.json +3 -0
  77. package/templates/vault/apify-ecommerce/reference/scripts/run_actor.js +369 -0
  78. package/templates/vault/apify-influencer-discovery/reference/scripts/run_actor.js +363 -0
  79. package/templates/vault/apify-lead-generation/reference/scripts/run_actor.js +363 -0
  80. package/templates/vault/apify-market-research/reference/scripts/run_actor.js +363 -0
  81. package/templates/vault/apify-trend-analysis/reference/scripts/run_actor.js +363 -0
  82. package/templates/vault/apify-ultimate-scraper/reference/scripts/run_actor.js +363 -0
  83. package/templates/vault/audio-transcriber/scripts/install-requirements.sh +190 -0
  84. package/templates/vault/audio-transcriber/scripts/transcribe.py +486 -0
  85. package/templates/vault/claude-monitor/scripts/api_bench.py +240 -0
  86. package/templates/vault/claude-monitor/scripts/config.py +69 -0
  87. package/templates/vault/claude-monitor/scripts/health_check.py +362 -0
  88. package/templates/vault/claude-monitor/scripts/monitor.py +296 -0
  89. package/templates/vault/content-creator/scripts/brand_voice_analyzer.py +185 -0
  90. package/templates/vault/content-creator/scripts/seo_optimizer.py +419 -0
  91. package/templates/vault/context-agent/scripts/active_context.py +227 -0
  92. package/templates/vault/context-agent/scripts/compressor.py +149 -0
  93. package/templates/vault/context-agent/scripts/config.py +69 -0
  94. package/templates/vault/context-agent/scripts/context_loader.py +155 -0
  95. package/templates/vault/context-agent/scripts/context_manager.py +302 -0
  96. package/templates/vault/context-agent/scripts/models.py +103 -0
  97. package/templates/vault/context-agent/scripts/project_registry.py +132 -0
  98. package/templates/vault/context-agent/scripts/requirements.txt +6 -0
  99. package/templates/vault/context-agent/scripts/search.py +115 -0
  100. package/templates/vault/context-agent/scripts/session_parser.py +206 -0
  101. package/templates/vault/context-agent/scripts/session_summary.py +319 -0
  102. package/templates/vault/context-guardian/scripts/context_snapshot.py +229 -0
  103. package/templates/vault/docx/ooxml/scripts/pack.py +159 -0
  104. package/templates/vault/docx/ooxml/scripts/unpack.py +29 -0
  105. package/templates/vault/docx/ooxml/scripts/validate.py +69 -0
  106. package/templates/vault/docx/ooxml/scripts/validation/__init__.py +15 -0
  107. package/templates/vault/docx/ooxml/scripts/validation/base.py +951 -0
  108. package/templates/vault/docx/ooxml/scripts/validation/docx.py +274 -0
  109. package/templates/vault/docx/ooxml/scripts/validation/pptx.py +315 -0
  110. package/templates/vault/docx/ooxml/scripts/validation/redlining.py +279 -0
  111. package/templates/vault/docx/scripts/__init__.py +1 -0
  112. package/templates/vault/docx/scripts/document.py +1276 -0
  113. package/templates/vault/docx/scripts/templates/comments.xml +3 -0
  114. package/templates/vault/docx/scripts/templates/commentsExtended.xml +3 -0
  115. package/templates/vault/docx/scripts/templates/commentsExtensible.xml +3 -0
  116. package/templates/vault/docx/scripts/templates/commentsIds.xml +3 -0
  117. package/templates/vault/docx/scripts/templates/people.xml +3 -0
  118. package/templates/vault/docx/scripts/utilities.py +374 -0
  119. package/templates/vault/docx-official/ooxml/scripts/pack.py +159 -0
  120. package/templates/vault/docx-official/ooxml/scripts/unpack.py +29 -0
  121. package/templates/vault/docx-official/ooxml/scripts/validate.py +69 -0
  122. package/templates/vault/docx-official/ooxml/scripts/validation/__init__.py +15 -0
  123. package/templates/vault/docx-official/ooxml/scripts/validation/base.py +951 -0
  124. package/templates/vault/docx-official/ooxml/scripts/validation/docx.py +274 -0
  125. package/templates/vault/docx-official/ooxml/scripts/validation/pptx.py +315 -0
  126. package/templates/vault/docx-official/ooxml/scripts/validation/redlining.py +279 -0
  127. package/templates/vault/docx-official/scripts/__init__.py +1 -0
  128. package/templates/vault/docx-official/scripts/document.py +1276 -0
  129. package/templates/vault/docx-official/scripts/templates/comments.xml +3 -0
  130. package/templates/vault/docx-official/scripts/templates/commentsExtended.xml +3 -0
  131. package/templates/vault/docx-official/scripts/templates/commentsExtensible.xml +3 -0
  132. package/templates/vault/docx-official/scripts/templates/commentsIds.xml +3 -0
  133. package/templates/vault/docx-official/scripts/templates/people.xml +3 -0
  134. package/templates/vault/docx-official/scripts/utilities.py +374 -0
  135. package/templates/vault/geo-fundamentals/scripts/geo_checker.py +289 -0
  136. package/templates/vault/helm-chart-scaffolding/scripts/validate-chart.sh +244 -0
  137. package/templates/vault/i18n-localization/scripts/i18n_checker.py +241 -0
  138. package/templates/vault/instagram/scripts/account_setup.py +233 -0
  139. package/templates/vault/instagram/scripts/analyze.py +221 -0
  140. package/templates/vault/instagram/scripts/api_client.py +444 -0
  141. package/templates/vault/instagram/scripts/auth.py +411 -0
  142. package/templates/vault/instagram/scripts/comments.py +160 -0
  143. package/templates/vault/instagram/scripts/config.py +111 -0
  144. package/templates/vault/instagram/scripts/db.py +467 -0
  145. package/templates/vault/instagram/scripts/export.py +138 -0
  146. package/templates/vault/instagram/scripts/governance.py +233 -0
  147. package/templates/vault/instagram/scripts/hashtags.py +114 -0
  148. package/templates/vault/instagram/scripts/insights.py +170 -0
  149. package/templates/vault/instagram/scripts/media.py +65 -0
  150. package/templates/vault/instagram/scripts/messages.py +103 -0
  151. package/templates/vault/instagram/scripts/profile.py +58 -0
  152. package/templates/vault/instagram/scripts/publish.py +449 -0
  153. package/templates/vault/instagram/scripts/requirements.txt +5 -0
  154. package/templates/vault/instagram/scripts/run_all.py +189 -0
  155. package/templates/vault/instagram/scripts/schedule.py +189 -0
  156. package/templates/vault/instagram/scripts/serve_api.py +234 -0
  157. package/templates/vault/instagram/scripts/templates.py +155 -0
  158. package/templates/vault/junta-leiloeiros/scripts/db.py +216 -0
  159. package/templates/vault/junta-leiloeiros/scripts/export.py +137 -0
  160. package/templates/vault/junta-leiloeiros/scripts/requirements.txt +15 -0
  161. package/templates/vault/junta-leiloeiros/scripts/run_all.py +190 -0
  162. package/templates/vault/junta-leiloeiros/scripts/scraper/__init__.py +4 -0
  163. package/templates/vault/junta-leiloeiros/scripts/scraper/base_scraper.py +209 -0
  164. package/templates/vault/junta-leiloeiros/scripts/scraper/generic_scraper.py +110 -0
  165. package/templates/vault/junta-leiloeiros/scripts/scraper/jucap.py +110 -0
  166. package/templates/vault/junta-leiloeiros/scripts/scraper/juceac.py +72 -0
  167. package/templates/vault/junta-leiloeiros/scripts/scraper/juceal.py +72 -0
  168. package/templates/vault/junta-leiloeiros/scripts/scraper/juceb.py +68 -0
  169. package/templates/vault/junta-leiloeiros/scripts/scraper/jucec.py +63 -0
  170. package/templates/vault/junta-leiloeiros/scripts/scraper/jucema.py +211 -0
  171. package/templates/vault/junta-leiloeiros/scripts/scraper/jucemg.py +218 -0
  172. package/templates/vault/junta-leiloeiros/scripts/scraper/jucep.py +70 -0
  173. package/templates/vault/junta-leiloeiros/scripts/scraper/jucepa.py +74 -0
  174. package/templates/vault/junta-leiloeiros/scripts/scraper/jucepar.py +80 -0
  175. package/templates/vault/junta-leiloeiros/scripts/scraper/jucepe.py +78 -0
  176. package/templates/vault/junta-leiloeiros/scripts/scraper/jucepi.py +69 -0
  177. package/templates/vault/junta-leiloeiros/scripts/scraper/jucer.py +256 -0
  178. package/templates/vault/junta-leiloeiros/scripts/scraper/jucerja.py +170 -0
  179. package/templates/vault/junta-leiloeiros/scripts/scraper/jucern.py +71 -0
  180. package/templates/vault/junta-leiloeiros/scripts/scraper/jucesc.py +89 -0
  181. package/templates/vault/junta-leiloeiros/scripts/scraper/jucesp.py +233 -0
  182. package/templates/vault/junta-leiloeiros/scripts/scraper/jucetins.py +134 -0
  183. package/templates/vault/junta-leiloeiros/scripts/scraper/jucis_df.py +63 -0
  184. package/templates/vault/junta-leiloeiros/scripts/scraper/jucisrs.py +299 -0
  185. package/templates/vault/junta-leiloeiros/scripts/scraper/states.py +99 -0
  186. package/templates/vault/junta-leiloeiros/scripts/serve_api.py +164 -0
  187. package/templates/vault/junta-leiloeiros/scripts/web_scraper_fallback.py +233 -0
  188. package/templates/vault/last30days/scripts/last30days.py +521 -0
  189. package/templates/vault/last30days/scripts/lib/__init__.py +1 -0
  190. package/templates/vault/last30days/scripts/lib/cache.py +152 -0
  191. package/templates/vault/last30days/scripts/lib/dates.py +124 -0
  192. package/templates/vault/last30days/scripts/lib/dedupe.py +120 -0
  193. package/templates/vault/last30days/scripts/lib/env.py +149 -0
  194. package/templates/vault/last30days/scripts/lib/http.py +152 -0
  195. package/templates/vault/last30days/scripts/lib/models.py +175 -0
  196. package/templates/vault/last30days/scripts/lib/normalize.py +160 -0
  197. package/templates/vault/last30days/scripts/lib/openai_reddit.py +230 -0
  198. package/templates/vault/last30days/scripts/lib/reddit_enrich.py +232 -0
  199. package/templates/vault/last30days/scripts/lib/render.py +383 -0
  200. package/templates/vault/last30days/scripts/lib/schema.py +336 -0
  201. package/templates/vault/last30days/scripts/lib/score.py +311 -0
  202. package/templates/vault/last30days/scripts/lib/ui.py +324 -0
  203. package/templates/vault/last30days/scripts/lib/websearch.py +401 -0
  204. package/templates/vault/last30days/scripts/lib/xai_x.py +217 -0
  205. package/templates/vault/leiloeiro-avaliacao/scripts/governance.py +106 -0
  206. package/templates/vault/leiloeiro-avaliacao/scripts/requirements.txt +1 -0
  207. package/templates/vault/leiloeiro-edital/scripts/governance.py +106 -0
  208. package/templates/vault/leiloeiro-edital/scripts/requirements.txt +1 -0
  209. package/templates/vault/leiloeiro-ia/scripts/governance.py +106 -0
  210. package/templates/vault/leiloeiro-ia/scripts/requirements.txt +1 -0
  211. package/templates/vault/leiloeiro-juridico/scripts/governance.py +106 -0
  212. package/templates/vault/leiloeiro-juridico/scripts/requirements.txt +1 -0
  213. package/templates/vault/leiloeiro-mercado/scripts/governance.py +106 -0
  214. package/templates/vault/leiloeiro-mercado/scripts/requirements.txt +1 -0
  215. package/templates/vault/leiloeiro-risco/scripts/governance.py +106 -0
  216. package/templates/vault/leiloeiro-risco/scripts/requirements.txt +1 -0
  217. package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/database.ts +24 -0
  218. package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/db.ts +35 -0
  219. package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/index.ts +2 -0
  220. package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/migrations.ts +31 -0
  221. package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/schema.sql +8 -0
  222. package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/index.ts +44 -0
  223. package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/routes/todos.ts +155 -0
  224. package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/types/index.ts +35 -0
  225. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/App.css +384 -0
  226. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/App.tsx +81 -0
  227. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/api/todos.ts +57 -0
  228. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/ConfirmDialog.tsx +26 -0
  229. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/EmptyState.tsx +8 -0
  230. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/TodoForm.tsx +43 -0
  231. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/TodoItem.tsx +36 -0
  232. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/TodoList.tsx +27 -0
  233. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/hooks/useTodos.ts +81 -0
  234. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/index.css +48 -0
  235. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/main.tsx +10 -0
  236. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/vite-env.d.ts +1 -0
  237. package/templates/vault/loki-mode/scripts/export-to-vibe-kanban.sh +178 -0
  238. package/templates/vault/loki-mode/scripts/loki-wrapper.sh +281 -0
  239. package/templates/vault/loki-mode/scripts/take-screenshots.js +55 -0
  240. package/templates/vault/matematico-tao/scripts/complexity_analyzer.py +544 -0
  241. package/templates/vault/matematico-tao/scripts/dependency_graph.py +538 -0
  242. package/templates/vault/mcp-builder/scripts/connections.py +151 -0
  243. package/templates/vault/mcp-builder/scripts/evaluation.py +373 -0
  244. package/templates/vault/mcp-builder/scripts/example_evaluation.xml +22 -0
  245. package/templates/vault/mcp-builder/scripts/requirements.txt +2 -0
  246. package/templates/vault/mobile-design/scripts/mobile_audit.py +670 -0
  247. package/templates/vault/notebooklm/scripts/__init__.py +81 -0
  248. package/templates/vault/notebooklm/scripts/ask_question.py +256 -0
  249. package/templates/vault/notebooklm/scripts/auth_manager.py +358 -0
  250. package/templates/vault/notebooklm/scripts/browser_session.py +255 -0
  251. package/templates/vault/notebooklm/scripts/browser_utils.py +107 -0
  252. package/templates/vault/notebooklm/scripts/cleanup_manager.py +302 -0
  253. package/templates/vault/notebooklm/scripts/config.py +44 -0
  254. package/templates/vault/notebooklm/scripts/notebook_manager.py +410 -0
  255. package/templates/vault/notebooklm/scripts/run.py +102 -0
  256. package/templates/vault/notebooklm/scripts/setup_environment.py +204 -0
  257. package/templates/vault/pdf/scripts/check_bounding_boxes.py +70 -0
  258. package/templates/vault/pdf/scripts/check_bounding_boxes_test.py +226 -0
  259. package/templates/vault/pdf/scripts/check_fillable_fields.py +12 -0
  260. package/templates/vault/pdf/scripts/convert_pdf_to_images.py +35 -0
  261. package/templates/vault/pdf/scripts/create_validation_image.py +41 -0
  262. package/templates/vault/pdf/scripts/extract_form_field_info.py +152 -0
  263. package/templates/vault/pdf/scripts/fill_fillable_fields.py +114 -0
  264. package/templates/vault/pdf/scripts/fill_pdf_form_with_annotations.py +108 -0
  265. package/templates/vault/pdf-official/scripts/check_bounding_boxes.py +70 -0
  266. package/templates/vault/pdf-official/scripts/check_bounding_boxes_test.py +226 -0
  267. package/templates/vault/pdf-official/scripts/check_fillable_fields.py +12 -0
  268. package/templates/vault/pdf-official/scripts/convert_pdf_to_images.py +35 -0
  269. package/templates/vault/pdf-official/scripts/create_validation_image.py +41 -0
  270. package/templates/vault/pdf-official/scripts/extract_form_field_info.py +152 -0
  271. package/templates/vault/pdf-official/scripts/fill_fillable_fields.py +114 -0
  272. package/templates/vault/pdf-official/scripts/fill_pdf_form_with_annotations.py +108 -0
  273. package/templates/vault/planning-with-files/scripts/check-complete.sh +44 -0
  274. package/templates/vault/planning-with-files/scripts/init-session.sh +120 -0
  275. package/templates/vault/pptx/ooxml/scripts/pack.py +159 -0
  276. package/templates/vault/pptx/ooxml/scripts/unpack.py +29 -0
  277. package/templates/vault/pptx/ooxml/scripts/validate.py +69 -0
  278. package/templates/vault/pptx/ooxml/scripts/validation/__init__.py +15 -0
  279. package/templates/vault/pptx/ooxml/scripts/validation/base.py +951 -0
  280. package/templates/vault/pptx/ooxml/scripts/validation/docx.py +274 -0
  281. package/templates/vault/pptx/ooxml/scripts/validation/pptx.py +315 -0
  282. package/templates/vault/pptx/ooxml/scripts/validation/redlining.py +279 -0
  283. package/templates/vault/pptx/scripts/html2pptx.js +979 -0
  284. package/templates/vault/pptx/scripts/inventory.py +1020 -0
  285. package/templates/vault/pptx/scripts/rearrange.py +231 -0
  286. package/templates/vault/pptx/scripts/replace.py +385 -0
  287. package/templates/vault/pptx/scripts/thumbnail.py +450 -0
  288. package/templates/vault/pptx-official/ooxml/scripts/pack.py +159 -0
  289. package/templates/vault/pptx-official/ooxml/scripts/unpack.py +29 -0
  290. package/templates/vault/pptx-official/ooxml/scripts/validate.py +69 -0
  291. package/templates/vault/pptx-official/ooxml/scripts/validation/__init__.py +15 -0
  292. package/templates/vault/pptx-official/ooxml/scripts/validation/base.py +951 -0
  293. package/templates/vault/pptx-official/ooxml/scripts/validation/docx.py +274 -0
  294. package/templates/vault/pptx-official/ooxml/scripts/validation/pptx.py +315 -0
  295. package/templates/vault/pptx-official/ooxml/scripts/validation/redlining.py +279 -0
  296. package/templates/vault/pptx-official/scripts/html2pptx.js +979 -0
  297. package/templates/vault/pptx-official/scripts/inventory.py +1020 -0
  298. package/templates/vault/pptx-official/scripts/rearrange.py +231 -0
  299. package/templates/vault/pptx-official/scripts/replace.py +385 -0
  300. package/templates/vault/pptx-official/scripts/thumbnail.py +450 -0
  301. package/templates/vault/product-manager-toolkit/scripts/customer_interview_analyzer.py +441 -0
  302. package/templates/vault/product-manager-toolkit/scripts/rice_prioritizer.py +296 -0
  303. package/templates/vault/prompt-engineering-patterns/scripts/optimize-prompt.py +279 -0
  304. package/templates/vault/scripts/.skill_cache.json +7538 -0
  305. package/templates/vault/scripts/skill_search.py +228 -0
  306. package/templates/vault/senior-architect/scripts/architecture_diagram_generator.py +114 -0
  307. package/templates/vault/senior-architect/scripts/dependency_analyzer.py +114 -0
  308. package/templates/vault/senior-architect/scripts/project_architect.py +114 -0
  309. package/templates/vault/shopify-development/scripts/requirements.txt +19 -0
  310. package/templates/vault/shopify-development/scripts/shopify_graphql.py +428 -0
  311. package/templates/vault/shopify-development/scripts/shopify_init.py +441 -0
  312. package/templates/vault/shopify-development/scripts/tests/test_shopify_init.py +379 -0
  313. package/templates/vault/skill-creator/scripts/init_skill.py +303 -0
  314. package/templates/vault/skill-creator/scripts/package_skill.py +110 -0
  315. package/templates/vault/skill-creator/scripts/quick_validate.py +95 -0
  316. package/templates/vault/skill-installer/scripts/detect_skills.py +318 -0
  317. package/templates/vault/skill-installer/scripts/install_skill.py +1708 -0
  318. package/templates/vault/skill-installer/scripts/package_skill.py +417 -0
  319. package/templates/vault/skill-installer/scripts/requirements.txt +1 -0
  320. package/templates/vault/skill-installer/scripts/validate_skill.py +430 -0
  321. package/templates/vault/skill-sentinel/scripts/analyzers/__init__.py +13 -0
  322. package/templates/vault/skill-sentinel/scripts/analyzers/code_quality.py +247 -0
  323. package/templates/vault/skill-sentinel/scripts/analyzers/cross_skill.py +134 -0
  324. package/templates/vault/skill-sentinel/scripts/analyzers/dependencies.py +121 -0
  325. package/templates/vault/skill-sentinel/scripts/analyzers/documentation.py +189 -0
  326. package/templates/vault/skill-sentinel/scripts/analyzers/governance_audit.py +153 -0
  327. package/templates/vault/skill-sentinel/scripts/analyzers/performance.py +164 -0
  328. package/templates/vault/skill-sentinel/scripts/analyzers/security.py +189 -0
  329. package/templates/vault/skill-sentinel/scripts/config.py +158 -0
  330. package/templates/vault/skill-sentinel/scripts/cost_optimizer.py +146 -0
  331. package/templates/vault/skill-sentinel/scripts/db.py +354 -0
  332. package/templates/vault/skill-sentinel/scripts/governance.py +58 -0
  333. package/templates/vault/skill-sentinel/scripts/recommender.py +228 -0
  334. package/templates/vault/skill-sentinel/scripts/report_generator.py +224 -0
  335. package/templates/vault/skill-sentinel/scripts/requirements.txt +1 -0
  336. package/templates/vault/skill-sentinel/scripts/run_audit.py +290 -0
  337. package/templates/vault/skill-sentinel/scripts/scanner.py +271 -0
  338. package/templates/vault/stability-ai/scripts/config.py +266 -0
  339. package/templates/vault/stability-ai/scripts/generate.py +687 -0
  340. package/templates/vault/stability-ai/scripts/requirements.txt +4 -0
  341. package/templates/vault/stability-ai/scripts/styles.py +174 -0
  342. package/templates/vault/telegram/assets/boilerplate/nodejs/src/bot-client.ts +86 -0
  343. package/templates/vault/telegram/assets/boilerplate/nodejs/src/handlers.ts +79 -0
  344. package/templates/vault/telegram/assets/boilerplate/nodejs/src/index.ts +32 -0
  345. package/templates/vault/telegram/scripts/send_message.py +143 -0
  346. package/templates/vault/telegram/scripts/setup_project.py +103 -0
  347. package/templates/vault/telegram/scripts/test_bot.py +144 -0
  348. package/templates/vault/typescript-expert/scripts/ts_diagnostic.py +203 -0
  349. package/templates/vault/ui-ux-pro-max/scripts/__pycache__/core.cpython-314.pyc +0 -0
  350. package/templates/vault/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-314.pyc +0 -0
  351. package/templates/vault/ui-ux-pro-max/scripts/core.py +257 -0
  352. package/templates/vault/ui-ux-pro-max/scripts/design_system.py +487 -0
  353. package/templates/vault/ui-ux-pro-max/scripts/search.py +76 -0
  354. package/templates/vault/videodb/scripts/ws_listener.py +204 -0
  355. package/templates/vault/web-artifacts-builder/scripts/bundle-artifact.sh +54 -0
  356. package/templates/vault/web-artifacts-builder/scripts/init-artifact.sh +322 -0
  357. package/templates/vault/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
  358. package/templates/vault/webapp-testing/scripts/with_server.py +106 -0
  359. package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/index.ts +125 -0
  360. package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/template-manager.ts +67 -0
  361. package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/types.ts +216 -0
  362. package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/webhook-handler.ts +173 -0
  363. package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/whatsapp-client.ts +193 -0
  364. package/templates/vault/whatsapp-cloud-api/scripts/send_test_message.py +137 -0
  365. package/templates/vault/whatsapp-cloud-api/scripts/setup_project.py +118 -0
  366. package/templates/vault/whatsapp-cloud-api/scripts/validate_config.py +190 -0
  367. package/templates/vault/youtube-summarizer/scripts/extract-transcript.py +65 -0
  368. package/templates/vault/youtube-summarizer/scripts/install-dependencies.sh +28 -0
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Skill Packager - Creates a distributable .skill file of a skill folder
4
+
5
+ Usage:
6
+ python utils/package_skill.py <path/to/skill-folder> [output-directory]
7
+
8
+ Example:
9
+ python utils/package_skill.py skills/public/my-skill
10
+ python utils/package_skill.py skills/public/my-skill ./dist
11
+ """
12
+
13
+ import sys
14
+ import zipfile
15
+ from pathlib import Path
16
+ from quick_validate import validate_skill
17
+
18
+
19
+ def package_skill(skill_path, output_dir=None):
20
+ """
21
+ Package a skill folder into a .skill file.
22
+
23
+ Args:
24
+ skill_path: Path to the skill folder
25
+ output_dir: Optional output directory for the .skill file (defaults to current directory)
26
+
27
+ Returns:
28
+ Path to the created .skill file, or None if error
29
+ """
30
+ skill_path = Path(skill_path).resolve()
31
+
32
+ # Validate skill folder exists
33
+ if not skill_path.exists():
34
+ print(f"❌ Error: Skill folder not found: {skill_path}")
35
+ return None
36
+
37
+ if not skill_path.is_dir():
38
+ print(f"❌ Error: Path is not a directory: {skill_path}")
39
+ return None
40
+
41
+ # Validate SKILL.md exists
42
+ skill_md = skill_path / "SKILL.md"
43
+ if not skill_md.exists():
44
+ print(f"❌ Error: SKILL.md not found in {skill_path}")
45
+ return None
46
+
47
+ # Run validation before packaging
48
+ print("🔍 Validating skill...")
49
+ valid, message = validate_skill(skill_path)
50
+ if not valid:
51
+ print(f"❌ Validation failed: {message}")
52
+ print(" Please fix the validation errors before packaging.")
53
+ return None
54
+ print(f"✅ {message}\n")
55
+
56
+ # Determine output location
57
+ skill_name = skill_path.name
58
+ if output_dir:
59
+ output_path = Path(output_dir).resolve()
60
+ output_path.mkdir(parents=True, exist_ok=True)
61
+ else:
62
+ output_path = Path.cwd()
63
+
64
+ skill_filename = output_path / f"{skill_name}.skill"
65
+
66
+ # Create the .skill file (zip format)
67
+ try:
68
+ with zipfile.ZipFile(skill_filename, 'w', zipfile.ZIP_DEFLATED) as zipf:
69
+ # Walk through the skill directory
70
+ for file_path in skill_path.rglob('*'):
71
+ if file_path.is_file():
72
+ # Calculate the relative path within the zip
73
+ arcname = file_path.relative_to(skill_path.parent)
74
+ zipf.write(file_path, arcname)
75
+ print(f" Added: {arcname}")
76
+
77
+ print(f"\n✅ Successfully packaged skill to: {skill_filename}")
78
+ return skill_filename
79
+
80
+ except Exception as e:
81
+ print(f"❌ Error creating .skill file: {e}")
82
+ return None
83
+
84
+
85
+ def main():
86
+ if len(sys.argv) < 2:
87
+ print("Usage: python utils/package_skill.py <path/to/skill-folder> [output-directory]")
88
+ print("\nExample:")
89
+ print(" python utils/package_skill.py skills/public/my-skill")
90
+ print(" python utils/package_skill.py skills/public/my-skill ./dist")
91
+ sys.exit(1)
92
+
93
+ skill_path = sys.argv[1]
94
+ output_dir = sys.argv[2] if len(sys.argv) > 2 else None
95
+
96
+ print(f"📦 Packaging skill: {skill_path}")
97
+ if output_dir:
98
+ print(f" Output directory: {output_dir}")
99
+ print()
100
+
101
+ result = package_skill(skill_path, output_dir)
102
+
103
+ if result:
104
+ sys.exit(0)
105
+ else:
106
+ sys.exit(1)
107
+
108
+
109
+ if __name__ == "__main__":
110
+ main()
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Quick validation script for skills - minimal version
4
+ """
5
+
6
+ import sys
7
+ import os
8
+ import re
9
+ import yaml
10
+ from pathlib import Path
11
+
12
+ def validate_skill(skill_path):
13
+ """Basic validation of a skill"""
14
+ skill_path = Path(skill_path)
15
+
16
+ # Check SKILL.md exists
17
+ skill_md = skill_path / 'SKILL.md'
18
+ if not skill_md.exists():
19
+ return False, "SKILL.md not found"
20
+
21
+ # Read and validate frontmatter
22
+ content = skill_md.read_text()
23
+ if not content.startswith('---'):
24
+ return False, "No YAML frontmatter found"
25
+
26
+ # Extract frontmatter
27
+ match = re.match(r'^---\n(.*?)\n---', content, re.DOTALL)
28
+ if not match:
29
+ return False, "Invalid frontmatter format"
30
+
31
+ frontmatter_text = match.group(1)
32
+
33
+ # Parse YAML frontmatter
34
+ try:
35
+ frontmatter = yaml.safe_load(frontmatter_text)
36
+ if not isinstance(frontmatter, dict):
37
+ return False, "Frontmatter must be a YAML dictionary"
38
+ except yaml.YAMLError as e:
39
+ return False, f"Invalid YAML in frontmatter: {e}"
40
+
41
+ # Define allowed properties
42
+ ALLOWED_PROPERTIES = {'name', 'description', 'license', 'allowed-tools', 'metadata'}
43
+
44
+ # Check for unexpected properties (excluding nested keys under metadata)
45
+ unexpected_keys = set(frontmatter.keys()) - ALLOWED_PROPERTIES
46
+ if unexpected_keys:
47
+ return False, (
48
+ f"Unexpected key(s) in SKILL.md frontmatter: {', '.join(sorted(unexpected_keys))}. "
49
+ f"Allowed properties are: {', '.join(sorted(ALLOWED_PROPERTIES))}"
50
+ )
51
+
52
+ # Check required fields
53
+ if 'name' not in frontmatter:
54
+ return False, "Missing 'name' in frontmatter"
55
+ if 'description' not in frontmatter:
56
+ return False, "Missing 'description' in frontmatter"
57
+
58
+ # Extract name for validation
59
+ name = frontmatter.get('name', '')
60
+ if not isinstance(name, str):
61
+ return False, f"Name must be a string, got {type(name).__name__}"
62
+ name = name.strip()
63
+ if name:
64
+ # Check naming convention (hyphen-case: lowercase with hyphens)
65
+ if not re.match(r'^[a-z0-9-]+$', name):
66
+ return False, f"Name '{name}' should be hyphen-case (lowercase letters, digits, and hyphens only)"
67
+ if name.startswith('-') or name.endswith('-') or '--' in name:
68
+ return False, f"Name '{name}' cannot start/end with hyphen or contain consecutive hyphens"
69
+ # Check name length (max 64 characters per spec)
70
+ if len(name) > 64:
71
+ return False, f"Name is too long ({len(name)} characters). Maximum is 64 characters."
72
+
73
+ # Extract and validate description
74
+ description = frontmatter.get('description', '')
75
+ if not isinstance(description, str):
76
+ return False, f"Description must be a string, got {type(description).__name__}"
77
+ description = description.strip()
78
+ if description:
79
+ # Check for angle brackets
80
+ if '<' in description or '>' in description:
81
+ return False, "Description cannot contain angle brackets (< or >)"
82
+ # Check description length (max 1024 characters per spec)
83
+ if len(description) > 1024:
84
+ return False, f"Description is too long ({len(description)} characters). Maximum is 1024 characters."
85
+
86
+ return True, "Skill is valid!"
87
+
88
+ if __name__ == "__main__":
89
+ if len(sys.argv) != 2:
90
+ print("Usage: python quick_validate.py <skill_directory>")
91
+ sys.exit(1)
92
+
93
+ valid, message = validate_skill(sys.argv[1])
94
+ print(message)
95
+ sys.exit(0 if valid else 1)
@@ -0,0 +1,318 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Skill Detector - Find uninstalled skills in common locations.
4
+
5
+ Scans known directories where skills might have been created
6
+ (skill-creator workspaces, Desktop, Downloads, temp dirs) and
7
+ identifies candidates that are not yet installed in the ecosystem.
8
+
9
+ Usage:
10
+ python detect_skills.py # Scan default locations
11
+ python detect_skills.py --path "C:\\some\\dir" # Scan specific path
12
+ python detect_skills.py --all # Scan all known locations
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import os
18
+ import sys
19
+ import json
20
+ import re
21
+ from pathlib import Path
22
+ from datetime import datetime
23
+
24
+ # ── Configuration ──────────────────────────────────────────────────────────
25
+
26
+ SKILLS_ROOT = Path(r"C:\Users\renat\skills")
27
+ USER_HOME = Path(os.path.expanduser("~"))
28
+ TEMP_DIR = Path(os.environ.get("TEMP", os.environ.get("TMP", str(USER_HOME / "AppData" / "Local" / "Temp"))))
29
+
30
+ # Where we consider skills "installed"
31
+ INSTALLED_LOCATIONS = [
32
+ SKILLS_ROOT,
33
+ SKILLS_ROOT / ".claude" / "skills",
34
+ ]
35
+
36
+ # Default locations to scan for uninstalled skills
37
+ DEFAULT_SCAN_LOCATIONS = [
38
+ USER_HOME / "Desktop",
39
+ USER_HOME / "Downloads",
40
+ USER_HOME / "Documents",
41
+ TEMP_DIR,
42
+ ]
43
+
44
+ # Additional locations for --all mode
45
+ EXTENDED_SCAN_LOCATIONS = [
46
+ USER_HOME,
47
+ USER_HOME / "Projects",
48
+ USER_HOME / "dev",
49
+ USER_HOME / "repos",
50
+ USER_HOME / "workspace",
51
+ USER_HOME / "code",
52
+ Path(r"C:\temp"),
53
+ Path(r"C:\projects"),
54
+ ]
55
+
56
+ MAX_SCAN_DEPTH = 5
57
+ WORKSPACE_PATTERN = re.compile(r".*-workspace[/\\]v\d+[/\\]skill$", re.IGNORECASE)
58
+
59
+
60
+ # ── YAML Frontmatter Parser ───────────────────────────────────────────────
61
+
62
+ def parse_yaml_frontmatter(path: Path) -> dict:
63
+ """Extract YAML frontmatter from a SKILL.md file."""
64
+ try:
65
+ text = path.read_text(encoding="utf-8")
66
+ except Exception:
67
+ return {}
68
+
69
+ match = re.match(r"^---\s*\n(.*?)\n---", text, re.DOTALL)
70
+ if not match:
71
+ return {}
72
+
73
+ try:
74
+ import yaml
75
+ return yaml.safe_load(match.group(1)) or {}
76
+ except Exception:
77
+ result = {}
78
+ block = match.group(1)
79
+ for key in ("name", "description", "version"):
80
+ m = re.search(rf'^{key}:\s*["\']?(.+?)["\']?\s*$', block, re.MULTILINE)
81
+ if m:
82
+ result[key] = m.group(1).strip()
83
+ else:
84
+ m2 = re.search(
85
+ rf'^{key}:\s*>-?\s*\n((?:\s+.+\n?)+)', block, re.MULTILINE
86
+ )
87
+ if m2:
88
+ lines = m2.group(1).strip().split("\n")
89
+ result[key] = " ".join(line.strip() for line in lines)
90
+ return result
91
+
92
+
93
+ # ── Core Functions ─────────────────────────────────────────────────────────
94
+
95
+ def get_installed_skill_names() -> set:
96
+ """Get set of skill names already installed in the ecosystem."""
97
+ names = set()
98
+
99
+ for base in INSTALLED_LOCATIONS:
100
+ if not base.exists():
101
+ continue
102
+ for root, dirs, files in os.walk(base):
103
+ depth = len(Path(root).relative_to(base).parts)
104
+ if depth > 3:
105
+ dirs.clear()
106
+ continue
107
+
108
+ # Skip certain directories
109
+ skip = {"agent-orchestrator", "skill-installer", ".git", "__pycache__", "node_modules"}
110
+ if any(part in skip for part in Path(root).parts):
111
+ continue
112
+
113
+ if "SKILL.md" in files:
114
+ skill_md = Path(root) / "SKILL.md"
115
+ meta = parse_yaml_frontmatter(skill_md)
116
+ name = meta.get("name", Path(root).name)
117
+ names.add(name.lower())
118
+
119
+ return names
120
+
121
+
122
+ def find_skill_candidates(scan_locations: list[Path]) -> list[dict]:
123
+ """Find SKILL.md files in given locations."""
124
+ candidates = []
125
+ seen_paths = set()
126
+ installed_names = get_installed_skill_names()
127
+
128
+ for base in scan_locations:
129
+ if not base.exists():
130
+ continue
131
+
132
+ # Skip the skills root itself (those are already installed)
133
+ try:
134
+ base.resolve().relative_to(SKILLS_ROOT.resolve())
135
+ continue
136
+ except ValueError:
137
+ pass
138
+
139
+ for root, dirs, files in os.walk(base):
140
+ root_path = Path(root)
141
+ depth = len(root_path.relative_to(base).parts)
142
+
143
+ if depth > MAX_SCAN_DEPTH:
144
+ dirs.clear()
145
+ continue
146
+
147
+ # Skip heavy directories
148
+ skip_dirs = {".git", "__pycache__", "node_modules", ".venv", "venv", ".tox"}
149
+ dirs[:] = [d for d in dirs if d not in skip_dirs]
150
+
151
+ if "SKILL.md" in files:
152
+ skill_md = root_path / "SKILL.md"
153
+ resolved = skill_md.resolve()
154
+
155
+ # Skip if already seen
156
+ if str(resolved) in seen_paths:
157
+ continue
158
+ seen_paths.add(str(resolved))
159
+
160
+ # Skip if inside SKILLS_ROOT
161
+ try:
162
+ resolved.relative_to(SKILLS_ROOT.resolve())
163
+ continue
164
+ except ValueError:
165
+ pass
166
+
167
+ # Parse metadata
168
+ meta = parse_yaml_frontmatter(skill_md)
169
+ name = meta.get("name", root_path.name)
170
+ description = meta.get("description", "")
171
+ has_valid = bool(name and description)
172
+
173
+ # Check if already installed
174
+ already_installed = name.lower() in installed_names
175
+
176
+ # Detect if in a workspace
177
+ is_workspace = bool(WORKSPACE_PATTERN.match(str(root_path)))
178
+
179
+ desc_str = str(description)
180
+
181
+ # Get modification timestamp and size
182
+ try:
183
+ mtime = skill_md.stat().st_mtime
184
+ mtime_str = datetime.fromtimestamp(mtime).strftime("%Y-%m-%d %H:%M")
185
+ except Exception:
186
+ mtime = 0.0
187
+ mtime_str = "unknown"
188
+
189
+ # Calculate directory size
190
+ total_size = 0
191
+ file_count = 0
192
+ for r2, _, f2 in os.walk(root_path):
193
+ for ff in f2:
194
+ try:
195
+ total_size += os.path.getsize(os.path.join(r2, ff))
196
+ file_count += 1
197
+ except OSError:
198
+ pass
199
+ size_kb = round(total_size / 1024, 1)
200
+
201
+ candidates.append({
202
+ "name": name,
203
+ "source_path": str(root_path),
204
+ "skill_md_path": str(skill_md),
205
+ "already_installed": already_installed,
206
+ "valid_frontmatter": has_valid,
207
+ "description": desc_str[:120] + ("..." if len(desc_str) > 120 else ""),
208
+ "version": meta.get("version", ""),
209
+ "is_workspace": is_workspace,
210
+ "location_type": _classify_location(root_path),
211
+ "last_modified": mtime_str,
212
+ "last_modified_ts": mtime,
213
+ "size_kb": size_kb,
214
+ "file_count": file_count,
215
+ })
216
+
217
+ return candidates
218
+
219
+
220
+ def _classify_location(path: Path) -> str:
221
+ """Classify where the skill was found."""
222
+ path_str = str(path).lower()
223
+
224
+ if "desktop" in path_str:
225
+ return "desktop"
226
+ if "download" in path_str:
227
+ return "downloads"
228
+ if "document" in path_str:
229
+ return "documents"
230
+ if "temp" in path_str or "tmp" in path_str:
231
+ return "temp"
232
+ if "workspace" in path_str:
233
+ return "workspace"
234
+ return "other"
235
+
236
+
237
+ # ── Main Logic ─────────────────────────────────────────────────────────────
238
+
239
+ def detect(paths: list[Path] = None, scan_all: bool = False) -> dict:
240
+ """
241
+ Detect uninstalled skills.
242
+
243
+ Args:
244
+ paths: Specific paths to scan. If None, uses defaults.
245
+ scan_all: If True, scan extended locations too.
246
+
247
+ Returns:
248
+ dict with candidates list and summary.
249
+ """
250
+ scan_locations = []
251
+
252
+ if paths:
253
+ scan_locations.extend(paths)
254
+ else:
255
+ scan_locations.extend(DEFAULT_SCAN_LOCATIONS)
256
+
257
+ if scan_all:
258
+ scan_locations.extend(EXTENDED_SCAN_LOCATIONS)
259
+
260
+ # Deduplicate
261
+ seen = set()
262
+ unique_locations = []
263
+ for loc in scan_locations:
264
+ resolved = str(loc.resolve())
265
+ if resolved not in seen:
266
+ seen.add(resolved)
267
+ unique_locations.append(loc)
268
+
269
+ candidates = find_skill_candidates(unique_locations)
270
+
271
+ # Sort: uninstalled first, then by most recently modified
272
+ candidates.sort(key=lambda c: (
273
+ c["already_installed"],
274
+ -c.get("last_modified_ts", 0),
275
+ c["name"].lower(),
276
+ ))
277
+
278
+ not_installed = [c for c in candidates if not c["already_installed"]]
279
+ already = [c for c in candidates if c["already_installed"]]
280
+
281
+ return {
282
+ "total_found": len(candidates),
283
+ "not_installed": len(not_installed),
284
+ "already_installed": len(already),
285
+ "scanned_locations": [str(p) for p in unique_locations if p.exists()],
286
+ "candidates": candidates,
287
+ }
288
+
289
+
290
+ # ── CLI Entry Point ───────────────────────────────────────────────────────
291
+
292
+ def main():
293
+ paths = []
294
+ scan_all = "--all" in sys.argv
295
+
296
+ if "--path" in sys.argv:
297
+ idx = sys.argv.index("--path")
298
+ if idx + 1 < len(sys.argv):
299
+ p = Path(sys.argv[idx + 1]).resolve()
300
+ if p.exists():
301
+ paths.append(p)
302
+ else:
303
+ print(json.dumps({
304
+ "error": f"Path does not exist: {p}",
305
+ "total_found": 0,
306
+ "candidates": [],
307
+ }, indent=2))
308
+ sys.exit(1)
309
+
310
+ result = detect(paths=paths if paths else None, scan_all=scan_all)
311
+ print(json.dumps(result, indent=2, ensure_ascii=False))
312
+
313
+ # Exit with 0 if candidates found, 1 if none
314
+ sys.exit(0 if result["total_found"] > 0 else 1)
315
+
316
+
317
+ if __name__ == "__main__":
318
+ main()