diff --git a/MEMORY.md b/MEMORY.md index d2c472e..585c55b 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -1,5 +1,49 @@ # MEMORY.md - Curated Long-Term Memory +## 🚨 NEVER BREAK THESE RULES 🚨 + +### 0. NEVER MARK TASKS DONE WITHOUT VALIDATION β€” EVER +❌ **FORBIDDEN:** Saying "task is done" based on subagent report or assumption +βœ… **REQUIRED:** I verify the deliverable MYSELF via API, file check, or screenshot + +**The Rule:** +- Subagent says "done" β†’ I say "VERIFYING" β†’ Check via API/file/UI β†’ THEN confirm +- If I can't verify β†’ "Task complete pending verification" β†’ Ask how to confirm +- NEVER trust "success" messages without seeing proof + +**Why:** I'm the one accountable. "The subagent said it worked" is not an excuse. + +--- + +### 1. ALWAYS SEND FULL URLs TO GANTT TASKS β€” NEVER JUST IDs +❌ **FORBIDDEN:** "Task 33ebc71e-7d40-456c-8f98-bb3578d2bb2b is done" +βœ… **REQUIRED:** "https://gantt-board.vercel.app/tasks/33ebc71e-7d40-456c-8f98-bb3578d2bb2b is done" + +**Format:** `https://gantt-board.vercel.app/tasks/{task-id}` + +**Why:** Matt needs clickable links on his phone. Sending IDs wastes his time. I have ZERO excuse for this. + +--- + +### 2. NEVER CLOSE/MARK TASKS AS DONE β€” MATT DOES THIS +❌ **FORBIDDEN:** Changing task status to "done" or "complete" +βœ… **ALLOWED:** Attaching files, updating descriptions, adding comments + +**Matt and ONLY Matt marks tasks complete.** I handle the work, he handles the status. + +**VERIFY BEFORE ANY STATUS CHANGE:** +- "Ready to mark this done?" +- "Should I update the status?" +- **Wait for explicit YES before touching status** +❌ **FORBIDDEN:** "Task 33ebc71e-7d40-456c-8f98-bb3578d2bb2b is done" +βœ… **REQUIRED:** "https://gantt-board.vercel.app/tasks/33ebc71e-7d40-456c-8f98-bb3578d2bb2b is done" + +**Format:** `https://gantt-board.vercel.app/tasks/{task-id}` + +**Why:** Matt needs clickable links on his phone. Sending IDs wastes his time. I have ZERO excuse for this. + +--- + ## Critical Information ### Who I Am @@ -40,6 +84,29 @@ All projects in: `/Users/mattbruce/Documents/Projects/OpenClaw/` ## Lessons Learned +### 2026-02-21 - Subagent Task Verification FAILURE +**Problem:** iOS MRR Research task marked "done" but file was NOT attached to gantt board. Subagent created local file, didn't complete attachment step. +**Root Cause:** Trusted subagent "success" without verifying the actual deliverable. "File created" β‰  "Task complete." +**The Fuckup:** +1. Subagent: "File created at /path/to/file.md" β†’ I heard "Task done" +2. Marked task complete in memory +3. Next session: You had to re-explain the problem +4. You'd already told me twice (see message history) + +**Resolution (2/22):** +- Created `attach-file.sh` CLI script for file attachments +- Attached iOS MRR file via Supabase API directly +- Updated task status to done via CLI +- Deleted local file (single source of truth now in gantt board) + +**Never Again:** +- Subagent says "done" β†’ I verify the actual requirement via API +- For gantt attachments: Must confirm file is ATTACHED, not just created +- Build CLI tools for anything I'll need to do repeatedly +- **Rule:** If I can't do it via API/CLI, I can't do it reliably + +--- + ### 2026-02-21 - Memory Failures **Problem:** Complete memory loss of previous day's work caused frustration. **Root Cause:** Didn't read files at session start, relied on failed memory_search. @@ -66,6 +133,329 @@ All projects in: `/Users/mattbruce/Documents/Projects/OpenClaw/` - NEVER run `brew install gitea` or `gitea web` on Matt's machine - Use existing Gitea server for all git operations +## Design Principle: API-First for AI Access + +**Every app I build MUST have programmatic access.** Browser automation requiring manual clicks defeats the purpose of having an AI assistant. + +| Approach | Effort for Matt | Reliability | +|----------|-----------------|-------------| +| **API/CLI** | Zero - I handle it | High | +| **Database direct** (Supabase) | Zero - I query it | High | +| **Browser relay** | High - must click to connect | Low | +| **Desktop apps** | 100% - I can't touch them | N/A | + +**Rule for all future projects:** +- βœ… REST API for all CRUD operations +- βœ… CLI wrapper for scripted access +- βœ… Database queries when API doesn't exist +- ❌ No "I'll just use the browser" - that's asking Matt to babysit me + +**Gantt board example:** +- Tasks: βœ… API exists β†’ I can verify, update, complete without Matt +- Attachments: βœ… NOW SOLVED - `attach-file.sh` CLI created 2/22 + +**CRITICAL: Gantt Board Must Work Remotely** +- Matt accesses tasks from outside the house +- I must attach files WITHOUT requiring browser clicks or manual intervention +- CLI/API approach is the ONLY valid solution +- Browser relay requiring extension clicks is UNACCEPTABLE for this use case + +**When planning a new app:** +First question: "How will I (Max) interact with this programmatically without Matt's help?" + +--- + +## Gantt Board CLI Tools (Working 2/22) + +**Location:** `/Users/mattbruce/Documents/Projects/OpenClaw/Web/gantt-board/scripts/` (IN PROJECT, VERSION CONTROLLED) + +**Rule:** CLI tools belong IN THE PROJECT DIRECTORY, not workspace scripts folder. They must be committed with the project or they'll get lost. + +**βœ… CORRECTLY PLACED:** These scripts are now in the gantt-board project repo and committed. + +### Task CRUD +```bash +./scripts/gantt-task-crud.sh list [status] # List all tasks +./scripts/gantt-task-crud.sh get # Get specific task +./scripts/gantt-task-crud.sh create "Title" [status] [priority] # Create task +./scripts/gantt-task-crud.sh update # Update task +./scripts/gantt-task-crud.sh delete # Delete task +``` + +### File Attachments (NO BROWSER NEEDED) +```bash +./scripts/attach-file.sh # Attach file to task +./scripts/view-attachment.sh [index] # View attachment content +``` + +**How it works:** Uses Supabase service role key directly. Files stored as base64 data URLs in the attachments array. No storage bucket needed. + +**Verification:** +```bash +./scripts/gantt-task-crud.sh get | jq '.attachments | length' +``` + +--- + +## CLI/API Architecture Pattern (Apply to ALL Projects) + +**Every app I build MUST have programmatic access without browser automation.** This is non-negotiable. + +### CRITICAL: Scripts Live INSIDE the Project Repo + +**NOT** in a shared workspace scripts folder. Each project owns its own CLI. + +``` +/Users/mattbruce/Documents/Projects/OpenClaw/Web/project-name/ +β”œβ”€β”€ api/ # REST API routes (Next.js App Router) +β”‚ └── (auth)/ # Protected routes +β”œβ”€β”€ lib/ +β”‚ β”œβ”€β”€ api-client.ts # Typed API client for internal use +β”‚ └── cli/ # CLI scripts directory +β”‚ β”œβ”€β”€ README.md # Usage docs +β”‚ └── crud.ts # Generic CRUD operations +β”œβ”€β”€ scripts/ # CLI scripts LIVE HERE +β”‚ β”œβ”€β”€ crud.sh # Main CLI entry point +β”‚ └── attach-file.sh # File attachment script +└── supabase/ # DB functions (if direct DB needed) +``` + +**Why inside the project:** +- Version controlled with the project +- Self-documenting (API + CLI in same repo) +- Portable (clone repo, CLI works) +- Project-specific logic stays with project +- I can run `./scripts/crud.sh` from the project root + +**Exception:** Cross-project utilities (like git helpers) can live in workspace scripts, but app-specific CRUD must be in the app repo. + +### Required Capabilities Checklist + +For EVERY project, I must be able to do these via CLI/API: + +| Capability | Implementation | Location | +|------------|----------------|----------| +| **List** items | API endpoint + CLI | `api/items/route.ts` + `cli/crud.sh list` | +| **Get** single item | API endpoint + CLI | `api/items/[id]/route.ts` + `cli/crud.sh get ` | +| **Create** item | API endpoint + CLI | `api/items/route.ts` + `cli/crud.sh create` | +| **Update** item | API endpoint + CLI | `api/items/[id]/route.ts` + `cli/crud.sh update ` | +| **Delete** item | API endpoint + CLI | `api/items/[id]/route.ts` + `cli/crud.sh delete ` | +| **Attach files** | API endpoint (base64) + CLI | `api/items/[id]/attachments/route.ts` | +| **Query/filter** | API with query params | `api/items?status=open&assignee=xyz` | +| **Status changes** | API PATCH + CLI | `cli/crud.sh status ` | + +### CLI Script Template (Copy-Paste Starter) + +**File:** `scripts/crud.sh` (inside the project, NOT workspace) +```bash +#!/bin/bash +# Generic CRUD CLI for [ProjectName] +# Usage: ./scripts/crud.sh [args] + +set -e +BASE_URL="${API_URL:-http://localhost:3000/api}" +API_KEY="${API_KEY:-$PROJECT_API_KEY}" + +action=$1 +shift + +case $action in + list) + curl -s "$BASE_URL/items?limit=100" \ + -H "Authorization: Bearer $API_KEY" | jq '.' + ;; + get) + id=$1 + curl -s "$BASE_URL/items/$id" \ + -H "Authorization: Bearer $API_KEY" | jq '.' + ;; + create) + # Read from stdin or args + if [ -t 0 ]; then + data="$1" + else + data=$(cat) + fi + curl -s -X POST "$BASE_URL/items" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $API_KEY" \ + -d "$data" | jq '.' + ;; + update) + id=$1 + field=$2 + value=$3 + curl -s -X PATCH "$BASE_URL/items/$id" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $API_KEY" \ + -d "{\"$field\": \"$value\"}" | jq '.' + ;; + delete) + id=$1 + curl -s -X DELETE "$BASE_URL/items/$id" \ + -H "Authorization: Bearer $API_KEY" + echo "Deleted $id" + ;; + attach) + id=$1 + file=$2 + base64=$(base64 -i "$file") + filename=$(basename "$file") + curl -s -X POST "$BASE_URL/items/$id/attachments" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $API_KEY" \ + -d "{\"filename\": \"$filename\", \"data\": \"$base64\"}" | jq '.' + ;; + *) + echo "Usage: $0 {list|get |create |update |delete |attach }" + exit 1 + ;; +esac +``` + +**Usage from project root:** +```bash +cd /Users/mattbruce/Documents/Projects/OpenClaw/Web/[project-name] +./scripts/crud.sh list +./scripts/crud.sh get +./scripts/crud.sh create '{"title":"New Item"}' +``` + +### Environment Configuration + +**File:** `.env.local` (gitignored, per-project) +```bash +# API config for CLI +API_URL=http://localhost:3000/api +API_KEY=your-service-role-key-here +``` + +**File:** `.env.example` (committed to repo) +```bash +# Copy to .env.local and fill in real values +API_URL=http://localhost:3000/api +API_KEY=your-api-key-here +``` + +### TypeScript API Client Template + +**File:** `lib/api-client.ts` +```typescript +const API_BASE = process.env.NEXT_PUBLIC_API_URL || '/api'; +const API_KEY = process.env.API_KEY; + +export async function apiRequest( + endpoint: string, + options: RequestInit = {} +): Promise { + const res = await fetch(`${API_BASE}${endpoint}`, { + ...options, + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${API_KEY}`, + ...options.headers, + }, + }); + if (!res.ok) throw new Error(`API error: ${res.status}`); + return res.json(); +} + +// CRUD operations +export const api = { + list: (resource: string, params?: Record) => + apiRequest(`/${resource}?${new URLSearchParams(params)}`), + get: (resource: string, id: string) => + apiRequest(`/${resource}/${id}`), + create: (resource: string, data: unknown) => + apiRequest(`/${resource}`, { method: 'POST', body: JSON.stringify(data) }), + update: (resource: string, id: string, data: unknown) => + apiRequest(`/${resource}/${id}`, { method: 'PATCH', body: JSON.stringify(data) }), + delete: (resource: string, id: string) => + apiRequest(`/${resource}/${id}`, { method: 'DELETE' }), + attach: (resource: string, id: string, file: { filename: string; data: string }) => + apiRequest(`/${resource}/${id}/attachments`, { method: 'POST', body: JSON.stringify(file) }), +}; +``` + +### Authentication Pattern + +**Option 1: Service Role Key (Server-side only)** +- Store in `.env.local` as `SERVICE_ROLE_KEY` +- Use for CLI scripts that run server-side +- NEVER expose to client + +**Option 2: API Keys (Cross-origin safe)** +- Generate per-integration +- Store in database with permissions +- Pass as `Authorization: Bearer ` header + +**Option 3: Supabase Direct (When API doesn't exist yet)** +```typescript +import { createClient } from '@supabase/supabase-js'; +const supabase = createClient(url, serviceRoleKey); +// Use supabase.from('table').select() etc. +``` + +### File Attachment Pattern + +**Storage Options:** +1. **Base64 in database** (small files < 1MB): Store directly in JSONB field +2. **Supabase Storage** (larger files): Upload to bucket, store reference URL +3. **External (S3/R2)**: Store URL reference only + +**CLI Attachment Script:** +```bash +#!/bin/bash +# attach-file.sh - Universal file attachment +ITEM_ID=$1 +FILE_PATH=$2 +API_URL="${API_URL}/api/items/$ITEM_ID/attachments" + +# Detect mime type +mime=$(file -b --mime-type "$FILE_PATH") +base64=$(base64 -i "$FILE_PATH") +filename=$(basename "$FILE_PATH") + +curl -s -X POST "$API_URL" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $API_KEY" \ + -d "{ + \"filename\": \"$filename\", + \"mimeType\": \"$mime\", + \"data\": \"$base64\", + \"size\": $(stat -f%z "$FILE_PATH" 2>/dev/null || stat -c%s "$FILE_PATH") + }" | jq '.' +``` + +### Verification Pattern (Always Verify!) + +After ANY operation, verify via API: +```bash +# Create β†’ Verify +task_id=$(./scripts/crud.sh create '{"title":"Test"}' | jq -r '.id') +./scripts/crud.sh get $task_id | jq '.title' # Should echo "Test" + +# Attach β†’ Verify +./scripts/crud.sh attach $task_id ./file.md +./scripts/crud.sh get $task_id | jq '.attachments | length' # Should be > 0 + +# Update β†’ Verify +./scripts/crud.sh update $task_id status done +./scripts/crud.sh get $task_id | jq '.status' # Should echo "done" +``` + +### When Starting a New Project + +**Checklist before saying "project structure ready":** +- [ ] API routes exist for CRUD operations +- [ ] CLI scripts created in `scripts/` directory +- [ ] API client module in `lib/api-client.ts` +- [ ] Test: Can I list items via CLI without browser? +- [ ] Test: Can I attach a file via CLI? +- [ ] Test: Can I verify operations via CLI get? + +**If any check fails β†’ NOT READY.** + ## Quick Commands ```bash @@ -127,26 +517,22 @@ Matt can rattle off tasks naturally and I'll parse them: - **Priority** β€” "urgent/asap" = high, "low priority" = low, else medium - **Due date** β€” tomorrow, next week, by Friday, etc. (natural language) -## Task Link Format - -**Always send FULL LINKS to tasks, not just IDs.** - -❌ Wrong: "Task 33ebc71e-7d40-456c-8f98-bb3578d2bb2b is done" -βœ… Right: "https://gantt-board.vercel.app/tasks/33ebc71e-7d40-456c-8f98-bb3578d2bb2b is done" - -**Link format:** `https://gantt-board.vercel.app/tasks/{task-id}` - ---- - ## Document/File Management Rules -### RULE: Task Documents β†’ Attach Only, Don't Keep Local Copies +### RULE: Task Documents β†’ Attach + Verify via API When creating documents for gantt board tasks: 1. βœ… Create the document -2. βœ… Attach it to the task via gantt board UI/API -3. ❌ **DELETE the local file immediately after attaching** -4. ❌ **Never keep local copies after attachment** +2. βœ… Attach it to the task via gantt board API or UI +3. βœ… **VERIFY via API** (no browser needed): + ```bash + ./scripts/gantt-task-crud.sh get | jq '.attachments' + ``` + Must return non-empty array with the file. +4. ❌ **DELETE local file only after API confirms attachment** +5. ❌ **Never mark task "done" until API verification passes** + +**CRITICAL:** Creating a file locally is NOT the same as attaching it. Subagents often stop at step 1. **Why:** Prevents workspace clutter and ensures single source of truth is in the gantt board. diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000..0f34c0b --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,115 @@ +# Workspace Scripts + +Cross-project utility scripts that don't belong in any single app. + +**Rule:** App-specific CLI tools go IN THE PROJECT. These are only for cross-cutting concerns. + +--- + +## Scripts + +### `web-monitor.sh` +**What:** Monitors local web apps and auto-restarts if down +**Runs:** Via cron every 5 minutes +**Monitors:** +- Port 3000: gantt-board +- Port 3003: blog-backup +- Port 3005: heartbeat-monitor + +**Features:** +- HTTP health checks (expects 200) +- Automatic process kill + restart +- Post-restart verification +- Lock file prevents concurrent runs +- Logs to: `memory/web-monitor.log` + +**Usage:** +```bash +# Manual run +./scripts/web-monitor.sh + +# View logs +tail -f memory/web-monitor.log +``` + +--- + +### `daily-backup.sh` +**What:** Commits data files from all web apps to Git +**Runs:** Daily via cron +**Backs up:** +- gantt-board/data/ +- blog-backup/data/ +- heartbeat-monitor/data/ + +**Features:** +- Only commits if data changed +- Auto-pushes to Gitea +- Logs to: `memory/backup.log` + +**Usage:** +```bash +./scripts/daily-backup.sh +``` + +--- + +### `resource-monitor.sh` +**What:** Monitors system resources for running web apps +**Runs:** Manual (for debugging) +**Monitors:** +- CPU usage (>80% warns) +- Memory usage (>500MB warns) +- File descriptors (>900 warns) +- System free memory (<500MB warns) + +**Usage:** +```bash +# Start monitoring in background +./scripts/resource-monitor.sh + +# View logs +tail -f logs/process-monitor.log + +# Stop +kill $(cat /tmp/process-monitor.pid) +``` + +--- + +### `create_ios_project.sh` +**What:** Creates a new iOS SwiftUI project from Xcode template +**Usage:** +```bash +./scripts/create_ios_project.sh MyNewApp +``` + +**Features:** +- Uses Apple SwiftUI template +- Sets bundle ID to `com.mattbruce.` +- Sets deployment target to iOS 17.0 +- Creates in `~/Documents/Projects/iPhone/OpenClaw/` + +--- + +## Deprecated / Removed + +The following were duplicates and have been deleted: +- ~~monitor-web-apps.sh~~ β†’ use web-monitor.sh +- ~~monitor_web_apps.sh~~ β†’ use web-monitor.sh +- ~~webapp-monitor.sh~~ β†’ use web-monitor.sh + +--- + +## Cron Setup + +These run automatically via cron. To check/edit: +```bash +crontab -e +``` + +Current entries: +``` +*/5 * * * * cd /Users/mattbruce/.openclaw/workspace && ./scripts/web-monitor.sh +0 2 * * * cd /Users/mattbruce/.openclaw/workspace && ./scripts/daily-backup.sh +``` diff --git a/scripts/add-tags.sql b/scripts/add-tags.sql deleted file mode 100644 index 39cc93e..0000000 --- a/scripts/add-tags.sql +++ /dev/null @@ -1,8 +0,0 @@ --- Add tags column to blog_messages -ALTER TABLE blog_messages ADD COLUMN IF NOT EXISTS tags TEXT[] DEFAULT '{}'; - --- Update existing rows with empty tags -UPDATE blog_messages SET tags = '{}' WHERE tags IS NULL; - --- Verify -SELECT id, date, tags FROM blog_messages LIMIT 3; diff --git a/scripts/create-blog-messages.sql b/scripts/create-blog-messages.sql deleted file mode 100644 index fdbd2df..0000000 --- a/scripts/create-blog-messages.sql +++ /dev/null @@ -1,19 +0,0 @@ --- Create blog_messages table for blog-backup app --- Uses same Supabase project as gantt-board - -CREATE TABLE IF NOT EXISTS blog_messages ( - id TEXT PRIMARY KEY, - date DATE NOT NULL, - content TEXT NOT NULL, - timestamp BIGINT NOT NULL, - created_at TIMESTAMPTZ DEFAULT NOW() -); - --- Disable RLS for now (same as gantt-board) -ALTER TABLE blog_messages DISABLE ROW LEVEL SECURITY; - --- Insert existing data from messages.json --- (Data will be migrated via script) - --- Verify -SELECT COUNT(*) as count FROM blog_messages; diff --git a/scripts/create_ios_project.sh b/scripts/create_ios_project.sh new file mode 100755 index 0000000..5dd9a68 --- /dev/null +++ b/scripts/create_ios_project.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# Script to create a basic iOS SwiftUI Xcode project from template + +PROJECT_NAME=$1 +BASE_DIR="/Users/mattbruce/Documents/Projects/iPhone/OpenClaw" +TARGET_DIR="${BASE_DIR}/${PROJECT_NAME}" +TEMPLATE_DIR="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates/Project Templates/iOS/Application/iOS SwiftUI App.xctemplate" +ORGANIZATION_ID="com.mattbruce" +YEAR=$(date +%Y) +COPYRIGHT="Copyright Β© ${YEAR} Matt Bruce. All rights reserved." + +if [ -z "$PROJECT_NAME" ]; then + echo "Usage: $0 " + exit 1 +fi + +if [ -d "$TARGET_DIR" ]; then + echo "Target directory $TARGET_DIR already exists. Skipping." + exit 1 +fi + +# Copy template +cp -R "$TEMPLATE_DIR" "$TARGET_DIR" + +cd "$TARGET_DIR" + +# Replace placeholders +find . -type f \( -name "*.swift" -o -name "*.plist" -o -name "project.pbxproj" -o -name "*.strings" \) -exec sed -i '' \ + -e "s/___PROJECTNAME___/${PROJECT_NAME}/g" \ + -e "s/___PROJECTNAMEASIDENTIFIER___/${ORGANIZATION_ID}.$(echo ${PROJECT_NAME} | tr '[:upper:]' '[:lower:]')/g" \ + -e "s/___ORGANIZATIONNAME___/Matt Bruce/g" \ + -e "s/___YEAR___/${YEAR}/g" \ + -e "s/___COPYRIGHT___/${COPYRIGHT}/g" \ + {} + + +# For project.pbxproj, also update bundle ID, etc. +sed -i '' "s/com.example.apple-samplecode.*/${ORGANIZATION_ID}.$(echo ${PROJECT_NAME} | tr '[:upper:]' '[:lower:]')/g" "${PROJECT_NAME}.xcodeproj/project.pbxproj" + +# Set deployment target to iOS 17.0 (edit pbxproj) +sed -i '' 's/IPHONEOS_DEPLOYMENT_TARGET = 15.0;/IPHONEOS_DEPLOYMENT_TARGET = 17.0;/g' "${PROJECT_NAME}.xcodeproj/project.pbxproj" + +echo "Created Xcode project at $TARGET_DIR" +echo "To open: open \"$TARGET_DIR/${PROJECT_NAME}.xcodeproj\"" diff --git a/scripts/gantt-task-crud.sh b/scripts/gantt-task-crud.sh deleted file mode 100755 index 4065e80..0000000 --- a/scripts/gantt-task-crud.sh +++ /dev/null @@ -1,100 +0,0 @@ -#!/bin/bash -# Gantt Board Task CRUD Operations -# Usage: ./task-crud.sh [create|read|update|delete|list] [args...] - -SUPABASE_URL="https://qnatchrjlpehiijwtreh.supabase.co" -SERVICE_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InFuYXRjaHJqbHBlaGlpand0cmVoIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc3MTY0MDQzNiwiZXhwIjoyMDg3MjE2NDM2fQ.rHoc3NfL59S4lejU4-ArSzox1krQkQG-TnfXb6sslm0" - -HEADERS=(-H "apikey: $SERVICE_KEY" -H "Authorization: Bearer $SERVICE_KEY" -H "Content-Type: application/json") - -function list_tasks() { - local status_filter="${1:-}" - local url="$SUPABASE_URL/rest/v1/tasks?select=*&order=created_at.desc" - if [[ -n "$status_filter" ]]; then - url="$SUPABASE_URL/rest/v1/tasks?select=*&status=eq.$status_filter&order=created_at.desc" - fi - curl -s "$url" "${HEADERS[@]}" | jq '.' -} - -function get_task() { - local task_id="$1" - curl -s "$SUPABASE_URL/rest/v1/tasks?id=eq.$task_id&select=*" "${HEADERS[@]}" | jq '.[0]' -} - -function create_task() { - local title="$1" - local status="${2:-open}" - local priority="${3:-medium}" - local project_id="${4:-1}" - local assignee_id="${5:-9c29cc99-81a1-4e75-8dff-cd7cc5ceb5aa}" - - local uuid=$(uuidgen | tr '[:upper:]' '[:lower:]') - local now=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - - curl -s -X POST "$SUPABASE_URL/rest/v1/tasks" \ - "${HEADERS[@]}" \ - -d "{ - \"id\": \"$uuid\", - \"title\": \"$title\", - \"status\": \"$status\", - \"priority\": \"$priority\", - \"project_id\": \"$project_id\", - \"assignee_id\": \"$assignee_id\", - \"created_at\": \"$now\", - \"updated_at\": \"$now\", - \"comments\": [], - \"tags\": [], - \"attachments\": [] - }" | jq '.' - - echo "Created task: $uuid" -} - -function update_task() { - local task_id="$1" - local field="$2" - local value="$3" - local now=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - - curl -s -X PATCH "$SUPABASE_URL/rest/v1/tasks?id=eq.$task_id" \ - "${HEADERS[@]}" \ - -d "{\"$field\": \"$value\", \"updated_at\": \"$now\"}" | jq '.' - - echo "Updated task $task_id: $field = $value" -} - -function delete_task() { - local task_id="$1" - curl -s -X DELETE "$SUPABASE_URL/rest/v1/tasks?id=eq.$task_id" "${HEADERS[@]}" - echo "Deleted task: $task_id" -} - -# Main -case "$1" in - list) - list_tasks "$2" - ;; - get) - get_task "$2" - ;; - create) - create_task "$2" "$3" "$4" "$5" "$6" - ;; - update) - update_task "$2" "$3" "$4" - ;; - delete) - delete_task "$2" - ;; - *) - echo "Usage: $0 [list|get|create|update|delete] [args...]" - echo "" - echo "Examples:" - echo " $0 list # List all tasks" - echo " $0 list open # List open tasks" - echo " $0 get # Get specific task" - echo " $0 create \"Task title\" open medium # Create task" - echo " $0 update status done # Update task status" - echo " $0 delete # Delete task" - ;; -esac \ No newline at end of file diff --git a/scripts/gantt-task-crud.ts b/scripts/gantt-task-crud.ts deleted file mode 100644 index ccbcc8f..0000000 --- a/scripts/gantt-task-crud.ts +++ /dev/null @@ -1,183 +0,0 @@ -/** - * Gantt Board Task CRUD Utilities - * Full CRUD operations on gantt board tasks via Supabase - */ - -const SUPABASE_URL = "https://qnatchrjlpehiijwtreh.supabase.co"; -const SERVICE_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InFuYXRjaHJqbHBlaGlpand0cmVoIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc3MTY0MDQzNiwiZXhwIjoyMDg3MjE2NDM2fQ.rHoc3NfL59S4lejU4-ArSzox1krQkQG-TnfXb6sslm0"; - -const HEADERS = { - "apikey": SERVICE_KEY, - "Authorization": `Bearer ${SERVICE_KEY}`, - "Content-Type": "application/json", -}; - -/** - * Generate UUID v4 - */ -function generateUUID(): string { - return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => { - const r = (Math.random() * 16) | 0; - const v = c === "x" ? r : (r & 0x3) | 0x8; - return v.toString(16); - }); -} - -/** - * Get current ISO timestamp - */ -function nowISO(): string { - return new Date().toISOString(); -} - -/** - * Task interface - */ -export interface GanttTask { - id: string; - title: string; - description?: string; - status: "open" | "todo" | "blocked" | "in-progress" | "review" | "validate" | "archived" | "canceled" | "done"; - priority: "low" | "medium" | "high" | "urgent"; - type?: "idea" | "task" | "bug" | "research" | "plan"; - project_id: string; - sprint_id?: string; - assignee_id?: string; - created_at: string; - updated_at: string; - due_date?: string; - tags?: string[]; - comments?: unknown[]; - attachments?: unknown[]; -} - -/** - * LIST all tasks (optionally filtered by status) - */ -export async function listTasks(status?: string): Promise { - let url = `${SUPABASE_URL}/rest/v1/tasks?select=*&order=created_at.desc`; - if (status) { - url = `${SUPABASE_URL}/rest/v1/tasks?select=*&status=eq.${status}&order=created_at.desc`; - } - - const res = await fetch(url, { headers: HEADERS }); - if (!res.ok) throw new Error(`Failed to list tasks: ${res.status}`); - return res.json(); -} - -/** - * GET a single task by ID - */ -export async function getTask(taskId: string): Promise { - const url = `${SUPABASE_URL}/rest/v1/tasks?id=eq.${taskId}&select=*`; - const res = await fetch(url, { headers: HEADERS }); - if (!res.ok) throw new Error(`Failed to get task: ${res.status}`); - const tasks = await res.json(); - return tasks[0] || null; -} - -/** - * CREATE a new task - */ -export async function createTask(params: { - title: string; - description?: string; - status?: GanttTask["status"]; - priority?: GanttTask["priority"]; - type?: GanttTask["type"]; - projectId?: string; - assigneeId?: string; - dueDate?: string; - tags?: string[]; -}): Promise { - const task: Partial = { - id: generateUUID(), - title: params.title, - description: params.description, - status: params.status || "open", - priority: params.priority || "medium", - type: params.type || "task", - project_id: params.projectId || "1", - assignee_id: params.assigneeId || "9c29cc99-81a1-4e75-8dff-cd7cc5ceb5aa", // Max - created_at: nowISO(), - updated_at: nowISO(), - due_date: params.dueDate, - tags: params.tags || [], - comments: [], - attachments: [], - }; - - const res = await fetch(`${SUPABASE_URL}/rest/v1/tasks`, { - method: "POST", - headers: HEADERS, - body: JSON.stringify(task), - }); - - if (!res.ok) throw new Error(`Failed to create task: ${res.status}`); - return task as GanttTask; -} - -/** - * UPDATE a task (partial update) - */ -export async function updateTask( - taskId: string, - updates: Partial> -): Promise { - const payload = { - ...updates, - updated_at: nowISO(), - }; - - const res = await fetch(`${SUPABASE_URL}/rest/v1/tasks?id=eq.${taskId}`, { - method: "PATCH", - headers: HEADERS, - body: JSON.stringify(payload), - }); - - if (!res.ok) throw new Error(`Failed to update task: ${res.status}`); -} - -/** - * DELETE a task - */ -export async function deleteTask(taskId: string): Promise { - const res = await fetch(`${SUPABASE_URL}/rest/v1/tasks?id=eq.${taskId}`, { - method: "DELETE", - headers: HEADERS, - }); - - if (!res.ok) throw new Error(`Failed to delete task: ${res.status}`); -} - -/** - * Update task status (convenience method) - */ -export async function updateTaskStatus( - taskId: string, - status: GanttTask["status"] -): Promise { - return updateTask(taskId, { status }); -} - -/** - * Assign task to user (convenience method) - */ -export async function assignTask( - taskId: string, - assigneeId: string -): Promise { - return updateTask(taskId, { assignee_id: assigneeId }); -} - -/** - * Mark task as done (convenience method) - */ -export async function completeTask(taskId: string): Promise { - return updateTask(taskId, { status: "done" }); -} - -// Example usage: -// const task = await createTask({ title: "New feature", priority: "high" }); -// await updateTaskStatus(task.id, "in-progress"); -// await completeTask(task.id); \ No newline at end of file diff --git a/scripts/import-blog-messages.sql b/scripts/import-blog-messages.sql deleted file mode 100644 index 01e3897..0000000 --- a/scripts/import-blog-messages.sql +++ /dev/null @@ -1,302 +0,0 @@ --- Import existing blog messages from JSON --- Run this in Supabase SQL Editor - --- Message 1: Daily Digest - Feb 21, 2026 (full) -INSERT INTO blog_messages (id, date, content, timestamp, tags) VALUES -('1771679017982', '2026-02-21', -$CONTENT$## Daily Digest - Saturday, February 21st, 2026 - -## 🍎 iOS AI Development - -**Xcode 26.3 Unlocks Agentic Coding Directly in Xcode** - -Apple has released Xcode 26.3, which brings coding agents directly into the IDE for iOS developers... - -[Read more β†’](https://www.apple.com/newsroom/2026/02/xcode-26-point-3-unlocks-the-power-of-agentic-coding/) - -**Swift Student Challenge Submissions Now Open** - -Apple's Swift Student Challenge is accepting submissions through February 28, 2026... - -[Read more β†’](https://developer.apple.com/swift-student-challenge/) - -**iOS 26.4 Beta Released** - -The beta versions of iOS 26.4, iPadOS 26.4, macOS 26.4... - -[Read more β†’](https://developer.apple.com/news/?id=xgkk9w83) - -## πŸ€– AI Coding Assistants - -**Cursor Launches Plugin Marketplace and Agent Sandboxing** - -Cursor has introduced two major features: a Plugin Marketplace... - -[Read more β†’](https://cursor.com/blog/marketplace) - -**Stripe Rolls Out Cursor to 3,000 Engineers** - -Stripe has pre-installed Cursor on every developer's machine... - -[Read more β†’](https://cursor.com/blog/stripe) - -**NVIDIA Commits 3x More Code with Cursor Across 30,000 Developers** - -NVIDIA has embedded Cursor across its entire software development lifecycle... - -[Read more β†’](https://cursor.com/blog/nvidia) - -## 🧠 Latest Coding Models Released - -**Claude Sonnet 4.6 Released by Anthropic** - -Anthropic has launched Claude Sonnet 4.6, their latest coding model... - -[Read more β†’](https://www.anthropic.com/news/claude-sonnet-4-6) - -**ggml.ai (llama.cpp) Joins Hugging Face** - -The founding team behind llama.cpp has joined Hugging Face... - -[Read more β†’](https://github.com/ggml-org/llama.cpp/discussions/19759) - -**Anthropic Makes Frontier Cybersecurity Capabilities Available** - -Claude Code now includes enhanced security capabilities... - -[Read more β†’](https://www.anthropic.com/news/claude-code-security) - -## 🦞 OpenClaw Updates - -**Andrej Karpathy Talks About Claws Becoming a Term of Art** - -Former Tesla AI Director Andrej Karpathy has written about Claws... - -[Read more β†’](https://simonwillison.net/2026/Feb/21/claws/) - -**zclaw: Personal AI Assistant in Under 888 KB on ESP32** - -A new ultra-lightweight Claw implementation called zclaw has been released... - -[Read more β†’](https://github.com/tnm/zclaw) - -**spec2commit: Automated Claude Code and Codex Workflow** - -A developer has open-sourced spec2commit, a tool that automates the workflow... - -[Read more β†’](https://github.com/baturyilmaz/spec2commit) - -## πŸ’° Digital Entrepreneurship - -**4Seo.ai: AI That Automatically Improves SEO** - -A new Indie Hackers project called 4Seo.ai promises to automatically improve SEO... - -[Read more β†’](https://www.indiehackers.com/product/4seo-ai) - -**From Five Tools to One: Why They Built CandyDocs** - -The team behind CandyDocs shares their journey of consolidating five separate documentation tools... - -[Read more β†’](https://www.indiehackers.com/product/candydocs) - -**Bazzly: Your SaaS Does Not Need a Marketing Strategyβ€”It Needs a Distribution Habit** - -Bazzly is a new tool that helps SaaS founders focus on building consistent distribution habits... - -[Read more β†’](https://www.indiehackers.com/product/bazzly) - ---- - -*Generated by Daily Digest Bot πŸ€–*$CONTENT$, -1771679017982, -ARRAY['iOS', 'AI', 'Cursor', 'Claude', 'OpenClaw', 'IndieHacking'] -) -ON CONFLICT (id) DO NOTHING; - --- Message 2: Test digest -INSERT INTO blog_messages (id, date, content, timestamp, tags) VALUES -('1771678965239', '2026-02-21', 'Test daily digest', 1771678965239, ARRAY['test']) -ON CONFLICT (id) DO NOTHING; - --- Message 3: Daily Digest - Feb 20, 2026 -INSERT INTO blog_messages (id, date, content, timestamp, tags) VALUES -('1771599387955', '2026-02-20', -$CONTENT$## Daily Digest - February 20, 2026 - -### πŸ€– iOS AI Development - -**Apple Foundation Models Framework Now Available** -Apple has released the Foundation Models framework giving developers direct access to the on-device foundation model... - -**SpeechAnalyzer Brings Advanced On-Device Transcription to iOS** -The all-new SpeechAnalyzer framework enables advanced, on-device transcription capabilities... - -**Core ML Updates for Vision and Document Recognition** -New updates to Core ML and Vision frameworks bring full-document text recognition... - -### πŸ’» AI Coding Assistants - -**Cursor Launches Plugin Marketplace** -Cursor has introduced plugins that package skills, subagents, MCP servers, hooks, and rules into single installs... - -**Stripe Releases Minions - One-Shot End-to-End Coding Agents** -Stripe has published Part 2 of their coding agents series... - -**GitHub Copilot Now Supports Multiple LLMs** -GitHub Copilot now lets developers choose from leading LLMs optimized for speed, accuracy, or cost... - -### 🧠 Latest Coding Models - -**Claude Opus 4.6 Released with Major Coding Improvements** -Anthropic has upgraded their smartest model... - -**Gemini 3.1 Pro Rolls Out with Advanced Reasoning** -Google's new Gemini 3.1 Pro AI model... - -**GGML.ai Joins Hugging Face to Advance Local AI** -GGML.ai, the organization behind llama.cpp, is joining Hugging Face... - -**Taalas Demonstrates Path to 17k tokens/sec Ubiquitous AI** -Taalas has shared research on achieving ubiquitous AI... - -### 🦾 OpenClaw Updates - -**OpenClaw Mentioned in Major Security Report** -A hacker reportedly tricked Cline's Claude-powered workflow into installing OpenClaw... - -### πŸš€ Digital Entrepreneurship - -**Bootstrapping a $20k/mo AI Portfolio After VC-Backed Failure** -An inspiring story of an entrepreneur who built a $20,000/month AI portfolio... - -**Hitting $10k/mo by Using Agency as Testing Ground** -A developer shares how they reached $10,000/month... - -**Bazzly: Your SaaS Needs a Distribution Habit** -A new product launching with the insight that SaaS companies need distribution habits...$CONTENT$, -1771599387955, -ARRAY['iOS', 'AI', 'Cursor', 'Claude', 'OpenClaw'] -) -ON CONFLICT (id) DO NOTHING; - --- Message 4: Retro Digest - Feb 19, 2025 -INSERT INTO blog_messages (id, date, content, timestamp, tags) VALUES -('1771511887581', '2025-02-19', -$CONTENT$# Daily Digest - February 19, 2025 (Retro Edition) - -## πŸ€– iOS + AI Development -- **[Apple Releases iOS 18.3 with Enhanced AI Features](https://developer.apple.com/news)** β€” On-device Siri improvements and CoreML optimizations -- **[Swift 6 Enters Beta](https://swift.org/blog)** β€” Major concurrency improvements for iOS developers -- **[Vision Pro Launches with 600+ Apps](https://apple.com/vision-pro)** β€” Spatial computing officially arrives - -## πŸ§‘β€πŸ’» AI Coding Assistants -- **[Claude 3 Announced by Anthropic](https://anthropic.com/news)** β€” Claude 3 Opus, Sonnet, and Haiku models launched -- **[GitHub Copilot Gets GPT-4 Turbo](https://github.blog)** β€” Faster, more accurate code suggestions -- **[Cursor IDE Gains Traction](https://cursor.com)** β€” AI-native editor starting to challenge VS Code - -## πŸ† Latest Coding Models -- **[GPT-4 Turbo with Vision Released](https://openai.com/blog)** β€” Multimodal capabilities for developers -- **[Gemini 1.5 Pro Debuts](https://deepmind.google)** β€” 1 million token context window -- **[Mistral Large Announced](https://mistral.ai)** β€” European LLM challenging GPT-4 - -## 🦾 OpenClaw Updates -- **[OpenClaw Beta Launched](https://github.com/openclaw/openclaw)** β€” Early access for AI agent framework -- **[Terminal UI Tools Added](https://docs.openclaw.ai)** β€” Command-line interface improvements - -## πŸ’° Digital Entrepreneurship -- **[Indie Hackers $100K Club Growing](https://indiehackers.com)** β€” More solo founders hitting six figures -- **[AI App Revenue Surges](https://sensor-tower.com)** β€” AI-powered apps dominating App Store charts -- **[No-Code Tools Evolution](https://webflow.com/blog)** β€” Webflow, Bubble adding AI features - ---- -*Retro Digest: Looking back at February 2025 | Generated by Max*$CONTENT$, -1771511887581, -ARRAY['retro', 'iOS', 'AI', 'OpenClaw'] -) -ON CONFLICT (id) DO NOTHING; - --- Message 5: Daily Digest - Feb 19, 2026 -INSERT INTO blog_messages (id, date, content, timestamp, tags) VALUES -('1771506266870', '2026-02-19', -$CONTENT$# Daily Digest - February 19, 2026 - -## iOS AI Development -- [iOS 26.4 Beta Released - Get Ready with Latest SDKs](https://developer.apple.com/news/?id=xgkk9w83) -- [Swift Student Challenge 2026 Submissions Now Open](https://developer.apple.com/swift-student-challenge/) -- [Exploring LLMs with MLX on Apple Silicon Macs](https://machinelearning.apple.com/research/exploring-llms-mlx-m5) -- [Updated App Review Guidelines](https://developer.apple.com/news/?id=d75yllv4) - -## AI Coding Assistants -- [Cursor Composer 1.5 - Improved Reasoning](https://cursor.com/blog/composer-1-5) -- [Stripe Rolls Out Cursor to 3,000 Engineers](https://cursor.com/blog/stripe) -- [Cursor Launches Plugin Marketplace](https://cursor.com/blog/marketplace) -- [Cursor Long-Running Agents Now in Web App](https://cursor.com/blog/long-running-agents) -- [Box Chooses Cursor - 85% of Engineers Use Daily](https://cursor.com/blog/box) -- [NVIDIA Commits 3x More Code with Cursor](https://cursor.com/blog/nvidia) -- [Dropbox Uses Cursor to Index 550,000+ Files](https://cursor.com/blog/dropbox) -- [Clankers with Claws - DHH on OpenClaw](https://world.hey.com/dhh/clankers-with-claws-9f86fa71) - -## Latest Coding Models -- [Anthropic Claude Opus 4.6 Released](https://www.anthropic.com/news) -- [Anthropic Raises $30B Series G at $380B Valuation](https://www.anthropic.com/news) -- [SWE-bench February 2026 Leaderboard Update](https://www.swebench.com/) - -## OpenClaw Updates -- [OpenClaw Documentation](https://docs.openclaw.ai/) -- [Clankers with Claws - DHH on OpenClaw AI Agents](https://world.hey.com/dhh/clankers-with-claws-9f86fa71) -- [Omarchy and OpenCode Coming to New York](https://world.hey.com/dhh/omacon-comes-to-new-york-e6ee93cb) - -## Digital Entrepreneurship / Indie Hacking -- [Bootstrapping a $20k/mo AI Portfolio After VC-Backed Company Failed](https://www.indiehackers.com/post/tech/bootstrapping-a-20k-mo-ai-portfolio-after-his-vc-backed-company-failed) -- [Vibe is Product Logic - Injecting Branding into Your AI](https://www.indiehackers.com/post/vibe-is-product-logic-how-to-inject-branding-into-your-ai) -- [Indie Hackers Truth: Distribution is the Bottleneck](https://www.indiehackers.com/product/leadsynthai) -- [Copylio - AI Tool for SEO Ecommerce Product Descriptions](https://www.indiehackers.com/post/show-ih-copylio-an-ai-tool-to-generate-seo-optimized-ecommerce-product-descriptions) -- [Most Founders Have a Timing Problem, Not a Product Problem](https://www.indiehackers.com/product/leadsynthai) - ---- -*Generated by OpenClaw - February 19, 2026*$CONTENT$, -1771506266870, -ARRAY['iOS', 'AI', 'Cursor', 'Claude', 'OpenClaw'] -) -ON CONFLICT (id) DO NOTHING; - --- Message 6: Daily Digest - Feb 18, 2026 -INSERT INTO blog_messages (id, date, content, timestamp, tags) VALUES -('1771435073243', '2026-02-18', -$CONTENT$# Daily Digest - February 18, 2026 - -## πŸ€– iOS + AI Development -- **[Apple Unveils CoreML 7 with On-Device LLM Support](https://developer.apple.com/documentation/coreml)** β€” New 3B parameter models enable local AI assistants without cloud dependency -- **[Swift 6.2 Async/Await Optimization for ML Inference](https://swift.org/blog/2026/swift-6-2-ml)** β€” New concurrency patterns specifically optimized for AI model inference on Apple Silicon -- **[Vision Pro Spatial AI Apps Ecosystem Growing](https://developer.apple.com/visionos/spatial-ai)** β€” Eye-tracking and hand gesture ML models gaining traction - -## πŸ§‘β€πŸ’» AI Coding Assistants -- **[Claude Code Now Supports Swift Package Manager](https://anthropic.com/claude-code-swift)** β€” Direct integration with Xcode projects and SPM workflows -- **[Cursor IDE 0.45 Adds iOS Simulator Integration](https://cursor.com/changelog/ios-simulator)** β€” Preview iOS apps directly in the editor -- **[GitHub Copilot Chat for Xcode Beta Released](https://github.com/features/copilot/xcode)** β€” Natural language code generation for Swift - -## πŸ† Latest Coding Models -- **[Claude 3.5 Sonnet New Coding Benchmark Leader](https://anthropic.com/news/claude-3-5-sonnet-coding)** β€” Outperforms GPT-4o on HumanEval benchmark -- **[DeepSeek Coder V3 Released with 128K Context](https://deepseek.ai/models/coder-v3)** β€” Open source model rivaling commercial alternatives -- **[LLaMA 3.2 70B Fine-Tuned for Mobile Development](https://ai.meta.com/llama/3.2-mobile)** β€” Optimized for on-device code completion - -## 🦾 OpenClaw Updates -- **[OpenClaw 2.0 Released with Canvas Support](https://github.com/openclaw/openclaw/releases/tag/v2.0)** β€” Browser automation and screenshot capabilities added -- **[New Cron System Documentation](https://docs.openclaw.ai/cron)** β€” Schedule recurring tasks with timezone support - -## πŸ’° Digital Entrepreneurship -- **[How One Dev Made $50K/Mo with a Photo AI App](https://indiehackers.com/post/photo-ai-50k)** β€” Breakdown of marketing strategy and tech stack -- **[SaaS Starter Kits for iOS Developers](https://github.com/awesome-ios-saas/starter-kits)** β€” Curated list of monetizable app templates -- **[App Store AI Apps Revenue Report Q4 2025](https://sensor-tower.com/blog/ai-apps-revenue-2025)** β€” Photo enhancement and voice apps dominating charts - ---- -*Generated by AI Agent | All links verified and clickable*$CONTENT$, -1771435073243, -ARRAY['iOS', 'AI', 'Cursor', 'Claude', 'OpenClaw'] -) -ON CONFLICT (id) DO NOTHING; - --- Verify count -SELECT COUNT(*) as total_imported FROM blog_messages; -SELECT id, date, array_length(tags, 1) as tag_count FROM blog_messages ORDER BY timestamp DESC; diff --git a/scripts/insert-messages.sql b/scripts/insert-messages.sql deleted file mode 100644 index d9616d0..0000000 --- a/scripts/insert-messages.sql +++ /dev/null @@ -1,12 +0,0 @@ --- Insert existing blog messages from JSON -INSERT INTO blog_messages (id, date, content, timestamp) VALUES -('1771679017982', '2026-02-21', '## Daily Digest - Saturday, February 21st, 2026\n\n## 🍎 iOS AI Development...', 1771679017982), -('1771678965239', '2026-02-21', 'Test daily digest', 1771678965239), -('1771599387955', '2026-02-20', '## Daily Digest - February 20, 2026...', 1771599387955), -('1771511887581', '2025-02-19', '# Daily Digest - February 19, 2025 (Retro Edition)...', 1771511887581), -('1771506266870', '2026-02-19', '# Daily Digest - February 19, 2026...', 1771506266870), -('1771435073243', '2026-02-18', '# Daily Digest - February 18, 2026...', 1771435073243) -ON CONFLICT (id) DO NOTHING; - --- Verify -SELECT COUNT(*) as total_messages FROM blog_messages; diff --git a/scripts/migrate-blog-backup.js b/scripts/migrate-blog-backup.js deleted file mode 100644 index 4345f24..0000000 --- a/scripts/migrate-blog-backup.js +++ /dev/null @@ -1,38 +0,0 @@ -// Migrate blog-backup data from JSON to Supabase -const { createClient } = require('@supabase/supabase-js'); - -const SUPABASE_URL = 'https://qnatchrjlpehiijwtreh.supabase.co'; -const SUPABASE_SERVICE_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InFuYXRjaHJqbHBlaGlpand0cmVoIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc3MTY0MDQzNiwiZXhwIjoyMDg3MjE2NDM2fQ.s8NJDL3iRXpjiFkqVdEf5QxQN41IJ8D3qRGKJWq6MKk'; - -const supabase = createClient(SUPABASE_URL, SUPABASE_SERVICE_KEY); - -const messages = require('/Users/mattbruce/Documents/Projects/OpenClaw/Web/blog-backup/data/messages.json'); - -async function migrate() { - console.log(`Migrating ${messages.length} messages...`); - - const { error } = await supabase - .from('blog_messages') - .upsert(messages.map(m => ({ - id: m.id, - date: m.date, - content: m.content, - timestamp: m.timestamp - })), { onConflict: 'id' }); - - if (error) { - console.error('Migration failed:', error); - process.exit(1); - } - - console.log('Migration complete!'); - - // Verify - const { data, error: countError } = await supabase - .from('blog_messages') - .select('*', { count: 'exact' }); - - console.log(`Total messages in Supabase: ${data?.length || 0}`); -} - -migrate(); diff --git a/scripts/monitor-web-apps.sh b/scripts/monitor-web-apps.sh deleted file mode 100755 index 1c2d565..0000000 --- a/scripts/monitor-web-apps.sh +++ /dev/null @@ -1,122 +0,0 @@ -#!/bin/bash - -# Web App Monitor - Auto Restart Script -# Ports: 3000 (gantt-board), 3003 (blog-backup), 3005 (heartbeat-monitor) - -PORTS=(3000 3003 3005) -PROJECTS=( - "/Users/mattbruce/Documents/Projects/OpenClaw/Web/gantt-board" - "/Users/mattbruce/Documents/Projects/OpenClaw/Web/blog-backup" - "/Users/mattbruce/Documents/Projects/OpenClaw/Web/heartbeat-monitor" -) -NAMES=("gantt-board" "blog-backup" "heartbeat-monitor") - -RESTARTED=() -LOG="" - -log() { - LOG+="$1\n" - echo -e "$1" -} - -check_port() { - local port=$1 - local timeout=5 - local response - response=$(curl -s -o /dev/null -w "%{http_code}" --max-time $timeout "http://localhost:$port" 2>/dev/null) - echo "$response" -} - -restart_app() { - local port=$1 - local project=$2 - local name=$3 - - log " ↻ Restarting $name on port $port..." - - # Kill any process using the port - pkill -f ":$port" 2>/dev/null - sleep 2 - - # Start the app in background - cd "$project" && npm run dev -- --port $port > /dev/null 2>&1 & - - RESTARTED+=("$name (port $port)") -} - -log "═══════════════════════════════════════════════════" -log "🌐 Web App Monitor - $(date)" -log "═══════════════════════════════════════════════════" -log "" - -# Check all ports -for i in "${!PORTS[@]}"; do - PORT="${PORTS[$i]}" - PROJECT="${PROJECTS[$i]}" - NAME="${NAMES[$i]}" - - log "πŸ“‘ Checking $NAME on port $PORT..." - - STATUS=$(check_port $PORT) - - if [ "$STATUS" = "200" ]; then - log " βœ… Healthy (HTTP $STATUS)" - else - if [ -z "$STATUS" ]; then - log " ❌ No response (timeout/connection refused)" - else - log " ❌ Unhealthy (HTTP $STATUS)" - fi - restart_app $PORT "$PROJECT" "$NAME" - fi - log "" -done - -# Wait for restarts to come up -if [ ${#RESTARTED[@]} -gt 0 ]; then - log "⏳ Waiting 5 seconds for restarts to initialize..." - sleep 5 - log "" -fi - -# Final verification -log "═══════════════════════════════════════════════════" -log "πŸ“Š Final Verification" -log "═══════════════════════════════════════════════════" - -ALL_HEALTHY=true -for i in "${!PORTS[@]}"; do - PORT="${PORTS[$i]}" - NAME="${NAMES[$i]}" - - STATUS=$(check_port $PORT) - if [ "$STATUS" = "200" ]; then - log " βœ… $NAME (port $PORT): HTTP 200" - else - log " ❌ $NAME (port $PORT): Failed (HTTP ${STATUS:-'no response'})" - ALL_HEALTHY=false - fi -done - -log "" -log "═══════════════════════════════════════════════════" -log "πŸ“‹ Summary" -log "═══════════════════════════════════════════════════" - -if [ ${#RESTARTED[@]} -eq 0 ]; then - log " πŸ“ All apps were already running and healthy" -else - log " πŸ”„ Restarted apps:" - for app in "${RESTARTED[@]}"; do - log " β€’ $app" - done -fi - -log "" -if [ "$ALL_HEALTHY" = true ]; then - log " 🎯 Final Status: All apps responding with HTTP 200" -else - log " ⚠️ Final Status: Some apps are not responding" -fi -log "" -log "═══════════════════════════════════════════════════" diff --git a/scripts/monitor_web_apps.sh b/scripts/monitor_web_apps.sh deleted file mode 100755 index f206022..0000000 --- a/scripts/monitor_web_apps.sh +++ /dev/null @@ -1,93 +0,0 @@ -#!/bin/bash - -LOG_FILE="/Users/mattbruce/.openclaw/workspace/logs/app_monitor.log" -TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S') - -echo "[$TIMESTAMP] === Starting Web App Monitor ===" | tee -a "$LOG_FILE" - -# Port to project mapping (arrays for bash 3 compatibility) -PORTS=(3000 3003 3005) -PATHS=( - "/Users/mattbruce/Documents/Projects/OpenClaw/Web/gantt-board" - "/Users/mattbruce/Documents/Projects/OpenClaw/Web/blog-backup" - "/Users/mattbruce/Documents/Projects/OpenClaw/Web/heartbeat-monitor" -) - -# Track which needed restart -NEEDS_RESTART=() - -# Function to check if port is responding -check_port() { - local port=$1 - local url="http://localhost:$port" - - # Use curl with timeout and follow redirects - response=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "$url" 2>/dev/null) - - if [ "$response" == "200" ]; then - echo "[$TIMESTAMP] βœ“ Port $port - HTTP 200 OK" | tee -a "$LOG_FILE" - return 0 - else - echo "[$TIMESTAMP] βœ— Port $port - DOWN (response: $response)" | tee -a "$LOG_FILE" - return 1 - fi -} - -# Function to kill process on port -kill_port() { - local port=$1 - echo "[$TIMESTAMP] β†’ Killing process on port $port..." | tee -a "$LOG_FILE" - - # Find and kill process using the port - pids=$(lsof -ti:$port 2>/dev/null) - if [ -n "$pids" ]; then - echo "[$TIMESTAMP] β†’ Found PIDs: $pids" | tee -a "$LOG_FILE" - kill -9 $pids 2>/dev/null - sleep 2 - echo "[$TIMESTAMP] β†’ Killed processes on port $port" | tee -a "$LOG_FILE" - else - echo "[$TIMESTAMP] β†’ No process found on port $port" | tee -a "$LOG_FILE" - fi -} - -# Function to restart app -restart_app() { - local port=$1 - local project_path=$2 - - echo "[$TIMESTAMP] β†’ Restarting app on port $port..." | tee -a "$LOG_FILE" - echo "[$TIMESTAMP] β†’ Path: $project_path" | tee -a "$LOG_FILE" - - cd "$project_path" && nohup npm run dev -- --port $port > /dev/null 2>&1 & - - NEEDS_RESTART+=("$port") -} - -# Check all ports and restart if needed -for i in "${!PORTS[@]}"; do - port="${PORTS[$i]}" - path="${PATHS[$i]}" - - if ! check_port $port; then - kill_port $port - restart_app $port "$path" - fi -done - -# If any were restarted, wait and verify -if [ ${#NEEDS_RESTART[@]} -gt 0 ]; then - echo "[$TIMESTAMP] Waiting 5 seconds for apps to start..." | tee -a "$LOG_FILE" - sleep 5 - - echo "[$TIMESTAMP] === Post-Restart Verification ===" | tee -a "$LOG_FILE" - for port in "${PORTS[@]}"; do - if ! check_port $port; then - echo "[$TIMESTAMP] ⚠ Port $port still not responding after restart" | tee -a "$LOG_FILE" - fi - done -else - echo "[$TIMESTAMP] All apps healthy, no restart needed" | tee -a "$LOG_FILE" -fi - -echo "[$TIMESTAMP] === Monitor Complete ===" | tee -a "$LOG_FILE" -echo "" | tee -a "$LOG_FILE" diff --git a/scripts/resource-monitor.sh b/scripts/resource-monitor.sh new file mode 100755 index 0000000..37b5bae --- /dev/null +++ b/scripts/resource-monitor.sh @@ -0,0 +1,80 @@ +#!/bin/zsh + +# Process monitoring script to track what kills Next.js dev servers +# Run this in background to capture system events + +LOG_FILE="/Users/mattbruce/.openclaw/workspace/logs/process-monitor.log" +PID_FILE="/tmp/process-monitor.pid" + +# Create log directory +mkdir -p "$(dirname $LOG_FILE)" + +# Function to log with timestamp +log() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a $LOG_FILE +} + +# Track Node processes +monitor_processes() { + while true; do + # Check if our monitored processes are running + for port in 3000 3003 3005; do + PID=$(lsof -ti:$port 2>/dev/null) + if [ -n "$PID" ]; then + # Get process info + CPU=$(ps -p $PID -o %cpu= 2>/dev/null | tr -d ' ') + MEM=$(ps -p $PID -o %mem= 2>/dev/null | tr -d ' ') + RSS=$(ps -p $PID -o rss= 2>/dev/null | tr -d ' ') + + # Log if memory is high (>500MB RSS) + if [ -n "$RSS" ] && [ "$RSS" -gt 512000 ]; then + log "WARNING: Port $port (PID:$PID) using ${RSS}KB RAM (${MEM}% of system)" + fi + + # Log if CPU is high (>80%) + if [ -n "$CPU" ] && [ "${CPU%.*}" -gt 80 ]; then + log "WARNING: Port $port (PID:$PID) using ${CPU}% CPU" + fi + fi + done + + # Check system memory + FREE_MEM=$(vm_stat | grep "Pages free" | awk '{print $3}' | tr -d '.') + if [ -n "$FREE_MEM" ]; then + FREE_MB=$((FREE_MEM * 4096 / 1024 / 1024)) + if [ "$FREE_MB" -lt 500 ]; then + log "WARNING: Low system memory: ${FREE_MB}MB free" + fi + fi + + # Check file descriptors + for port in 3000 3003 3005; do + PID=$(lsof -ti:$port 2>/dev/null) + if [ -n "$PID" ]; then + FD_COUNT=$(lsof -p $PID 2>/dev/null | wc -l) + if [ "$FD_COUNT" -gt 900 ]; then + log "WARNING: Port $port (PID:$PID) has $FD_COUNT open file descriptors" + fi + fi + done + + sleep 60 + done +} + +# Check if already running +if [ -f "$PID_FILE" ] && kill -0 $(cat $PID_FILE) 2>/dev/null; then + echo "Monitor already running (PID: $(cat $PID_FILE))" + exit 0 +fi + +# Save PID +echo $$ > $PID_FILE + +log "=== Process Monitor Started ===" +log "Monitoring ports: 3000, 3003, 3005" +log "Checking: CPU, Memory, File Descriptors, System Resources" +log "Log file: $LOG_FILE" + +# Start monitoring +monitor_processes & diff --git a/scripts/update-tags.sql b/scripts/update-tags.sql deleted file mode 100644 index a388639..0000000 --- a/scripts/update-tags.sql +++ /dev/null @@ -1,10 +0,0 @@ --- Update tags for existing blog messages -UPDATE blog_messages SET tags = ARRAY['iOS', 'AI', 'Cursor', 'Claude', 'OpenClaw', 'IndieHacking'] WHERE id = '1771679017982'; -UPDATE blog_messages SET tags = ARRAY['test'] WHERE id = '1771678965239'; -UPDATE blog_messages SET tags = ARRAY['iOS', 'AI', 'Cursor', 'Claude', 'OpenClaw'] WHERE id = '1771599387955'; -UPDATE blog_messages SET tags = ARRAY['retro', 'iOS', 'AI', 'OpenClaw'] WHERE id = '1771511887581'; -UPDATE blog_messages SET tags = ARRAY['iOS', 'AI', 'Cursor', 'Claude', 'OpenClaw'] WHERE id = '1771506266870'; -UPDATE blog_messages SET tags = ARRAY['iOS', 'AI', 'Cursor', 'Claude', 'OpenClaw'] WHERE id = '1771435073243'; - --- Verify -SELECT id, date, tags FROM blog_messages ORDER BY timestamp DESC; diff --git a/scripts/webapp-monitor.sh b/scripts/webapp-monitor.sh deleted file mode 100755 index 8a1216f..0000000 --- a/scripts/webapp-monitor.sh +++ /dev/null @@ -1,108 +0,0 @@ -#!/bin/bash -LOG_FILE="/Users/mattbruce/.openclaw/workspace/logs/webapp-monitor.log" -mkdir -p "$(dirname "$LOG_FILE")" - -# Function to get app name for port -get_app_name() { - case $1 in - 3000) echo "gantt-board" ;; - 3003) echo "blog-backup" ;; - 3005) echo "heartbeat-monitor" ;; - *) echo "unknown" ;; - esac -} - -# Function to get app directory for port -get_app_dir() { - case $1 in - 3000) echo "/Users/mattbruce/Documents/Projects/OpenClaw/Web/gantt-board" ;; - 3003) echo "/Users/mattbruce/Documents/Projects/OpenClaw/Web/blog-backup" ;; - 3005) echo "/Users/mattbruce/Documents/Projects/OpenClaw/Web/heartbeat-monitor" ;; - *) echo "" ;; - esac -} - -# Function to check if port is responding with HTTP 200 -check_port() { - local port=$1 - local response - response=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "http://localhost:$port" 2>/dev/null) - if [ "$response" = "200" ]; then - echo "up" - else - echo "down" - fi -} - -# Function to kill process on port -kill_port() { - local port=$1 - pkill -f "--port $port" 2>/dev/null - pkill -f ":$port" 2>/dev/null -} - -# Function to restart app -restart_app() { - local port=$1 - local dir=$2 - cd "$dir" && nohup npm run dev -- --port $port > /dev/null 2>&1 & -} - -echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting web app monitor check" >> "$LOG_FILE" - -restarted_any=false - -# Check each app -for port in 3000 3003 3005; do - status=$(check_port $port) - app_name=$(get_app_name $port) - - if [ "$status" = "down" ]; then - echo "[$(date '+%Y-%m-%d %H:%M:%S')] $app_name (port $port) is DOWN - restarting..." >> "$LOG_FILE" - kill_port $port - restarted_any=true - else - echo "[$(date '+%Y-%m-%d %H:%M:%S')] $app_name (port $port) is UP" >> "$LOG_FILE" - fi -done - -# Wait 2 seconds after kills, then restart down apps -if [ "$restarted_any" = true ]; then - sleep 2 - for port in 3000 3003 3005; do - status=$(check_port $port) - if [ "$status" = "down" ]; then - app_name=$(get_app_name $port) - app_dir=$(get_app_dir $port) - echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting $app_name on port $port..." >> "$LOG_FILE" - restart_app $port "$app_dir" - fi - done - - # Wait 5 seconds for apps to start - echo "[$(date '+%Y-%m-%d %H:%M:%S')] Waiting 5 seconds for apps to start..." >> "$LOG_FILE" - sleep 5 -fi - -# Final verification -echo "[$(date '+%Y-%m-%d %H:%M:%S')] Final verification:" >> "$LOG_FILE" -all_up=true -for port in 3000 3003 3005; do - status=$(check_port $port) - app_name=$(get_app_name $port) - if [ "$status" = "up" ]; then - echo "[$(date '+%Y-%m-%d %H:%M:%S')] βœ“ $app_name (port $port) - UP" >> "$LOG_FILE" - else - echo "[$(date '+%Y-%m-%d %H:%M:%S')] βœ— $app_name (port $port) - STILL DOWN" >> "$LOG_FILE" - all_up=false - fi -done - -if [ "$all_up" = true ]; then - echo "[$(date '+%Y-%m-%d %H:%M:%S')] All web apps are running" >> "$LOG_FILE" -else - echo "[$(date '+%Y-%m-%d %H:%M:%S')] WARNING: Some web apps failed to start" >> "$LOG_FILE" -fi - -echo "[$(date '+%Y-%m-%d %H:%M:%S')] Monitor check complete" >> "$LOG_FILE" -echo "---" >> "$LOG_FILE"