optimal-cli 1.0.0 → 1.1.0

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 (85) hide show
  1. package/.claude-plugin/marketplace.json +18 -0
  2. package/.claude-plugin/plugin.json +10 -0
  3. package/.env.example +17 -0
  4. package/CLAUDE.md +67 -0
  5. package/COMMANDS.md +264 -0
  6. package/PUBLISH.md +70 -0
  7. package/agents/content-ops.md +2 -2
  8. package/agents/financial-ops.md +2 -2
  9. package/agents/infra-ops.md +2 -2
  10. package/apps/.gitkeep +0 -0
  11. package/bin/optimal.ts +278 -591
  12. package/docs/MIGRATION_NEEDED.md +37 -0
  13. package/docs/plans/.gitkeep +0 -0
  14. package/docs/plans/optimal-cli-config-registry-v1.md +71 -0
  15. package/hooks/.gitkeep +0 -0
  16. package/lib/config/registry.ts +5 -4
  17. package/lib/kanban-obsidian.ts +232 -0
  18. package/lib/kanban-sync.ts +258 -0
  19. package/lib/kanban.ts +239 -0
  20. package/lib/obsidian-tasks.ts +231 -0
  21. package/package.json +5 -19
  22. package/pnpm-workspace.yaml +3 -0
  23. package/scripts/check-table.ts +24 -0
  24. package/scripts/create-tables.ts +94 -0
  25. package/scripts/migrate-kanban.sh +28 -0
  26. package/scripts/migrate-v2.ts +78 -0
  27. package/scripts/migrate.ts +79 -0
  28. package/scripts/run-migration.ts +59 -0
  29. package/scripts/seed-board.ts +203 -0
  30. package/scripts/test-kanban.ts +21 -0
  31. package/skills/audit-financials/SKILL.md +33 -0
  32. package/skills/board-create/SKILL.md +28 -0
  33. package/skills/board-update/SKILL.md +27 -0
  34. package/skills/board-view/SKILL.md +27 -0
  35. package/skills/delete-batch/SKILL.md +77 -0
  36. package/skills/deploy/SKILL.md +40 -0
  37. package/skills/diagnose-months/SKILL.md +68 -0
  38. package/skills/distribute-newsletter/SKILL.md +58 -0
  39. package/skills/export-budget/SKILL.md +44 -0
  40. package/skills/export-kpis/SKILL.md +52 -0
  41. package/skills/generate-netsuite-template/SKILL.md +51 -0
  42. package/skills/generate-newsletter/SKILL.md +53 -0
  43. package/skills/generate-newsletter-insurance/SKILL.md +59 -0
  44. package/skills/generate-social-posts/SKILL.md +67 -0
  45. package/skills/health-check/SKILL.md +42 -0
  46. package/skills/ingest-transactions/SKILL.md +51 -0
  47. package/skills/manage-cms/SKILL.md +50 -0
  48. package/skills/manage-scenarios/SKILL.md +83 -0
  49. package/skills/migrate-db/SKILL.md +79 -0
  50. package/skills/preview-newsletter/SKILL.md +50 -0
  51. package/skills/project-budget/SKILL.md +60 -0
  52. package/skills/publish-blog/SKILL.md +70 -0
  53. package/skills/publish-social-posts/SKILL.md +70 -0
  54. package/skills/rate-anomalies/SKILL.md +62 -0
  55. package/skills/scrape-ads/SKILL.md +49 -0
  56. package/skills/stamp-transactions/SKILL.md +62 -0
  57. package/skills/upload-income-statements/SKILL.md +54 -0
  58. package/skills/upload-netsuite/SKILL.md +56 -0
  59. package/skills/upload-r1/SKILL.md +45 -0
  60. package/supabase/.temp/cli-latest +1 -0
  61. package/supabase/migrations/.gitkeep +0 -0
  62. package/supabase/migrations/20250305000001_create_agent_configs.sql +36 -0
  63. package/supabase/migrations/20260305111300_create_cli_config_registry.sql +22 -0
  64. package/supabase/migrations/20260306195000_create_kanban_tables.sql +97 -0
  65. package/tests/config-command-smoke.test.ts +395 -0
  66. package/tests/config-registry.test.ts +173 -0
  67. package/tsconfig.json +19 -0
  68. package/agents/profiles.json +0 -5
  69. package/docs/CLI-REFERENCE.md +0 -361
  70. package/lib/assets/index.ts +0 -225
  71. package/lib/assets.ts +0 -124
  72. package/lib/auth/index.ts +0 -189
  73. package/lib/board/index.ts +0 -309
  74. package/lib/board/types.ts +0 -124
  75. package/lib/bot/claim.ts +0 -43
  76. package/lib/bot/coordinator.ts +0 -254
  77. package/lib/bot/heartbeat.ts +0 -37
  78. package/lib/bot/index.ts +0 -9
  79. package/lib/bot/protocol.ts +0 -99
  80. package/lib/bot/reporter.ts +0 -42
  81. package/lib/bot/skills.ts +0 -81
  82. package/lib/errors.ts +0 -129
  83. package/lib/format.ts +0 -120
  84. package/lib/returnpro/validate.ts +0 -154
  85. package/lib/social/meta.ts +0 -228
@@ -0,0 +1,83 @@
1
+ ---
2
+ name: manage-scenarios
3
+ description: Save, load, compare, and delete named budget scenarios for Wes projections
4
+ ---
5
+
6
+ ## Purpose
7
+ Manages named budget scenarios for the Wes dashboard budget projection system. Scenarios are snapshots of `fpa_yield_assumptions` data (unit forecasts, WIP units, yield rates) that can be saved, loaded, compared side-by-side, and shared across users. This enables what-if analysis — e.g., "optimistic Q2" vs "conservative Q2" scenarios.
8
+
9
+ ## Inputs
10
+ - **action** (required): One of `save`, `load`, `list`, `compare`, `delete`.
11
+ - **name** (required for save/load/delete): Scenario name (e.g., `"optimistic-q2-2026"`, `"baseline-fy25"`).
12
+ - **fiscal-year** (optional): Fiscal year filter. Default: current fiscal year.
13
+ - **user-id** (optional): User UUID for scenario ownership. Default: Carlos's user ID.
14
+ - **compare-with** (required for compare): Second scenario name to compare against.
15
+ - **format** (optional): Output format for compare/list — `table` (default) or `csv`.
16
+
17
+ ## Steps
18
+ 1. Call `lib/budget/scenarios.ts::manageScenarios(action, options)` to orchestrate
19
+ 2. **save**: Snapshot current `fpa_yield_assumptions` rows (filtered by user + fiscal year) into a named scenario record, stored as JSON blob with metadata (name, created_at, user_id, row_count)
20
+ 3. **load**: Restore a saved scenario by overwriting `fpa_yield_assumptions` rows for the target user + fiscal year with the snapshot data. Creates a backup of current data first.
21
+ 4. **list**: Show all saved scenarios with name, created_at, fiscal_year, row_count, user
22
+ 5. **compare**: Load two scenarios side-by-side and compute deltas — total units, revenue projection, per-program differences
23
+ 6. **delete**: Remove a named scenario (with confirmation)
24
+ 7. Log execution via `lib/kanban.ts::logSkillExecution()`
25
+
26
+ ## Output
27
+ **list**:
28
+ ```
29
+ | Name | FY | Created | Rows | User |
30
+ |------|----|---------|------|------|
31
+ | baseline-fy25 | FY25 | 2026-01-15 | 1,264 | Carlos |
32
+ | optimistic-q2 | FY26 | 2026-02-20 | 1,310 | Carlos |
33
+ ```
34
+
35
+ **compare**:
36
+ ```
37
+ Comparing: baseline-fy25 vs optimistic-q2
38
+
39
+ | Program | Baseline Units | Optimistic Units | Delta | Delta % |
40
+ |---------|---------------|-----------------|-------|---------|
41
+ | BRTON-WM | 42,000 | 48,500 | +6,500 | ↑15.5% |
42
+ | FORTX-POOL | 18,200 | 16,800 | -1,400 | ↓-7.7% |
43
+ | Total | 7,352,022 | 7,891,450 | +539,428 | ↑7.3% |
44
+ ```
45
+
46
+ **save**: `Saved scenario "optimistic-q2" (1,310 rows, FY26)`
47
+
48
+ **load**: `Loaded scenario "baseline-fy25" → overwrote 1,264 rows (backup: auto-backup-20260301T100000)`
49
+
50
+ ## CLI Usage
51
+ ```bash
52
+ # Save current assumptions as a named scenario
53
+ optimal manage-scenarios save --name optimistic-q2 --fiscal-year FY26
54
+
55
+ # Load a saved scenario
56
+ optimal manage-scenarios load --name baseline-fy25
57
+
58
+ # List all scenarios
59
+ optimal manage-scenarios list
60
+
61
+ # Compare two scenarios
62
+ optimal manage-scenarios compare --name baseline-fy25 --compare-with optimistic-q2
63
+
64
+ # Delete a scenario
65
+ optimal manage-scenarios delete --name old-test-scenario
66
+ ```
67
+
68
+ ## Environment
69
+ Requires: `RETURNPRO_SUPABASE_URL`, `RETURNPRO_SUPABASE_SERVICE_KEY`
70
+
71
+ ## Tables Touched
72
+ - `fpa_yield_assumptions` — read/write unit forecasts and yield data. Unique key: `(user_id, fiscal_year, month, master_program_id)`
73
+ - `wes_imports` — baseline reference for sync validation
74
+ - Scenario storage: TBD — either a new `budget_scenarios` table or JSON blobs in an existing metadata table
75
+
76
+ ## Gotchas
77
+ - **fpa_yield_assumptions refactor**: Table was refactored Feb 2026 — dropped 3 WIP% columns, added `wip_units INTEGER`. Unique key is `(user_id, fiscal_year, month, master_program_id)`.
78
+ - **Wes sync pattern**: When baselines diverge, copy from Carlos to all other users. FY25 baseline: 7,352,022 units, 1,264 rows, 97 masters.
79
+ - **Load creates backup**: Loading a scenario automatically saves the current state as `auto-backup-{timestamp}` before overwriting.
80
+ - **User isolation**: Scenarios are per-user by default. Cross-user scenario sharing requires explicit user-id parameter.
81
+
82
+ ## Status
83
+ Implementation status: Not yet implemented. Spec only. Lib function `lib/budget/scenarios.ts` to be built on top of existing `lib/budget/projections.ts` in wes-dashboard.
@@ -0,0 +1,79 @@
1
+ ---
2
+ name: migrate-db
3
+ description: Run Supabase database migrations across ReturnPro or OptimalOS instances
4
+ ---
5
+
6
+ ## Purpose
7
+ Executes pending database migrations against the linked Supabase instance using `supabase db push --linked`. This is the only sanctioned way to modify database schemas — Carlos never runs SQL manually. Supports both the ReturnPro instance (financial data) and the OptimalOS instance (kanban, transactions).
8
+
9
+ ## Inputs
10
+ - **instance** (required): Target Supabase instance — `returnpro` or `optimalos`.
11
+ - **migration** (optional): Specific migration file name to create before pushing (e.g., `add-budget-scenarios-table`). If provided, creates the file in `supabase/migrations/` first.
12
+ - **sql** (optional): SQL content for a new migration file. Required if `--migration` is specified.
13
+ - **dry-run** (optional): Show pending migrations without applying them.
14
+
15
+ ## Steps
16
+ 1. Call `lib/infra/migrate.ts::migrateDb(instance, options?)` to orchestrate
17
+ 2. **Resolve instance** — map `returnpro` to `/home/optimal/dashboard-returnpro` supabase config, `optimalos` to `/home/optimal/optimalos` supabase config (or the consolidated `/home/optimal/optimal-cli/supabase` config)
18
+ 3. **Create migration file** (if `--migration` + `--sql` provided):
19
+ - Generate timestamped filename: `{YYYYMMDDHHmmss}_{migration-name}.sql`
20
+ - Write SQL content to `supabase/migrations/{filename}`
21
+ 4. **List pending** — show which migrations haven't been applied yet
22
+ 5. **Push** — run `supabase db push --linked` from the appropriate project directory
23
+ 6. **Verify** — confirm migration was applied successfully
24
+ 7. Log execution via `lib/kanban.ts::logSkillExecution()`
25
+
26
+ ## Output
27
+ ```
28
+ Instance: returnpro
29
+ Project dir: /home/optimal/dashboard-returnpro
30
+ Pending migrations: 1
31
+
32
+ Applying: 20260301100000_add-budget-scenarios-table.sql
33
+ Migration applied successfully.
34
+
35
+ Current schema version: 20260301100000
36
+ ```
37
+
38
+ Dry-run:
39
+ ```
40
+ [DRY RUN] Instance: returnpro
41
+ Pending migrations:
42
+ 1. 20260301100000_add-budget-scenarios-table.sql (new)
43
+ Would run: supabase db push --linked
44
+ ```
45
+
46
+ ## CLI Usage
47
+ ```bash
48
+ # Push pending migrations to ReturnPro
49
+ optimal migrate-db --instance returnpro
50
+
51
+ # Push to OptimalOS
52
+ optimal migrate-db --instance optimalos
53
+
54
+ # Create a new migration and push
55
+ optimal migrate-db --instance returnpro --migration add-budget-scenarios --sql "CREATE TABLE budget_scenarios (...);"
56
+
57
+ # Dry run
58
+ optimal migrate-db --instance returnpro --dry-run
59
+ ```
60
+
61
+ ## Environment
62
+ Requires: `supabase` CLI installed (Homebrew, v2.72.7+). Authentication is handled internally by `supabase db push --linked`.
63
+
64
+ ## Supabase Instances
65
+
66
+ | Instance | Supabase URL | Project Directory |
67
+ |----------|-------------|-------------------|
68
+ | returnpro | vvutttwunexshxkmygik.supabase.co | /home/optimal/dashboard-returnpro |
69
+ | optimalos | hbfalrpswysryltysonm.supabase.co | /home/optimal/optimalos |
70
+
71
+ ## Gotchas
72
+ - **Never run SQL manually**: Always use migration files + `supabase db push --linked`. This is a hard rule.
73
+ - **No `supabase db execute`**: The `db execute` subcommand does not exist in Supabase CLI v2.72.7. Use migration files instead.
74
+ - **No psql directly**: The pooler has permission issues and direct IPv6 is unreachable. `db push --linked` handles auth internally.
75
+ - **stg_financials_raw.amount is TEXT**: If a migration touches this column, remember it stores amounts as TEXT strings, not NUMERIC.
76
+ - **Reserved fields in Strapi**: If migrating Strapi's underlying Postgres, avoid `status`, `published_at`, `locale`, `meta` as column names.
77
+
78
+ ## Status
79
+ Implementation status: Not yet implemented. Spec only. Lib function `lib/infra/migrate.ts` to be built as a cross-repo migration runner.
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: preview-newsletter
3
+ description: Deploy the newsletter preview site to Vercel for client review
4
+ ---
5
+
6
+ ## Purpose
7
+ Deploys the newsletter-preview Next.js site to Vercel so clients and stakeholders can review newsletters and social posts in a branded web interface. This is a thin wrapper around the `deploy` skill, pre-configured for the `newsletter-preview` app. The preview site renders published Strapi content at `https://newsletter.op-hub.com`.
8
+
9
+ ## Inputs
10
+ - **prod** (optional): Deploy to production. Default: preview deployment.
11
+
12
+ ## Steps
13
+ 1. Call `lib/infra/deploy.ts::deploy('newsletter-preview', options?)` to deploy
14
+ 2. Resolves to path `/home/optimal/projects/newsletter-preview`
15
+ 3. Runs `vercel --cwd /home/optimal/projects/newsletter-preview` (or `vercel --prod` for production)
16
+ 4. Waits for deployment to complete (up to 2 minutes)
17
+ 5. Returns the deployment URL
18
+ 6. Log execution via `lib/kanban.ts::logSkillExecution()`
19
+
20
+ ## Output
21
+ ```
22
+ Deploying newsletter-preview...
23
+ Preview URL: https://newsletter-preview-abc123.vercel.app
24
+ ```
25
+
26
+ Or for production:
27
+ ```
28
+ Deploying newsletter-preview to production...
29
+ Production URL: https://newsletter.op-hub.com
30
+ ```
31
+
32
+ ## CLI Usage
33
+ ```bash
34
+ # Preview deployment
35
+ optimal preview-newsletter
36
+
37
+ # Production deployment
38
+ optimal preview-newsletter --prod
39
+ ```
40
+
41
+ ## Environment
42
+ Requires: `vercel` CLI installed globally and authenticated.
43
+
44
+ ## Gotchas
45
+ - **This is just a deploy**: No content generation happens here. Use `generate-newsletter` or `generate-newsletter-insurance` first to create content, then deploy the preview.
46
+ - **Strapi must be reachable**: The preview site fetches content from Strapi at build time and runtime. Strapi must be running at `https://strapi.op-hub.com`.
47
+ - **Brand routes**: CRE-11TRUST content at `/cre-11trust`, LIFEINSUR content at `/lifeinsur`.
48
+
49
+ ## Status
50
+ Implementation status: Not yet implemented. Spec only. Uses existing `lib/infra/deploy.ts` with `newsletter-preview` app name.
@@ -0,0 +1,60 @@
1
+ ---
2
+ name: project-budget
3
+ description: Run FY26 budget projections with percentage or flat adjustments on FY25 checked-in units
4
+ ---
5
+
6
+ ## Purpose
7
+ Generates FY26 budget projections by applying uniform adjustments (percentage or flat) to FY25 actual checked-in units. Supports both unit volume and average retail price projections, with revenue calculations (units x retail).
8
+
9
+ Data comes from either:
10
+ 1. ReturnPro Supabase `fpa_wes_imports` table (default)
11
+ 2. A JSON file of `CheckedInUnitsSummary[]` (via `--file` flag or stdin)
12
+
13
+ ## Inputs
14
+ - **adjustment-type** (optional): `percent` or `flat`. Default: `percent`.
15
+ - **adjustment-value** (optional): Numeric value for the adjustment. Default: `0` (no change).
16
+ - **format** (optional): Output format — `table` (Bloomberg-dense markdown) or `csv`. Default: `table`.
17
+ - **fiscal-year** (optional): Base fiscal year for actuals. Default: `2025`.
18
+ - **user-id** (optional): Supabase user UUID to filter imports by.
19
+ - **file** (optional): Path to a JSON file containing `CheckedInUnitsSummary[]` array. If provided, skips Supabase fetch.
20
+
21
+ ## Steps
22
+ 1. Load FY25 data: fetch from `fpa_wes_imports` or parse from JSON file
23
+ 2. Call `initializeProjections(summary)` to create baseline entries
24
+ 3. Call `applyUniformAdjustment(projections, type, value)` if adjustment specified
25
+ 4. Format output as table or CSV via `formatProjectionTable()` or `exportToCSV()`
26
+
27
+ ## Output
28
+ Table format (default) — Bloomberg-dense:
29
+
30
+ ```
31
+ FY25 Actual: 7.35M units | FY26 Projected: 7.65M units | +4.0%
32
+ Revenue: $180.2M -> $187.4M | +4.0%
33
+
34
+ | Client | Program | FY25 Units | FY26 Units | Delta | Avg Retail | Proj Retail | Rev Delta |
35
+ |--------|---------|------------|------------|-------|------------|-------------|-----------|
36
+ | Walmart | BRTON-WM-LIQ | 120.5K | 125.3K | +4.8K (4.0%) | $45.20 | $45.20 | +$217.0K |
37
+ ```
38
+
39
+ CSV format: full-precision export with unit, retail, and inventory value columns.
40
+
41
+ ## CLI Usage
42
+ ```bash
43
+ # Default: no adjustment, table format, FY25 from Supabase
44
+ npx tsx bin/optimal.ts project-budget
45
+
46
+ # 4% growth projection
47
+ npx tsx bin/optimal.ts project-budget --adjustment-type percent --adjustment-value 4
48
+
49
+ # Flat +500 units per program
50
+ npx tsx bin/optimal.ts project-budget --adjustment-type flat --adjustment-value 500
51
+
52
+ # CSV output
53
+ npx tsx bin/optimal.ts project-budget --adjustment-type percent --adjustment-value 4 --format csv
54
+
55
+ # From JSON file instead of Supabase
56
+ npx tsx bin/optimal.ts project-budget --file ./fy25-actuals.json --adjustment-type percent --adjustment-value 10
57
+ ```
58
+
59
+ ## Environment
60
+ Requires (when using Supabase): `RETURNPRO_SUPABASE_URL`, `RETURNPRO_SUPABASE_SERVICE_KEY`
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: publish-blog
3
+ description: Publish a blog post to Strapi CMS and deploy the portfolio site to Vercel
4
+ ---
5
+
6
+ ## Purpose
7
+ End-to-end blog publishing workflow: creates or updates a blog post in Strapi CMS, publishes it, and deploys the portfolio-2026 site to Vercel so the post goes live at carloslenis.com. Supports both manual blog posts and automated research reports (AI-generated content with specific formatting rules).
8
+
9
+ ## Inputs
10
+ - **title** (required): Blog post title.
11
+ - **slug** (optional): URL slug. Default: auto-generated from title + year (e.g., `copper-investment-thesis-ai-data-centers-2026`).
12
+ - **content** (required): Markdown content for the blog post body.
13
+ - **site** (required): Site filter — `portfolio` (carloslenis.com) or `insurance` (future).
14
+ - **tags** (optional): Comma-separated tag names (e.g., `"Automated Report,AI,Finance"`).
15
+ - **documentId** (optional): If provided, updates an existing post instead of creating a new one.
16
+ - **deploy** (optional): Auto-deploy portfolio site after publishing. Default: true.
17
+ - **draft** (optional): Create as draft without publishing. Default: false (publish immediately).
18
+
19
+ ## Steps
20
+ 1. Call `lib/cms/strapi-client.ts` functions to manage the blog post lifecycle
21
+ 2. **Build payload** — construct Strapi `blog-post` data: title, slug, content, site, tags
22
+ 3. **Create or update** — if `--documentId` given, `strapiPut('/api/blog-posts', documentId, data)`; otherwise `strapiPost('/api/blog-posts', data)`
23
+ 4. **Publish** — unless `--draft` is set, call `publish('blog-posts', documentId)`
24
+ 5. **Deploy** — unless `--deploy false`, call `lib/infra/deploy.ts::deploy('portfolio', { prod: true })` to push to Vercel production
25
+ 6. **Return** — documentId, slug, and live URL
26
+ 7. Log execution via `lib/kanban.ts::logSkillExecution()`
27
+
28
+ ## Output
29
+ ```
30
+ Blog post published: "Copper Investment Thesis for AI Data Centers"
31
+ Slug: copper-investment-thesis-ai-data-centers-2026
32
+ DocumentId: abc123-def456
33
+ Site: portfolio
34
+ Tags: Automated Report, AI, Finance
35
+ Deploy: production → https://carloslenis.com
36
+ ```
37
+
38
+ ## CLI Usage
39
+ ```bash
40
+ # Publish a new blog post and deploy
41
+ optimal publish-blog --title "My Post" --content ./post.md --site portfolio --tags "Finance"
42
+
43
+ # Create as draft (no deploy)
44
+ optimal publish-blog --title "Draft Post" --content ./draft.md --site portfolio --draft
45
+
46
+ # Update existing post
47
+ optimal publish-blog --documentId abc123 --content ./updated.md --deploy
48
+ ```
49
+
50
+ ## Automated Report Format
51
+ When publishing AI-generated research reports, the content MUST follow these rules:
52
+ - **Tag**: `"Automated Report"` (never pretend Carlos wrote it)
53
+ - **Structure**: Disclosure blockquote at top, `---` between every section, `## ` headings for section cards
54
+ - **References**: Numbered inline refs `[[1]](#ref-1)` with `## Sources & References` section at bottom
55
+ - **Tables**: Use heavily to break up text (data tables, comparisons, scorecards)
56
+ - **Links**: Every data claim must be hyperlinked to its source
57
+ - **Slug format**: `topic-keywords-YYYY`
58
+
59
+ The portfolio site's `BlogContent` component auto-detects multi-section posts (4+ sections with `---`) and renders each `## ` section as a themed card with colored borders.
60
+
61
+ ## Environment
62
+ Requires: `STRAPI_URL`, `STRAPI_API_TOKEN`, `vercel` CLI (for deploy)
63
+
64
+ ## Gotchas
65
+ - **documentId for updates**: Strapi v5 uses documentId (UUID), not numeric id.
66
+ - **Site field**: The `site` enum in Strapi blog-post schema determines which site renders the post. Add new sites by editing the schema.
67
+ - **Deploy after publish**: By default, the portfolio site is redeployed to production after publishing. Pass `--deploy false` to skip.
68
+
69
+ ## Status
70
+ Implementation status: Not yet implemented. Spec only. Uses existing `lib/cms/strapi-client.ts` for Strapi operations and `lib/infra/deploy.ts` for Vercel deployment.
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: publish-social-posts
3
+ description: Push social post drafts from Strapi to live platforms (Instagram, Facebook, LinkedIn) via distribution hub
4
+ ---
5
+
6
+ ## Purpose
7
+ Publishes social post drafts from Strapi CMS to their target platforms (Instagram, Facebook, LinkedIn, Twitter/X). Works through the distribution hub: publishes the Strapi entry (triggers webhook), which n8n picks up to post to the actual social platforms via Meta Marketing API and other platform APIs. Handles both immediate publishing and scheduled posts.
8
+
9
+ ## Inputs
10
+ - **brand** (required): Brand key — `CRE-11TRUST` or `LIFEINSUR`
11
+ - **documentIds** (optional): Comma-separated Strapi documentIds to publish. If omitted, publishes all pending drafts for the brand.
12
+ - **schedule-only** (optional): Only publish posts with a `scheduled_date` in the future (let the 15-minute cron handle delivery). Default: false.
13
+ - **test** (optional): Post to test/sandbox accounts instead of live pages.
14
+
15
+ ## Steps
16
+ 1. Call `lib/cms/strapi-client.ts::publishSocialPosts(brand, options?)` to orchestrate
17
+ 2. **Fetch pending posts** — if `--documentIds` provided, fetch those specific posts; otherwise `listByBrand('social-posts', brand, 'draft')` to get all pending drafts
18
+ 3. **Validate posts** — ensure each post has required fields: headline, body, image_url, platform, scheduled_date
19
+ 4. **Publish in Strapi** — `publish('social-posts', documentId)` for each post (sets publishedAt, triggers webhook)
20
+ 5. **n8n distribution** — Strapi publish webhook fires n8n workflow which:
21
+ - Reads `brand-config` for platform IDs (IG page ID, FB page ID, etc.)
22
+ - Posts to target platform via respective API
23
+ - Updates `delivery_status` (pending → scheduled/delivered/failed)
24
+ - Writes `platform_post_id` back to Strapi on success
25
+ 6. **Report results** — summarize published vs scheduled vs failed
26
+ 7. Log execution via `lib/kanban.ts::logSkillExecution()`
27
+
28
+ ## Output
29
+ ```
30
+ Brand: LIFEINSUR
31
+ Posts processed: 9
32
+
33
+ | # | Headline | Platform | Status | Post ID |
34
+ |---|----------|----------|--------|---------|
35
+ | 1 | "Protect What Matters" | instagram | delivered | 17899... |
36
+ | 2 | "Your Family's Future" | facebook | delivered | 61294... |
37
+ | 3 | "Life Insurance Myths" | instagram | scheduled (3/4) | — |
38
+ | ... | ... | ... | ... | ... |
39
+
40
+ Delivered: 4 | Scheduled: 5 | Failed: 0
41
+ ```
42
+
43
+ ## CLI Usage
44
+ ```bash
45
+ # Publish all pending LIFEINSUR posts
46
+ optimal publish-social-posts --brand LIFEINSUR
47
+
48
+ # Publish specific posts
49
+ optimal publish-social-posts --brand CRE-11TRUST --documentIds abc123,def456,ghi789
50
+
51
+ # Only set up scheduled posts (don't post immediately)
52
+ optimal publish-social-posts --brand LIFEINSUR --schedule-only
53
+
54
+ # Test mode
55
+ optimal publish-social-posts --brand LIFEINSUR --test
56
+ ```
57
+
58
+ ## Environment
59
+ Requires: `STRAPI_URL`, `STRAPI_API_TOKEN`
60
+ Platform credentials managed in n8n (Meta access token, Twitter keys, etc.), not in CLI.
61
+
62
+ ## Gotchas
63
+ - **n8n must be running**: Distribution depends on n8n catching the Strapi webhook.
64
+ - **brand-config required**: The `brand-config` content type in Strapi must have platform IDs (IG page ID, FB page ID) for the target brand.
65
+ - **Scheduling**: Posts with future `scheduled_date` are queued — a 15-minute n8n cron picks them up. Posts with past/blank `scheduled_date` are distributed immediately on publish.
66
+ - **delivery_status flow**: pending → scheduled (if future date) → delivered/failed. Partial state means some platforms succeeded and others failed.
67
+ - **Meta Marketing API**: Requires `ads_management` + `ads_read` permissions on the Meta developer app access token.
68
+
69
+ ## Status
70
+ Implementation status: Not yet implemented. Spec only. Uses existing `lib/cms/strapi-client.ts` for Strapi operations; distribution handled by n8n webhook.
@@ -0,0 +1,62 @@
1
+ ---
2
+ name: rate-anomalies
3
+ description: Flag outlier $/unit rates across programs and months in ReturnPro financial data
4
+ ---
5
+
6
+ ## Purpose
7
+ Detects anomalous per-unit rates ($/unit) across programs and months in ReturnPro's staged financial data. Compares each program-month's rate against its historical average and flags statistical outliers. This is a key data quality tool — rate anomalies often indicate data entry errors, misclassified programs, or upstream reporting issues.
8
+
9
+ ## Inputs
10
+ - **months** (optional): Comma-separated YYYY-MM strings to analyze. Default: last 6 months with data.
11
+ - **threshold** (optional): Z-score threshold for flagging anomalies. Default: `2.0` (2 standard deviations).
12
+ - **programs** (optional): Comma-separated program name substrings to filter. Default: all programs.
13
+ - **format** (optional): Output format — `table` (default) or `csv`.
14
+
15
+ ## Steps
16
+ 1. Call `lib/returnpro/anomalies.ts::detectRateAnomalies(options?)` to fetch and analyze data
17
+ 2. Query `stg_financials_raw` for revenue and unit rows across the target months
18
+ 3. Compute $/unit rate for each program-month combination
19
+ 4. Calculate historical mean and standard deviation per program
20
+ 5. Flag any program-month where the rate's z-score exceeds the threshold
21
+ 6. Sort flagged anomalies by severity (highest z-score first)
22
+ 7. Log execution via `lib/kanban.ts::logSkillExecution()`
23
+
24
+ ## Output
25
+ Bloomberg-dense table with inline severity indicators:
26
+
27
+ | Program | Month | Rate | Avg Rate | Z-Score | Delta |
28
+ |---------|-------|------|----------|---------|-------|
29
+ | BRTON-WM | 2026-01 | $3.42/u | $2.10/u | 3.1 | ↑62.9% |
30
+ | FORTX-POOL | 2025-12 | $0.89/u | $1.55/u | -2.4 | ↓-42.6% |
31
+
32
+ Rows with z-score > 3.0 get `bg-red-500/5` row tint indicator. All numbers are `font-mono`, right-aligned.
33
+
34
+ ## CLI Usage
35
+ ```bash
36
+ # Default: last 6 months, z-score threshold 2.0
37
+ optimal rate-anomalies
38
+
39
+ # Specific months, stricter threshold
40
+ optimal rate-anomalies --months 2026-01,2025-12 --threshold 1.5
41
+
42
+ # Filter to specific programs
43
+ optimal rate-anomalies --programs BRTON,FORTX
44
+
45
+ # CSV export
46
+ optimal rate-anomalies --format csv > anomalies.csv
47
+ ```
48
+
49
+ ## Environment
50
+ Requires: `RETURNPRO_SUPABASE_URL`, `RETURNPRO_SUPABASE_SERVICE_KEY`
51
+
52
+ ## Tables Touched
53
+ - `stg_financials_raw` — read revenue and unit data (CAST amount from TEXT)
54
+ - `dim_master_program` — resolve program names
55
+
56
+ ## Gotchas
57
+ - **amount is TEXT**: Always CAST `stg_financials_raw.amount` before numeric operations.
58
+ - **Reference implementation**: The Bloomberg-dense rate anomaly explorer UI is at `components/analysis/rate-anomaly-explorer.tsx` in dashboard-returnpro (Feb 2026 redesign).
59
+ - **Sign conventions**: Revenue is positive, expenses are negative. Unit counts are always positive.
60
+
61
+ ## Status
62
+ Implementation status: Not yet implemented. Spec only. Lib function `lib/returnpro/anomalies.ts` to be extracted from dashboard-returnpro's `/api/analytics/rate-anomalies` route.
@@ -0,0 +1,49 @@
1
+ ---
2
+ name: scrape-ads
3
+ description: Scrape Meta Ad Library for competitor ad intelligence using headless Playwright browser
4
+ ---
5
+
6
+ ## Purpose
7
+ Scrape the Facebook/Meta Ad Library to extract competitor ad data for analysis. Uses headless Chromium with anti-detection measures (disabled automation features, realistic user agent, viewport 1920x1080). Extracts ad metadata including Library ID, status, start date, ad copy, platforms, impressions, spend, and media type.
8
+
9
+ ## Inputs
10
+ - **companies** (required): Comma-separated list of company names, OR a path to a text file with one company per line
11
+ - **output** (optional): File path to save CSV results (default: stdout)
12
+ - **batch-size** (optional): Number of companies per batch (default: 6). Companies run sequentially within a batch with 4s delay between each.
13
+
14
+ ## Steps
15
+ 1. **Parse companies** — accept CSV string or read from file (one company per line)
16
+ 2. **Launch browser** — headless Chromium with anti-detection args
17
+ 3. **Batch processing** — split companies into batches of `batch-size`, each batch gets a fresh browser context
18
+ 4. **Per company**: navigate to Ad Library search URL, wait for load, scroll to load all ads (up to 15 scrolls with 2s delay each)
19
+ 5. **Extract ads** — two-stage: first try DOM querySelectorAll for divs containing exactly one `Library ID: \d+`, then fallback to splitting full page text by Library ID boundaries
20
+ 6. **Parse metadata** — regex extraction of Library ID, start date, status, page name, ad text, impressions, spend, media type, platforms
21
+ 7. **Extract landing URLs** — scan DOM for `l.facebook.com` redirect links and associate with ad IDs
22
+ 8. **Output CSV** — columns: company_searched, ad_id, page_name, ad_text, status, start_date, impressions, spend, media_type, platforms, landing_page_url, full_text_snippet
23
+
24
+ ## Output
25
+ - CSV data (to stdout or file) with one row per ad
26
+ - Console progress logs during scraping
27
+
28
+ ## CLI Usage
29
+ ```bash
30
+ # Scrape specific companies (stdout)
31
+ optimal scrape-ads --companies "State Farm,Allstate,GEICO"
32
+
33
+ # Scrape from file, save to CSV
34
+ optimal scrape-ads --companies ~/projects/meta-ad-scraper/data/companies.txt --output ./ads.csv
35
+
36
+ # Custom batch size
37
+ optimal scrape-ads --companies "Company A,Company B" --batch-size 3
38
+ ```
39
+
40
+ ## Environment
41
+ Requires: `playwright` npm package with Chromium browser installed (`npx playwright install chromium`)
42
+
43
+ ## Gotchas
44
+ - **Browser install**: Playwright requires a one-time `npx playwright install chromium` to download the browser binary. The scraper will fail if this hasn't been run.
45
+ - **Rate limiting**: Facebook may rate-limit or block automated access. The 4s delay between companies and fresh contexts per batch help mitigate this.
46
+ - **Anti-detection**: Uses `--disable-blink-features=AutomationControlled` and a realistic user agent string.
47
+ - **DOM extraction**: The primary extraction strategy looks for div elements containing exactly one Library ID with text length between 50-5000 chars, deduplicated by Library ID. Falls back to text splitting if DOM strategy finds nothing.
48
+ - **Batch strategy**: Per memory, run in 3 parallel batches of 6 companies each for optimal throughput.
49
+ - **No actual execution in CI**: This scraper hits live Facebook servers. Do not run in automated pipelines without explicit intent.
@@ -0,0 +1,62 @@
1
+ ---
2
+ name: stamp-transactions
3
+ description: Auto-categorize unclassified transactions using a 4-stage rule-based matching engine
4
+ ---
5
+
6
+ ## Purpose
7
+ Queries all unclassified transactions (where `provider IS NULL` or `category_id IS NULL`) for a given user and runs them through a 4-stage matching algorithm to assign provider names and categories. Optionally runs in dry-run mode to preview results without writing.
8
+
9
+ ## Inputs
10
+ - **user-id** (required): Supabase user UUID whose transactions to stamp
11
+ - **dry-run** (optional): Preview matches without updating the database
12
+
13
+ ## Matching Algorithm (4 stages)
14
+ | Stage | Name | Confidence | Method |
15
+ |-------|------|------------|--------|
16
+ | 1 | PATTERN | 100% | Regex patterns for transfers, Zelle, P2P, CC payments, payroll, ATM, fees |
17
+ | 2 | LEARNED | 80-99% | Description hash lookup in `learned_patterns` (weight determines confidence) |
18
+ | 3 | EXACT | 100% | Provider name (or variant) found as substring in description |
19
+ | 4 | FUZZY | 60-95% | Token overlap between description and provider names (threshold 0.6) |
20
+ | Fallback | CATEGORY_INFER | 50% | Map institution-specific category to standard category |
21
+
22
+ Transactions matching at >= 90% confidence are auto-confirmed. Below that, they remain `pending` for user review.
23
+
24
+ ## Steps
25
+ 1. Load matching rules from DB: `providers`, `learned_patterns`, `user_provider_overrides`
26
+ 2. Fetch unclassified transactions for the user
27
+ 3. Fetch `stamp_categories` and user `categories` for name-to-ID mapping
28
+ 4. Run each transaction through the 4-stage pipeline
29
+ 5. Update matched rows with `provider`, `provider_method`, `provider_confidence`, `category_id`
30
+
31
+ ## Output
32
+ ```
33
+ Matcher loaded: 342 providers, 89 learned patterns
34
+ Found 156 unclassified transactions
35
+
36
+ Stamped: 127 | Unmatched: 29 | Total: 156
37
+ By match type: PATTERN=18, LEARNED=34, EXACT=52, FUZZY=19, CATEGORY_INFER=4
38
+ ```
39
+
40
+ In dry-run mode, no database writes occur — the output shows what would happen.
41
+
42
+ ## CLI Usage
43
+ ```bash
44
+ # Full stamp run
45
+ tsx bin/optimal.ts stamp-transactions --user-id <uuid>
46
+
47
+ # Preview only
48
+ tsx bin/optimal.ts stamp-transactions --user-id <uuid> --dry-run
49
+ ```
50
+
51
+ ## Environment
52
+ Requires: `OPTIMAL_SUPABASE_URL`, `OPTIMAL_SUPABASE_SERVICE_KEY`
53
+
54
+ ## Tables Read
55
+ - `providers` — global provider-to-category mappings + aliases
56
+ - `learned_patterns` — user-confirmed description patterns
57
+ - `user_provider_overrides` — per-user category overrides
58
+ - `stamp_categories` — standard category definitions
59
+ - `categories` — user-specific categories
60
+
61
+ ## Tables Written
62
+ - `transactions` — update `provider`, `provider_method`, `provider_confidence`, `provider_inferred_at`, `category_id`
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: upload-income-statements
3
+ description: Load confirmed income statement CSVs into ReturnPro for accuracy auditing
4
+ ---
5
+
6
+ ## Purpose
7
+ Uploads confirmed income statement CSVs (exported from NetSuite) into `confirmed_income_statements`. These serve as the source of truth for financial accuracy auditing — the audit-financials skill compares staged data against these confirmed records. Maintaining accurate confirmed data is essential because ReturnPro targets 100% accuracy between staged and confirmed financials.
8
+
9
+ ## Inputs
10
+ - **file** (required): Absolute path to the income statement CSV file on disk
11
+ - **month** (required): Target month as YYYY-MM (e.g., `2026-01`)
12
+ - **replace** (optional): If set, deletes existing confirmed rows for the target month before inserting. Default: false (append/upsert).
13
+
14
+ ## Steps
15
+ 1. Call `lib/returnpro/upload-income.ts::uploadIncomeStatements(file, month, options?)` to orchestrate the upload
16
+ 2. Read the CSV file and parse rows (account_code, account_name, total_amount, period)
17
+ 3. Validate account codes against `dim_account`
18
+ 4. If `--replace` flag is set, delete existing `confirmed_income_statements` rows for the target month
19
+ 5. Batch-insert rows into `confirmed_income_statements`
20
+ 6. Run a quick accuracy check against `stg_financials_raw` for the uploaded month (same logic as audit-financials)
21
+ 7. Report accuracy inline so Carlos immediately knows the data state
22
+ 8. Log execution via `lib/kanban.ts::logSkillExecution()`
23
+
24
+ ## Output
25
+ ```
26
+ Parsed income statement CSV: 189 accounts for 2026-01
27
+ Inserted: 189 | Replaced: 0
28
+ Quick accuracy check (2026-01): 91.2% (83/91 staged accounts match)
29
+ ```
30
+
31
+ ## CLI Usage
32
+ ```bash
33
+ # Upload income statement
34
+ optimal upload-income-statements --file ~/Downloads/returnpro-data/IS-Jan-2026.csv --month 2026-01
35
+
36
+ # Replace existing month data
37
+ optimal upload-income-statements --file ~/Downloads/returnpro-data/IS-Jan-2026.csv --month 2026-01 --replace
38
+ ```
39
+
40
+ ## Environment
41
+ Requires: `RETURNPRO_SUPABASE_URL`, `RETURNPRO_SUPABASE_SERVICE_KEY`
42
+
43
+ ## Tables Touched
44
+ - `confirmed_income_statements` — insert/replace confirmed GL account rows
45
+ - `stg_financials_raw` — read-only for post-upload accuracy check
46
+ - `dim_account` — validate account codes
47
+
48
+ ## Gotchas
49
+ - **Coverage gap**: Confirmed data has ~185-193 accounts/month vs staging's ~88-93. The delta is GL accounts not in Solution7 (expected, but tracked).
50
+ - **Always run audit after upload**: The skill automatically runs a quick accuracy check, but a full audit-financials run is recommended for detailed investigation.
51
+ - **Upload via Admin Console**: Can also be done via the ReturnPro Admin Console UI (Income Statement tab).
52
+
53
+ ## Status
54
+ Implementation status: Not yet implemented. Spec only. Lib function `lib/returnpro/upload-income.ts` to be extracted from dashboard-returnpro's `/api/admin/confirmed-income-statements` route.