seedproject-web/api/commands/commands.md
Carlos Arias 1559ce017d chore: scaffold SeedProject base (Phase 1)
Clean-room copy of the reusable engines from comiida, with all
instance data, secrets, dependencies, and build output excluded:
- app/         Astro theme skeleton (no comiida blog posts; hero image -> placeholder)
- api/         SeedProject PHP framework (no vendor/.env/config.php)
- content-pipeline/  engine only (scripts/admin/prompts; empty runtime state)
- astroagent.config.json + app/.astroagent/skills

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SYHWLHihq3v9nxNwoPCKSn
2026-07-04 22:53:10 +00:00

119 lines
2.9 KiB
Markdown

# CLI Commands — `commands/`
Scripts run via `php console <CommandName>`. No HTTP context — output goes to stdout.
## File Naming
`PascalCaseCommand.php` — e.g. `SyncDataCommand.php`
## Running Commands
```bash
php console SyncDataCommand
php console SyncDataCommand --date="2024-10-28" --force
php console CleanupDatabaseCommand --days=30 --dry-run
php console ProcessEmailQueueCommand --limit=50
```
## Command Structure
```php
<?php
// commands/SyncDataCommand.php
echo "=== Data Sync ===\n";
$options = getopt("", ["date:", "force"]);
$date = $options['date'] ?? 'today';
$force = isset($options['force']);
echo "Syncing for: $date\n";
try {
$records = Db::select("SELECT * FROM sp_sync_queue WHERE synced_at IS NULL");
foreach ($records as $record) {
if (!$force) {
$exists = Db::getRow("SELECT 1 FROM sp_data WHERE external_id = :id LIMIT 1",
[':id' => $record['external_id']]
);
if ($exists) { echo "Skipping {$record['external_id']}\n"; continue; }
}
Db::insert('sp_data', [
'external_id' => $record['external_id'],
'payload' => $record['payload'],
'synced_at' => date('Y-m-d H:i:s')
]);
echo "Inserted {$record['external_id']}\n";
}
echo "Done.\n";
} catch (Exception $e) {
echo "ERROR: " . $e->getMessage() . "\n";
exit(1);
}
```
## Error Handling Pattern
Log individual failures and keep processing — exit with code 1 only on fatal errors:
```php
$errors = [];
$processed = 0;
foreach ($items as $item) {
try {
processItem($item);
$processed++;
echo ".";
} catch (Exception $e) {
$errors[] = ['id' => $item['id'], 'error' => $e->getMessage()];
echo "E";
}
}
echo "\n\nProcessed: $processed | Errors: " . count($errors) . "\n";
if (!empty($errors)) {
foreach ($errors as $err) {
echo "Item #{$err['id']}: {$err['error']}\n";
}
exit(1);
}
```
## Dry-Run Pattern
```php
$dryRun = isset($options['dry-run']);
if ($dryRun) { echo "DRY RUN — no changes will be made\n"; }
$count = Db::getRow("SELECT COUNT(*) as n FROM sp_logs WHERE created_at < :date",
[':date' => $cutoffDate]
);
echo "Rows to delete: {$count['n']}\n";
if (!$dryRun) {
Db::delete('sp_logs', 'created_at < :date', [':date' => $cutoffDate]);
}
```
## Cron Setup
```bash
crontab -e
```
```cron
# Every 5 minutes
*/5 * * * * cd /www/wwwroot/appSeedProject && php console ProcessEmailQueueCommand >> /var/log/email-queue.log 2>&1
# Every hour
0 * * * * cd /www/wwwroot/appSeedProject && php console SyncDataCommand >> /var/log/sync.log 2>&1
# Nightly cleanup at 2 AM
0 2 * * * cd /www/wwwroot/appSeedProject && php console CleanupDatabaseCommand --days=30 >> /var/log/cleanup.log 2>&1
```