# Skinbase26 CLI Reference

Last updated: 2026-05-19

This document lists the repository-specific command-line entry points in Skinbase26.

Scope:
- Custom Artisan commands from `app/Console/Commands`
- Maintained standalone scripts in the repo root and `scripts/`
- Ad hoc and temporary scripts that are still present in the workspace

Not included:
- Built-in Laravel Artisan commands such as `migrate`, `queue:work`, `cache:clear`, and similar framework defaults
- Raw SQL files such as `missing_users.sql` or `recover-users.sql`

Examples below are representative. For the full option list of any Artisan command, run `php artisan help <command>`.

## Main Entry Points

| Entry point | Why it is used | Example |
| --- | --- | --- |
| `php artisan` | Main Laravel CLI for all custom app commands listed below | `php artisan list --raw` |
| `bash sync.sh` | Main production deploy wrapper; delegates to the safe release-based production deploy script | `bash sync.sh` |
| `bash sync_dev.sh` | Push the development environment to the configured remote dev host | `bash sync_dev.sh` |

## Maintained Standalone Scripts

| Script | Why it is used | Example |
| --- | --- | --- |
| `scripts/deploy-production.sh` | Safe production deployment wrapper that resolves to the retained-release deploy workflow with a server-side current-release switch | `bash scripts/deploy-production.sh --mode=normal` |
| `scripts/rollback-production.sh` | Switch the active production release to a previously retained server-side deployment | `bash scripts/rollback-production.sh --previous` |
| `scripts/push-db-to-prod.sh` | Push the local database to production with remote backup controls | `bash scripts/push-db-to-prod.sh --force` |
| `scripts/render-nova-card.cjs` | Render a Nova card screenshot through Playwright | `node scripts/render-nova-card.cjs --url=https://example.test/card --out=tmp/card.png` |
| `scripts/vision-smoke.ps1` | Smoke-test the configured Vision API from PowerShell | `powershell -ExecutionPolicy Bypass -File scripts/vision-smoke.ps1` |

## Custom Artisan Commands

### Analytics

| Command | Why it is used | Example |
| --- | --- | --- |
| `analytics:aggregate-discovery-feedback` | Aggregate discovery feedback events into daily metrics by algorithm and surface | `php artisan analytics:aggregate-discovery-feedback --date=2026-04-16` |
| `analytics:aggregate-feed` | Aggregate feed analytics into daily metrics by algorithm version and source | `php artisan analytics:aggregate-feed --date=2026-04-16` |
| `analytics:aggregate-similar-artworks` | Aggregate similar-artwork analytics into daily counts by algorithm version | `php artisan analytics:aggregate-similar-artworks --date=2026-04-16` |
| `analytics:aggregate-tag-interactions` | Aggregate tag-interaction analytics by surface, tag, source tag, and query | `php artisan analytics:aggregate-tag-interactions --date=2026-04-16` |
| `analytics:compare-feed-ab` | Compare baseline versus candidate feed algorithms over a date window | `php artisan analytics:compare-feed-ab baseline_v1 candidate_v2 --from=2026-04-01 --to=2026-04-16` |
| `analytics:evaluate-feed-weights` | Run offline feed-weight evaluation against stored daily metrics | `php artisan analytics:evaluate-feed-weights --algo=v2 --from=2026-04-01 --to=2026-04-16` |
| `analytics:seed-tag-interaction-demo` | Seed demo analytics data for local dashboards and ranking experiments | `php artisan analytics:seed-tag-interaction-demo --days=7 --per-day=100` |

### AI Biography

| Command | Why it is used | Example |
| --- | --- | --- |
| `ai-biography:generate` | Generate missing AI biographies or refresh stale ones | `php artisan ai-biography:generate --stale --limit=50` |
| `ai-biography:inspect` | Inspect an AI biography record and its normalized prompt/input payload | `php artisan ai-biography:inspect 123` |
| `ai-biography:providers` | Check provider health and list available AI biography models | `php artisan ai-biography:providers --limit=10` |
| `ai-biography:review-queue` | List AI biography records that need manual review | `php artisan ai-biography:review-queue --needs-review --limit=25` |
| `ai-biography:validate` | Revalidate stored AI biographies against the current rules | `php artisan ai-biography:validate --limit=100 --dry-run` |

### Academy

| Command | Why it is used | Example |
| --- | --- | --- |
| `academy:analytics-health` | Inspect Academy analytics collection health, rollup freshness, and privacy safeguards | `php artisan academy:analytics-health --json` |
| `academy:billing-health` | Check Academy Stripe billing rollout readiness, including Stripe secrets, price IDs, Cashier routes, and billing tables | `php artisan academy:billing-health --strict` |
| `academy:courses:sync-foundations` | Create or update the default AI-Assisted Digital Art Foundations Academy course | `php artisan academy:courses:sync-foundations` |

### Artworks

| Command | Why it is used | Example |
| --- | --- | --- |
| `artworks:ai-suggest` | Generate studio AI suggestions for artworks, including suggested tags | `php artisan artworks:ai-suggest --after-id=1000 --limit=100 --queue` |
| `artworks:ai-tag` | Generate artwork tags through the local LM Studio vision model | `php artisan artworks:ai-tag --after-id=1000 --limit=50 --dry-run` |
| `artworks:audit-legacy-user-ids` | Compare `artworks.user_id` with legacy `wallz.user_id` using shared artwork IDs | `php artisan artworks:audit-legacy-user-ids --chunk=1000 --show=25 --json` |
| `artworks:audit-thumbnail-maturity` | Scan thumbnails for possible mature content without changing maturity fields | `php artisan artworks:audit-thumbnail-maturity --after-id=1 --limit=100 --dry-run` |
| `artworks:audit-thumbnails` | Audit object-storage thumbnails and flag artworks with missing variants | `php artisan artworks:audit-thumbnails --limit=1000 --dry-run` |
| `artworks:check-user-ids` | Verify that every `artworks.user_id` exists in `users` | `php artisan artworks:check-user-ids --show-missing=50` |
| `artworks:check-user-refs` | Full artwork-user reference audit with optional legacy-user recovery helpers | `php artisan artworks:check-user-refs --show-missing=50 --json` |
| `artworks:embeddings-backfill` | Queue resumable CLIP embedding backfill jobs for artworks | `php artisan artworks:embeddings-backfill --after-id=0 --batch=200` |
| `artworks:generate-missing-sq-thumbs` | Generate missing smart square thumbnails | `php artisan artworks:generate-missing-sq-thumbs --limit=200 --dry-run` |
| `artworks:import-hashes` | Import artwork hash and extension metadata from a CSV file | `php artisan artworks:import-hashes artworks_hash_skinbase.csv --start=0 --limit=1000` |
| `artworks:normalize-slugs` | Normalize stored artwork slugs from titles without enforcing uniqueness | `php artisan artworks:normalize-slugs --only-mismatched --dry-run` |
| `artworks:publish-scheduled` | Publish artworks whose scheduled publish date has passed | `php artisan artworks:publish-scheduled --limit=100` |
| `artworks:search-rebuild` | Requeue all artworks for Meilisearch indexing in chunks | `php artisan artworks:search-rebuild --chunk=500` |
| `artworks:search-reindex-recent` | Reindex recent public artworks to recover missed search updates | `php artisan artworks:search-reindex-recent --hours=24 --limit=500` |
| `artworks:sync-created-at` | Copy `published_at` into `created_at` for published artworks | `php artisan artworks:sync-created-at --only-null --dry-run` |
| `artworks:vectors-index` | Send artwork image URLs to the vector gateway for indexing | `php artisan artworks:vectors-index --public-only --batch=200 --limit=1000` |
| `artworks:vectors-repair` | Repair vector gateway state for artworks that already have local embeddings | `php artisan artworks:vectors-repair --public-only --stale-hours=24` |
| `artworks:vectors-search` | Search for similar artworks through the vector gateway | `php artisan artworks:vectors-search 123 --limit=12` |

### Awards

| Command | Why it is used | Example |
| --- | --- | --- |
| `awards:import-legacy` | Import legacy `users_opinions` rows into `artwork_medals` | `php artisan awards:import-legacy --chunk=500 --dry-run` |

### Avatars

| Command | Why it is used | Example |
| --- | --- | --- |
| `avatars:bulk-update` | Bulk update `user_profiles.avatar_hash` from a CSV file | `php artisan avatars:bulk-update user_profiles_avatar.csv --dry-run` |
| `avatars:migrate` | Migrate legacy avatars and generate resized WebP variants | `php artisan avatars:migrate --dry-run --force` |

### Collections

| Command | Why it is used | Example |
| --- | --- | --- |
| `collections:dispatch-maintenance` | Dispatch maintenance jobs for collection health, recommendations, and duplicate handling | `php artisan collections:dispatch-maintenance --health --recommendations` |
| `collections:sync-lifecycle` | Expire pending invites and sync collection lifecycle state | `php artisan collections:sync-lifecycle` |

### Comments

| Command | Why it is used | Example |
| --- | --- | --- |
| `comments:import-legacy` | Import legacy `artworks_comments` into `artwork_comments` | `php artisan comments:import-legacy --chunk=500 --dry-run` |

### Creator Journey

| Command | Why it is used | Example |
| --- | --- | --- |
| `creator-journey:rebuild-eras` | Rebuild creator-era records from public artwork history | `php artisan creator-journey:rebuild-eras 123 --chunk=200` |

### Forum

| Command | Why it is used | Example |
| --- | --- | --- |
| `forum:convert-posts` | Convert migrated forum posts from legacy BBCode to HTML | `php artisan forum:convert-posts --chunk=500 --report` |
| `forum:migrate-old` | Migrate legacy forum data into the new forum tables | `php artisan forum:migrate-old --chunk=500 --report --dry-run` |

### Health

| Command | Why it is used | Example |
| --- | --- | --- |
| `health:check` | Run the application health check across critical services and dependencies | `php artisan health:check --json` |
| `health:tick` | Write the scheduler heartbeat used by `health:check` | `php artisan health:tick` |

### Homepage

| Command | Why it is used | Example |
| --- | --- | --- |
| `homepage:warm-guest-cache` | Warm the guest homepage cache payload | `php artisan homepage:warm-guest-cache` |

### Imports

| Command | Why it is used | Example |
| --- | --- | --- |
| `import:categories` | Import categories from the legacy CSV source file | `php artisan import:categories database/artworks_categories.csv` |
| `import:legacy-favourites` | Copy legacy favourites from another DB connection into `user_favorites` | `php artisan import:legacy-favourites --connection=legacy --chunk=500` |
| `import:wallz-categories` | Import artwork-category mappings from the legacy `wallz` table | `php artisan import:wallz-categories --connection=legacy --table=wallz --chunk=500` |

### Legacy Repair

| Command | Why it is used | Example |
| --- | --- | --- |
| `legacySB:repair-legacy-wallz-users` | Backfill legacy `wallz.user_id` values by matching usernames to current users | `php artisan legacySB:repair-legacy-wallz-users --chunk=500 --dry-run` |

### Meilisearch

| Command | Why it is used | Example |
| --- | --- | --- |
| `meilisearch:configure-index` | Push sortable/filterable settings to the Meilisearch artworks index | `php artisan meilisearch:configure-index --index=artworks` |

### Migrations

| Command | Why it is used | Example |
| --- | --- | --- |
| `migrate:featured-works` | Safely migrate legacy `featured_works` rows into `artwork_features` | `php artisan migrate:featured-works --chunk=500 --dry-run` |

### News

| Command | Why it is used | Example |
| --- | --- | --- |
| `news:import-legacy` | Import legacy news articles into `news_articles` | `php artisan news:import-legacy --start=0 --limit=500 --dry-run` |
| `news:publish-scheduled` | Publish scheduled news items whose publish time has passed | `php artisan news:publish-scheduled --limit=100` |

### Nova

| Command | Why it is used | Example |
| --- | --- | --- |
| `nova:metrics-snapshot-hourly` | Collect hourly metric snapshots used by rising/heat calculations | `php artisan nova:metrics-snapshot-hourly --days=1` |
| `nova:prune-metric-snapshots` | Delete old hourly metric snapshots past the retention window | `php artisan nova:prune-metric-snapshots --keep-days=90` |
| `nova:recalculate-heat` | Recalculate heat and momentum scores for the rising engine | `php artisan nova:recalculate-heat --days=30 --chunk=500` |
| `nova:recalculate-rankings` | Recalculate V2 ranking scores | `php artisan nova:recalculate-rankings --chunk=500 --sync-rank-scores` |

### Nova Cards

| Command | Why it is used | Example |
| --- | --- | --- |
| `nova-cards:publish-scheduled` | Publish scheduled Nova cards | `php artisan nova-cards:publish-scheduled --limit=100` |

### Posts

| Command | Why it is used | Example |
| --- | --- | --- |
| `posts:publish-scheduled` | Publish scheduled posts when their publish time arrives | `php artisan posts:publish-scheduled` |
| `posts:warm-trending` | Refresh the post-trending cache | `php artisan posts:warm-trending` |

### Schema

| Command | Why it is used | Example |
| --- | --- | --- |
| `schema:audit-migrations` | Compare the live schema with executed migration files | `php artisan schema:audit-migrations --json` |

### Skinbase Core

| Command | Why it is used | Example |
| --- | --- | --- |
| `skinbase:audit-orphaned-artworks` | Find artworks whose `user_id` no longer exists and optionally export repair data | `php artisan skinbase:audit-orphaned-artworks --chunk=1000 --output=orphaned.csv` |
| `skinbase:backfill-user-activities` | Backfill historical profile activity into `user_activities` | `php artisan skinbase:backfill-user-activities --chunk=500 --dry-run` |
| `skinbase:enforce-usernames` | Normalize and enforce the username policy on existing users | `php artisan skinbase:enforce-usernames --dry-run` |
| `skinbase:flag-legacy-users` | Flag legacy users for migration based on activity across legacy tables | `php artisan skinbase:flag-legacy-users --chunk=1000 --dry-run` |
| `skinbase:flush-redis-stats` | Drain Redis artwork stat deltas into MySQL | `php artisan skinbase:flush-redis-stats --max=1000` |
| `skinbase:import-legacy-artworks` | Import artworks from the legacy `wallz` table | `php artisan skinbase:import-legacy-artworks --chunk=200 --dry-run` |
| `skinbase:import-legacy-users` | Import legacy users into the current auth schema | `php artisan skinbase:import-legacy-users --chunk=200 --dry-run` |
| `skinbase:migrate-favourites` | Migrate legacy favourites into `artwork_favourites` | `php artisan skinbase:migrate-favourites --chunk=500 --dry-run` |
| `skinbase:migrate-follows` | Migrate legacy `friends_list` data into `user_followers` | `php artisan skinbase:migrate-follows --chunk=500 --dry-run` |
| `skinbase:migrate-messages` | Migrate legacy chat/messages into the conversation system | `php artisan skinbase:migrate-messages --chunk=500 --dry-run` |
| `skinbase:migrate-smileys` | Convert legacy `:smiley:` codes to Unicode emoji in content fields | `php artisan skinbase:migrate-smileys --chunk=500 --dry-run` |
| `skinbase:migrate-wallz-stats` | Import views/downloads from legacy `wallz` into `artwork_stats` | `php artisan skinbase:migrate-wallz-stats --chunk=500 --dry-run` |
| `skinbase:prune-view-events` | Delete old `artwork_view_events` rows | `php artisan skinbase:prune-view-events --days=90` |
| `skinbase:rebuild-creator-journey` | Rebuild persisted creator-journey milestones | `php artisan skinbase:rebuild-creator-journey --all --chunk=200` |
| `skinbase:recalculate-trending` | Recalculate artwork trending scores and sync them to search | `php artisan skinbase:recalculate-trending --period=30d --chunk=500` |
| `skinbase:recalculate-user-xp` | Rebuild stored XP, level, and rank from `user_xp_logs` | `php artisan skinbase:recalculate-user-xp --all --chunk=500` |
| `skinbase:recompute-user-stats` | Rebuild `user_statistics` counters from source tables | `php artisan skinbase:recompute-user-stats --all --chunk=500` |
| `skinbase:repair-temp-usernames` | Replace placeholder usernames such as `tmpu123` using user profile data | `php artisan skinbase:repair-temp-usernames --chunk=500 --dry-run` |
| `skinbase:repair-user-join-dates` | Backfill `users.created_at` from the legacy `joinDate` value | `php artisan skinbase:repair-user-join-dates --chunk=500 --only-null --dry-run` |
| `skinbase:rescan-content-moderation` | Reprocess stored moderation findings using the latest rules | `php artisan skinbase:rescan-content-moderation --limit=100 --force` |
| `skinbase:reset-all-passwords` | Reset every user password and force password reset on next login | `php artisan skinbase:reset-all-passwords --chunk=500 --yes` |
| `skinbase:reset-windowed-stats` | Reset windowed view/download counters in `artwork_stats` | `php artisan skinbase:reset-windowed-stats --period=30d` |
| `skinbase:sanitize-content` | Sanitize legacy HTML and populate sanitized columns | `php artisan skinbase:sanitize-content --chunk=500 --dry-run` |
| `skinbase:scan-content-moderation` | Scan comments and descriptions for suspicious or spam-like content | `php artisan skinbase:scan-content-moderation --limit=100 --dry-run` |
| `skinbase:sitemaps:build` | Build a versioned sitemap release artifact set | `php artisan skinbase:sitemaps:build --release=manual-20260417 --dry-run` |
| `skinbase:sitemaps:publish` | Build, validate, and atomically publish a sitemap release | `php artisan skinbase:sitemaps:publish --sync` |
| `skinbase:sitemaps:releases` | List recent sitemap releases and the active release | `php artisan skinbase:sitemaps:releases` |
| `skinbase:sitemaps:rollback` | Roll back sitemap delivery to a previous published release | `php artisan skinbase:sitemaps:rollback release-20260416-1` |
| `skinbase:sitemaps:validate` | Validate sitemap XML, shard integrity, and public URL safety | `php artisan skinbase:sitemaps:validate --active` |
| `skinbase:sync-countries` | Synchronize ISO 3166 country metadata into the local countries table | `php artisan skinbase:sync-countries --deactivate-missing` |

### Stories

| Command | Why it is used | Example |
| --- | --- | --- |
| `stories:migrate-legacy` | Import legacy interview/story records into the Nova Stories system | `php artisan stories:migrate-legacy --chunk=200 --dry-run` |

### Storage

| Command | Why it is used | Example |
| --- | --- | --- |
| `storage:test-upload` | Upload a probe file to object storage and verify persistence | `php artisan storage:test-upload --disk=s3 --keep` |

### Tags

| Command | Why it is used | Example |
| --- | --- | --- |
| `tags:fix-names` | Convert slug-style tag names into readable display names | `php artisan tags:fix-names --dry-run` |

### Translations

| Command | Why it is used | Example |
| --- | --- | --- |
| `translations:export-missing` | Export missing translations for one language file into CSV | `php artisan translations:export-missing admin --out=storage/app/missing-admin.csv` |

### Users

| Command | Why it is used | Example |
| --- | --- | --- |
| `users:audit-missing-migrated` | List legacy users marked for migration but still missing from the new users table | `php artisan users:audit-missing-migrated --chunk=1000` |

## Ad Hoc and Inspection Scripts

These are useful, but they read more like maintenance probes and one-off utilities than stable product commands.

### scripts/ Helpers

| Script | Why it is used | Example |
| --- | --- | --- |
| `scripts/check_col_charset.php` | Inspect charset/collation of artwork comment content columns | `php scripts/check_col_charset.php` |
| `scripts/check_fp_translations.php` | Inspect translation-file stats and active language rows | `php scripts/check_fp_translations.php admin` |
| `scripts/check_intervention.php` | Verify the available image-processing backend such as GD or Imagick | `php scripts/check_intervention.php` |
| `scripts/check_redis.php` | Ping Redis and inspect the artwork stat delta queue depth | `php scripts/check_redis.php` |
| `scripts/check_reply_tree.php` | Inspect the artwork-comment reply tree for a hardcoded test case | `php scripts/check_reply_tree.php` |
| `scripts/check_stats.php` | Inspect a specific artwork stats row and related counters | `php scripts/check_stats.php` |
| `scripts/check_stats2.php` | Alternate stats inspection helper for the same debugging area | `php scripts/check_stats2.php` |
| `scripts/fill_translations_csv.php` | Fill the `Value` column in a translations CSV from language files | `php scripts/fill_translations_csv.php` |
| `scripts/fix_artworks_updated_at.php` | Sync `artworks.updated_at` to `created_at` in bulk | `php scripts/fix_artworks_updated_at.php --dry-run` |
| `scripts/inspect_autotag_jobs.php` | Inspect queued and failed `AutoTagArtworkJob` jobs | `php scripts/inspect_autotag_jobs.php` |
| `scripts/inspect_legacy_users.php` | Inspect the legacy users table schema and a sample row | `php scripts/inspect_legacy_users.php` |
| `scripts/populate_sl_translations.php` | Translate missing strings to Slovenian using LibreTranslate | `php scripts/populate_sl_translations.php input.csv output.csv` |
| `scripts/repair_broken_artworks.php` | Repair artworks with missing or invalid metadata | `php scripts/repair_broken_artworks.php --limit=25 --publish` |
| `scripts/rerender_comments.php` | Re-render artwork comments through the content sanitizer | `php scripts/rerender_comments.php` |
| `scripts/verify_upload_publish.php` | Check that an artwork has the fields required for upload/publish | `php scripts/verify_upload_publish.php --id=123 --limit=5` |

### Temporary Root Scripts

| Script | Why it is used | Example |
| --- | --- | --- |
| `temp-ai-debug.php` | Temporary PHP debug probe for AI-related investigation | `php temp-ai-debug.php` |
| `temp-ai-title-debug.php` | Temporary PHP debug probe for AI title generation/debugging | `php temp-ai-title-debug.php` |
| `temp-check-collection-controllers.php` | Temporary PHP inspection script for collection controllers | `php temp-check-collection-controllers.php` |
| `temp-check-collection-services.php` | Temporary PHP inspection script for collection services | `php temp-check-collection-services.php` |
| `temp-check-user-id-cols.php` | Temporary PHP probe for user ID column inspection | `php temp-check-user-id-cols.php` |
| `temp-latest-playwright-owner.php` | Temporary PHP probe related to Playwright ownership/debug output | `php temp-latest-playwright-owner.php` |
| `temp-playwright-probe.cjs` | Temporary Node/CommonJS Playwright probe | `node temp-playwright-probe.cjs` |
| `temp-playwright-probe.js` | Temporary Node Playwright probe | `node temp-playwright-probe.js` |
| `temp-probe-ai-title.cjs` | Temporary Node/CommonJS AI title probe | `node temp-probe-ai-title.cjs` |

## Notes

- The deploy mirror under `.deploy/artwork-evolution-release` contains equivalent command classes for deployment packaging, but this reference is sourced from the main repository tree.
- For commands that mutate production data, prefer running a dry-run mode first when one exists.
- For one-off scripts, read the file before using it. Several are intentionally ad hoc and may contain hardcoded IDs, paths, or assumptions.

## Scheduler

- **Active scheduler file:** The application's active runtime schedule is consolidated in [routes/console.php](routes/console.php). Do not duplicate recurring entries in [app/Console/Kernel.php](app/Console/Kernel.php) — the app boots command routes from `bootstrap/app.php` and registering the same cron bodies in multiple places can lead to duplicate runs.
- **Validation:** After editing schedules run `php artisan schedule:list` to confirm Laravel parses the schedule and to view the resolved cron timings. This command also surfaces invalid frequency methods (for example `everyHour()` or `everyFiveMinute()`) which will break schedule introspection.
- **Best practice:** Stagger heavy jobs away from the same minute boundaries (avoid piling multiple CPU-heavy commands on `:00`, `:10`, `:15`, `:30`). Prefer cron offsets or `->cron()` expressions in `routes/console.php` and use `->withoutOverlapping()` and `->runInBackground()` where appropriate.