optimal-cli 1.0.1 → 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 (185) 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 +1418 -0
  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/budget/projections.ts +561 -0
  17. package/lib/budget/scenarios.ts +312 -0
  18. package/lib/cms/publish-blog.ts +129 -0
  19. package/lib/cms/strapi-client.ts +302 -0
  20. package/lib/config/registry.ts +229 -0
  21. package/lib/config/schema.ts +58 -0
  22. package/lib/config.ts +247 -0
  23. package/lib/infra/.gitkeep +0 -0
  24. package/lib/infra/deploy.ts +70 -0
  25. package/lib/infra/migrate.ts +141 -0
  26. package/lib/kanban-obsidian.ts +232 -0
  27. package/lib/kanban-sync.ts +258 -0
  28. package/lib/kanban.ts +239 -0
  29. package/lib/newsletter/.gitkeep +0 -0
  30. package/lib/newsletter/distribute.ts +256 -0
  31. package/{dist/lib/newsletter/generate-insurance.d.ts → lib/newsletter/generate-insurance.ts} +24 -7
  32. package/lib/newsletter/generate.ts +735 -0
  33. package/lib/obsidian-tasks.ts +231 -0
  34. package/lib/returnpro/.gitkeep +0 -0
  35. package/lib/returnpro/anomalies.ts +258 -0
  36. package/lib/returnpro/audit.ts +194 -0
  37. package/lib/returnpro/diagnose.ts +400 -0
  38. package/lib/returnpro/kpis.ts +255 -0
  39. package/lib/returnpro/templates.ts +323 -0
  40. package/lib/returnpro/upload-income.ts +311 -0
  41. package/lib/returnpro/upload-netsuite.ts +696 -0
  42. package/lib/returnpro/upload-r1.ts +563 -0
  43. package/lib/social/post-generator.ts +468 -0
  44. package/lib/social/publish.ts +301 -0
  45. package/lib/social/scraper.ts +503 -0
  46. package/lib/supabase.ts +25 -0
  47. package/lib/transactions/delete-batch.ts +258 -0
  48. package/lib/transactions/ingest.ts +659 -0
  49. package/lib/transactions/stamp.ts +654 -0
  50. package/package.json +5 -18
  51. package/pnpm-workspace.yaml +3 -0
  52. package/scripts/check-table.ts +24 -0
  53. package/scripts/create-tables.ts +94 -0
  54. package/scripts/migrate-kanban.sh +28 -0
  55. package/scripts/migrate-v2.ts +78 -0
  56. package/scripts/migrate.ts +79 -0
  57. package/scripts/run-migration.ts +59 -0
  58. package/scripts/seed-board.ts +203 -0
  59. package/scripts/test-kanban.ts +21 -0
  60. package/skills/audit-financials/SKILL.md +33 -0
  61. package/skills/board-create/SKILL.md +28 -0
  62. package/skills/board-update/SKILL.md +27 -0
  63. package/skills/board-view/SKILL.md +27 -0
  64. package/skills/delete-batch/SKILL.md +77 -0
  65. package/skills/deploy/SKILL.md +40 -0
  66. package/skills/diagnose-months/SKILL.md +68 -0
  67. package/skills/distribute-newsletter/SKILL.md +58 -0
  68. package/skills/export-budget/SKILL.md +44 -0
  69. package/skills/export-kpis/SKILL.md +52 -0
  70. package/skills/generate-netsuite-template/SKILL.md +51 -0
  71. package/skills/generate-newsletter/SKILL.md +53 -0
  72. package/skills/generate-newsletter-insurance/SKILL.md +59 -0
  73. package/skills/generate-social-posts/SKILL.md +67 -0
  74. package/skills/health-check/SKILL.md +42 -0
  75. package/skills/ingest-transactions/SKILL.md +51 -0
  76. package/skills/manage-cms/SKILL.md +50 -0
  77. package/skills/manage-scenarios/SKILL.md +83 -0
  78. package/skills/migrate-db/SKILL.md +79 -0
  79. package/skills/preview-newsletter/SKILL.md +50 -0
  80. package/skills/project-budget/SKILL.md +60 -0
  81. package/skills/publish-blog/SKILL.md +70 -0
  82. package/skills/publish-social-posts/SKILL.md +70 -0
  83. package/skills/rate-anomalies/SKILL.md +62 -0
  84. package/skills/scrape-ads/SKILL.md +49 -0
  85. package/skills/stamp-transactions/SKILL.md +62 -0
  86. package/skills/upload-income-statements/SKILL.md +54 -0
  87. package/skills/upload-netsuite/SKILL.md +56 -0
  88. package/skills/upload-r1/SKILL.md +45 -0
  89. package/supabase/.temp/cli-latest +1 -0
  90. package/supabase/migrations/.gitkeep +0 -0
  91. package/supabase/migrations/20250305000001_create_agent_configs.sql +36 -0
  92. package/supabase/migrations/20260305111300_create_cli_config_registry.sql +22 -0
  93. package/supabase/migrations/20260306195000_create_kanban_tables.sql +97 -0
  94. package/tests/config-command-smoke.test.ts +395 -0
  95. package/tests/config-registry.test.ts +173 -0
  96. package/tsconfig.json +19 -0
  97. package/agents/profiles.json +0 -5
  98. package/dist/bin/optimal.d.ts +0 -2
  99. package/dist/bin/optimal.js +0 -1590
  100. package/dist/lib/assets/index.d.ts +0 -79
  101. package/dist/lib/assets/index.js +0 -153
  102. package/dist/lib/assets.d.ts +0 -20
  103. package/dist/lib/assets.js +0 -112
  104. package/dist/lib/auth/index.d.ts +0 -83
  105. package/dist/lib/auth/index.js +0 -146
  106. package/dist/lib/board/index.d.ts +0 -39
  107. package/dist/lib/board/index.js +0 -285
  108. package/dist/lib/board/types.d.ts +0 -111
  109. package/dist/lib/board/types.js +0 -1
  110. package/dist/lib/bot/claim.d.ts +0 -3
  111. package/dist/lib/bot/claim.js +0 -20
  112. package/dist/lib/bot/coordinator.d.ts +0 -27
  113. package/dist/lib/bot/coordinator.js +0 -178
  114. package/dist/lib/bot/heartbeat.d.ts +0 -6
  115. package/dist/lib/bot/heartbeat.js +0 -30
  116. package/dist/lib/bot/index.d.ts +0 -9
  117. package/dist/lib/bot/index.js +0 -6
  118. package/dist/lib/bot/protocol.d.ts +0 -12
  119. package/dist/lib/bot/protocol.js +0 -74
  120. package/dist/lib/bot/reporter.d.ts +0 -3
  121. package/dist/lib/bot/reporter.js +0 -27
  122. package/dist/lib/bot/skills.d.ts +0 -26
  123. package/dist/lib/bot/skills.js +0 -69
  124. package/dist/lib/budget/projections.d.ts +0 -115
  125. package/dist/lib/budget/projections.js +0 -384
  126. package/dist/lib/budget/scenarios.d.ts +0 -93
  127. package/dist/lib/budget/scenarios.js +0 -214
  128. package/dist/lib/cms/publish-blog.d.ts +0 -62
  129. package/dist/lib/cms/publish-blog.js +0 -74
  130. package/dist/lib/cms/strapi-client.d.ts +0 -123
  131. package/dist/lib/cms/strapi-client.js +0 -213
  132. package/dist/lib/config/registry.d.ts +0 -17
  133. package/dist/lib/config/registry.js +0 -182
  134. package/dist/lib/config/schema.d.ts +0 -31
  135. package/dist/lib/config/schema.js +0 -25
  136. package/dist/lib/config.d.ts +0 -55
  137. package/dist/lib/config.js +0 -206
  138. package/dist/lib/errors.d.ts +0 -25
  139. package/dist/lib/errors.js +0 -91
  140. package/dist/lib/format.d.ts +0 -28
  141. package/dist/lib/format.js +0 -98
  142. package/dist/lib/infra/deploy.d.ts +0 -29
  143. package/dist/lib/infra/deploy.js +0 -58
  144. package/dist/lib/infra/migrate.d.ts +0 -34
  145. package/dist/lib/infra/migrate.js +0 -103
  146. package/dist/lib/newsletter/distribute.d.ts +0 -52
  147. package/dist/lib/newsletter/distribute.js +0 -193
  148. package/dist/lib/newsletter/generate-insurance.js +0 -36
  149. package/dist/lib/newsletter/generate.d.ts +0 -104
  150. package/dist/lib/newsletter/generate.js +0 -571
  151. package/dist/lib/returnpro/anomalies.d.ts +0 -64
  152. package/dist/lib/returnpro/anomalies.js +0 -166
  153. package/dist/lib/returnpro/audit.d.ts +0 -32
  154. package/dist/lib/returnpro/audit.js +0 -147
  155. package/dist/lib/returnpro/diagnose.d.ts +0 -52
  156. package/dist/lib/returnpro/diagnose.js +0 -281
  157. package/dist/lib/returnpro/kpis.d.ts +0 -32
  158. package/dist/lib/returnpro/kpis.js +0 -192
  159. package/dist/lib/returnpro/templates.d.ts +0 -48
  160. package/dist/lib/returnpro/templates.js +0 -229
  161. package/dist/lib/returnpro/upload-income.d.ts +0 -25
  162. package/dist/lib/returnpro/upload-income.js +0 -235
  163. package/dist/lib/returnpro/upload-netsuite.d.ts +0 -37
  164. package/dist/lib/returnpro/upload-netsuite.js +0 -566
  165. package/dist/lib/returnpro/upload-r1.d.ts +0 -48
  166. package/dist/lib/returnpro/upload-r1.js +0 -398
  167. package/dist/lib/returnpro/validate.d.ts +0 -37
  168. package/dist/lib/returnpro/validate.js +0 -124
  169. package/dist/lib/social/meta.d.ts +0 -90
  170. package/dist/lib/social/meta.js +0 -160
  171. package/dist/lib/social/post-generator.d.ts +0 -83
  172. package/dist/lib/social/post-generator.js +0 -333
  173. package/dist/lib/social/publish.d.ts +0 -66
  174. package/dist/lib/social/publish.js +0 -226
  175. package/dist/lib/social/scraper.d.ts +0 -67
  176. package/dist/lib/social/scraper.js +0 -361
  177. package/dist/lib/supabase.d.ts +0 -4
  178. package/dist/lib/supabase.js +0 -20
  179. package/dist/lib/transactions/delete-batch.d.ts +0 -60
  180. package/dist/lib/transactions/delete-batch.js +0 -203
  181. package/dist/lib/transactions/ingest.d.ts +0 -43
  182. package/dist/lib/transactions/ingest.js +0 -555
  183. package/dist/lib/transactions/stamp.d.ts +0 -51
  184. package/dist/lib/transactions/stamp.js +0 -524
  185. package/docs/CLI-REFERENCE.md +0 -361
@@ -0,0 +1,24 @@
1
+ import { createClient } from '@supabase/supabase-js'
2
+
3
+ const url = 'https://hbfalrpswysryltysonm.supabase.co'
4
+ const key = process.env.OPTIMAL_SUPABASE_SERVICE_KEY || 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImhiZmFscnBzd3lzcnlsdHlzb25tIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTczODI0NTY0NywiZXhwIjoyMDUzODIxNjQ3fQ.placeholder'
5
+
6
+ const supabase = createClient(url, key)
7
+
8
+ async function checkTable() {
9
+ // Try to query the table
10
+ const { data, error } = await supabase
11
+ .from('agent_configs')
12
+ .select('*')
13
+ .limit(1)
14
+
15
+ if (error) {
16
+ console.log('Table status:', error.code === '42P01' ? 'DOES NOT EXIST' : 'ERROR')
17
+ console.log('Error:', error.message)
18
+ } else {
19
+ console.log('Table status: EXISTS')
20
+ console.log('Rows:', data.length)
21
+ }
22
+ }
23
+
24
+ checkTable()
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Run kanban migration - creates tables in supabase
3
+ */
4
+ import { createClient } from '@supabase/supabase-js'
5
+
6
+ const SUPABASE_URL = 'https://vvutttwunexshxkmygik.supabase.co'
7
+ const SUPABASE_SERVICE_KEY = 'sb_secret_zokm_eFGX9ENuRV8hKA05Q_JQDOEOOl'
8
+
9
+ const supabase = createClient(SUPABASE_URL, SUPABASE_SERVICE_KEY, {
10
+ db: { schema: 'public' }
11
+ })
12
+
13
+ const sql = `
14
+ -- Projects table
15
+ CREATE TABLE IF NOT EXISTS cli_projects (
16
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
17
+ slug TEXT UNIQUE NOT NULL,
18
+ name TEXT NOT NULL,
19
+ description TEXT,
20
+ status TEXT DEFAULT 'active' CHECK (status IN ('active', 'archived', 'on_hold')),
21
+ created_at TIMESTAMPTZ DEFAULT NOW(),
22
+ updated_at TIMESTAMPTZ DEFAULT NOW()
23
+ );
24
+
25
+ -- Tasks table
26
+ CREATE TABLE IF NOT EXISTS cli_tasks (
27
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
28
+ project_id UUID REFERENCES cli_projects(id) ON DELETE CASCADE,
29
+ task_id TEXT NOT NULL,
30
+ title TEXT NOT NULL,
31
+ description TEXT,
32
+ status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'in_progress', 'done', 'cancelled')),
33
+ priority INTEGER DEFAULT 3 CHECK (priority BETWEEN 1 AND 4),
34
+ owner TEXT,
35
+ assignee TEXT,
36
+ tags JSONB DEFAULT '[]',
37
+ source TEXT,
38
+ created_at TIMESTAMPTZ DEFAULT NOW(),
39
+ updated_at TIMESTAMPTZ DEFAULT NOW(),
40
+ completed_at TIMESTAMPTZ,
41
+ completed_by TEXT,
42
+ metadata JSONB DEFAULT '{}',
43
+ UNIQUE(project_id, task_id)
44
+ );
45
+
46
+ -- Sync log
47
+ CREATE TABLE IF NOT EXISTS cli_sync_log (
48
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
49
+ entity_type TEXT NOT NULL,
50
+ entity_id UUID NOT NULL,
51
+ action TEXT NOT NULL,
52
+ source TEXT NOT NULL,
53
+ synced_at TIMESTAMPTZ DEFAULT NOW(),
54
+ payload JSONB
55
+ );
56
+
57
+ -- Indexes
58
+ CREATE INDEX IF NOT EXISTS idx_tasks_project ON cli_tasks(project_id);
59
+ CREATE INDEX IF NOT EXISTS idx_tasks_status ON cli_tasks(status);
60
+ CREATE INDEX IF NOT EXISTS idx_tasks_owner ON cli_tasks(owner);
61
+ CREATE INDEX IF NOT EXISTS idx_tasks_assignee ON cli_tasks(assignee);
62
+ `
63
+
64
+ async function main() {
65
+ console.log('Running migration...')
66
+
67
+ // Run raw SQL via postgrest
68
+ const { data, error } = await supabase.rpc('pg_catalog.exec', {
69
+ query: sql
70
+ }).catch(() => {
71
+ // Fallback: try via REST directly
72
+ return supabase.from('_temp').select('*').limit(0)
73
+ })
74
+
75
+ // Actually, rpc won't work - let's just create tables one by one via insert
76
+ // Since we can't run raw SQL easily, let's try a different approach
77
+ console.log('Trying alternative...')
78
+
79
+ // Check if tables exist
80
+ const { data: tables } = await supabase
81
+ .from('information_schema.tables')
82
+ .select('table_name')
83
+ .eq('table_schema', 'public')
84
+ .in('table_name', ['cli_projects', 'cli_tasks'])
85
+
86
+ console.log('Existing tables:', tables)
87
+
88
+ if (!tables?.length) {
89
+ console.log('Tables do not exist. Trying to create via console...')
90
+ console.log('Please run manually or use supabase studio.')
91
+ }
92
+ }
93
+
94
+ main().catch(console.error)
@@ -0,0 +1,28 @@
1
+ #!/bin/bash
2
+ # Migrate kanban tables to supabase
3
+
4
+ SQL_FILE="supabase/migrations/20260306195000_create_kanban_tables.sql"
5
+
6
+ # Load env from workspace secrets
7
+ if [ -f ".secrets/dashboard-returnpro.vercel.env" ]; then
8
+ export "$(cat .secrets/dashboard-returnpro.vercel.env | grep -v '^#' | xargs)"
9
+ fi
10
+
11
+ # Check for supabase URL and key
12
+ if [ -z "$SUPABASE_URL" ] || [ -z "$SUPABASE_SERVICE_ROLE_KEY" ]; then
13
+ echo "Error: SUPABASE_URL or SUPABASE_SERVICE_ROLE_KEY not found"
14
+ exit 1
15
+ fi
16
+
17
+ echo "Applying migration..."
18
+ echo "SUPABASE_URL: $SUPABASE_URL"
19
+
20
+ # Use psql if available, otherwise try with supabase cli
21
+ if command -v psql &> /dev/null; then
22
+ # Extract password from the key (it's after the = sign)
23
+ DB_PASS="${SUPABASE_SERVICE_ROLE_KEY##*=}"
24
+ psql "host=vvutttwunexshxkmygik.supabase.co port=5432 dbname=postgres user=postgres password=$DB_PASS" -f "$SQL_FILE" 2>&1
25
+ else
26
+ echo "psql not available, trying supabase cli..."
27
+ npx supabase db push 2>&1
28
+ fi
@@ -0,0 +1,78 @@
1
+ import { createClient } from '@supabase/supabase-js'
2
+
3
+ const url = 'https://hbfalrpswysryltysonm.supabase.co'
4
+ const key = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImhiZmFscnBzd3lzcnlsdHlzb25tIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc0MjIzMTEyMiwiZXhwIjoyMDU3ODA3MTIyfQ.oyzf_We-WCOsJ8xYs2_Q9wi8QSBr_1Ym_F_75o67kR0'
5
+
6
+ const supabase = createClient(url, key)
7
+
8
+ async function runMigration() {
9
+ console.log('🔧 Running migration: agent_configs table')
10
+ console.log(` URL: ${url}`)
11
+ console.log('')
12
+
13
+ // Test connection first
14
+ const { data: testData, error: testError } = await supabase
15
+ .from('agent_configs')
16
+ .select('count')
17
+ .limit(1)
18
+
19
+ if (testError?.code === '42P01') {
20
+ console.log(' Table does not exist. Creating...')
21
+ } else if (testError) {
22
+ console.log(' Error checking table:', testError.message)
23
+ } else {
24
+ console.log(' ✓ Table already exists')
25
+ return
26
+ }
27
+
28
+ // Create table using raw SQL
29
+ const sql = `
30
+ CREATE TABLE IF NOT EXISTS agent_configs (
31
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
32
+ agent_name TEXT NOT NULL UNIQUE,
33
+ config_json JSONB NOT NULL DEFAULT '{}',
34
+ version TEXT NOT NULL DEFAULT '0.0.0',
35
+ created_at TIMESTAMPTZ DEFAULT NOW(),
36
+ updated_at TIMESTAMPTZ DEFAULT NOW()
37
+ );
38
+ `
39
+
40
+ // Try using the SQL API directly
41
+ const response = await fetch(`${url}/rest/v1/`, {
42
+ method: 'POST',
43
+ headers: {
44
+ 'Content-Type': 'application/json',
45
+ 'Authorization': `Bearer ${key}`,
46
+ 'apikey': key,
47
+ 'Prefer': 'tx=rollback'
48
+ },
49
+ body: JSON.stringify({ query: sql })
50
+ })
51
+
52
+ console.log(' Response status:', response.status)
53
+
54
+ if (response.status === 404 || response.status === 400) {
55
+ console.log('')
56
+ console.log('⚠️ Cannot run migration via REST API.')
57
+ console.log('')
58
+ console.log('Please run this SQL in Supabase SQL Editor:')
59
+ console.log('')
60
+ console.log('━'.repeat(60))
61
+ console.log(sql)
62
+ console.log('━'.repeat(60))
63
+ console.log('')
64
+ console.log('Then run:')
65
+ console.log(' CREATE INDEX idx_agent_configs_name ON agent_configs(agent_name);')
66
+ console.log(' ALTER TABLE agent_configs ENABLE ROW LEVEL SECURITY;')
67
+ console.log(' CREATE POLICY "Service role full access" ON agent_configs FOR ALL TO service_role USING (true) WITH CHECK (true);')
68
+ process.exit(1)
69
+ }
70
+
71
+ const text = await response.text()
72
+ console.log(' Response:', text.slice(0, 200))
73
+ }
74
+
75
+ runMigration().catch(err => {
76
+ console.error('❌ Migration failed:', err)
77
+ process.exit(1)
78
+ })
@@ -0,0 +1,79 @@
1
+ import { createClient } from '@supabase/supabase-js'
2
+
3
+ const url = 'https://hbfalrpswysryltysonm.supabase.co'
4
+ const key = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImhiZmFscnBzd3lzcnlsdHlzb25tIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc0MjIzMTEyMiwiZXhwIjoyMDU3ODA3MTIyfQ.oyzf_We-WCOsJ8xYs2_Q9wi8QSBr_1Ym_F_75o67kR0'
5
+
6
+ const supabase = createClient(url, key)
7
+
8
+ async function createTable() {
9
+ console.log('🔧 Creating agent_configs table...')
10
+
11
+ // Create table using raw SQL via RPC or direct query
12
+ const createTableSQL = `
13
+ CREATE TABLE IF NOT EXISTS agent_configs (
14
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
15
+ agent_name TEXT NOT NULL UNIQUE,
16
+ config_json JSONB NOT NULL DEFAULT '{}',
17
+ version TEXT NOT NULL DEFAULT '0.0.0',
18
+ created_at TIMESTAMPTZ DEFAULT NOW(),
19
+ updated_at TIMESTAMPTZ DEFAULT NOW()
20
+ );
21
+ `
22
+
23
+ // Try to create the table by inserting and letting it fail if exists
24
+ const { error: insertError } = await supabase
25
+ .from('agent_configs')
26
+ .insert({
27
+ agent_name: '__test__',
28
+ config_json: {},
29
+ version: '0.0.0'
30
+ })
31
+
32
+ if (insertError?.code === '42P01') {
33
+ console.log(' Table does not exist. Creating via SQL...')
34
+
35
+ // Use the SQL API
36
+ const response = await fetch(`${url}/rest/v1/rpc/exec_sql`, {
37
+ method: 'POST',
38
+ headers: {
39
+ 'Content-Type': 'application/json',
40
+ 'Authorization': `Bearer ${key}`,
41
+ 'apikey': key
42
+ },
43
+ body: JSON.stringify({ sql: createTableSQL })
44
+ })
45
+
46
+ if (!response.ok) {
47
+ const text = await response.text()
48
+ console.log(' RPC failed, trying alternative...')
49
+ console.log(' Error:', text.slice(0, 200))
50
+
51
+ // Alternative: create via pg_graphql or direct postgrest
52
+ console.log(' Please run this SQL in Supabase SQL Editor:')
53
+ console.log('')
54
+ console.log(createTableSQL)
55
+ console.log('')
56
+ console.log(' Then run:')
57
+ console.log(' CREATE INDEX idx_agent_configs_name ON agent_configs(agent_name);')
58
+ console.log(' ALTER TABLE agent_configs ENABLE ROW LEVEL SECURITY;')
59
+ process.exit(1)
60
+ }
61
+
62
+ console.log(' ✓ Table created')
63
+ } else if (insertError?.code === '23505') {
64
+ console.log(' ✓ Table already exists (unique constraint violation on test insert)')
65
+ // Clean up test row
66
+ await supabase.from('agent_configs').delete().eq('agent_name', '__test__')
67
+ } else if (insertError) {
68
+ console.log(' Error:', insertError.message)
69
+ } else {
70
+ console.log(' ✓ Table exists and is writable')
71
+ // Clean up test row
72
+ await supabase.from('agent_configs').delete().eq('agent_name', '__test__')
73
+ }
74
+
75
+ console.log('')
76
+ console.log('✅ Migration complete!')
77
+ }
78
+
79
+ createTable().catch(console.error)
@@ -0,0 +1,59 @@
1
+ import { createClient } from '@supabase/supabase-js'
2
+
3
+ const url = process.env.OPTIMAL_SUPABASE_URL!
4
+ const key = process.env.OPTIMAL_SUPABASE_SERVICE_KEY!
5
+
6
+ const supabase = createClient(url, key)
7
+
8
+ // Create the table via SQL query
9
+ const sql = `
10
+ create table if not exists public.cli_config_registry (
11
+ id uuid primary key default gen_random_uuid(),
12
+ owner text not null,
13
+ profile text not null default 'default',
14
+ config_version text not null,
15
+ payload jsonb not null,
16
+ payload_hash text not null,
17
+ source text not null default 'optimal-cli',
18
+ updated_by text,
19
+ updated_at timestamptz not null default now(),
20
+ created_at timestamptz not null default now(),
21
+ unique (owner, profile)
22
+ );
23
+
24
+ create index if not exists idx_cli_config_registry_owner_profile
25
+ on public.cli_config_registry (owner, profile);
26
+
27
+ create index if not exists idx_cli_config_registry_updated_at
28
+ on public.cli_config_registry (updated_at desc);
29
+ `
30
+
31
+ const { data, error } = await supabase.rpc('pg_execute', { sql_text: sql })
32
+
33
+ if (error) {
34
+ console.error('Migration failed:', error)
35
+ // Try alternative approach
36
+ console.log('Attempting alternative table creation...')
37
+
38
+ // Just verify table exists by querying
39
+ const { data: test, error: testErr } = await supabase
40
+ .from('cli_config_registry')
41
+ .select('count')
42
+ .limit(1)
43
+
44
+ if (testErr && testErr.code !== '42P01') {
45
+ console.error('Table verification failed:', testErr)
46
+ process.exit(1)
47
+ }
48
+
49
+ if (testErr?.code === '42P01') {
50
+ console.error('Table cli_config_registry does not exist and could not be created.')
51
+ console.error('Please run this SQL manually in Supabase SQL Editor:')
52
+ console.log(sql)
53
+ process.exit(1)
54
+ }
55
+
56
+ console.log('Table exists!')
57
+ } else {
58
+ console.log('Migration applied successfully')
59
+ }
@@ -0,0 +1,203 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Seed the kanban board with the full implementation backlog.
4
+ * Idempotent — checks existing tasks by title before creating.
5
+ *
6
+ * Usage: npx tsx scripts/seed-board.ts
7
+ */
8
+ import 'dotenv/config'
9
+ import { createTask, getBoard } from '../lib/kanban.js'
10
+
11
+ const PROJECT_SLUG = 'optimal-cli-refactor'
12
+
13
+ interface SeedTask {
14
+ title: string
15
+ skill_ref?: string
16
+ priority: 1 | 2 | 3 | 4
17
+ labels: string[]
18
+ description?: string
19
+ }
20
+
21
+ const tasks: SeedTask[] = [
22
+ // --- Phase 2 follow-ups (ready — no blockers) ---
23
+ {
24
+ title: 'Implement upload-r1 lib function',
25
+ skill_ref: '/upload-r1',
26
+ priority: 2,
27
+ labels: ['returnpro', 'phase-2'],
28
+ description: 'Extract R1 marketplace data upload from dashboard-returnpro into lib/returnpro/upload-r1.ts',
29
+ },
30
+ {
31
+ title: 'Implement upload-netsuite lib function',
32
+ skill_ref: '/upload-netsuite',
33
+ priority: 2,
34
+ labels: ['returnpro', 'phase-2'],
35
+ description: 'Extract NetSuite XLSM upload pipeline into lib/returnpro/upload-netsuite.ts',
36
+ },
37
+ {
38
+ title: 'Implement upload-income-statements lib function',
39
+ skill_ref: '/upload-income-statements',
40
+ priority: 2,
41
+ labels: ['returnpro', 'phase-2'],
42
+ description: 'Extract confirmed income statement CSV upload into lib/returnpro/upload-income-statements.ts',
43
+ },
44
+ {
45
+ title: 'Implement rate-anomalies lib function',
46
+ skill_ref: '/rate-anomalies',
47
+ priority: 3,
48
+ labels: ['returnpro', 'phase-2'],
49
+ description: 'Extract rate anomaly detection logic from dashboard-returnpro into lib/returnpro/rate-anomalies.ts',
50
+ },
51
+ {
52
+ title: 'Implement diagnose-months lib function',
53
+ skill_ref: '/diagnose-months',
54
+ priority: 3,
55
+ labels: ['returnpro', 'phase-2'],
56
+ description: 'Extract month-level diagnostic comparison into lib/returnpro/diagnose-months.ts',
57
+ },
58
+ {
59
+ title: 'Implement generate-netsuite-template lib function',
60
+ skill_ref: '/generate-netsuite-template',
61
+ priority: 3,
62
+ labels: ['returnpro', 'phase-2'],
63
+ description: 'Generate blank NetSuite XLSM templates for data entry into lib/returnpro/generate-netsuite-template.ts',
64
+ },
65
+
66
+ // --- Content follow-ups (ready) ---
67
+ {
68
+ title: 'Implement generate-newsletter-insurance',
69
+ skill_ref: '/generate-newsletter-insurance',
70
+ priority: 2,
71
+ labels: ['content', 'phase-3'],
72
+ description: 'Insurance-specific newsletter generation (LIFEINSUR brand) with Groq AI content',
73
+ },
74
+ {
75
+ title: 'Implement distribute-newsletter (n8n webhook)',
76
+ skill_ref: '/distribute-newsletter',
77
+ priority: 2,
78
+ labels: ['content', 'phase-3'],
79
+ description: 'Trigger n8n webhook to distribute published newsletter via GoHighLevel email',
80
+ },
81
+ {
82
+ title: 'Implement generate-social-posts pipeline',
83
+ skill_ref: '/generate-social-posts',
84
+ priority: 2,
85
+ labels: ['content', 'phase-3'],
86
+ description: 'Full pipeline: scrape competitors, analyze patterns, generate 9 social posts, push to Strapi',
87
+ },
88
+ {
89
+ title: 'Implement publish-social-posts',
90
+ skill_ref: '/publish-social-posts',
91
+ priority: 3,
92
+ labels: ['content', 'phase-3'],
93
+ description: 'Publish scheduled social posts to Meta (IG/FB) via Marketing API',
94
+ },
95
+ {
96
+ title: 'Implement publish-blog',
97
+ skill_ref: '/publish-blog',
98
+ priority: 3,
99
+ labels: ['content', 'phase-3'],
100
+ description: 'Publish blog post to Strapi CMS and deploy preview site via Vercel',
101
+ },
102
+
103
+ // --- Infrastructure follow-ups (ready) ---
104
+ {
105
+ title: 'Implement migrate-db skill',
106
+ skill_ref: '/migrate-db',
107
+ priority: 3,
108
+ labels: ['infra', 'phase-3'],
109
+ description: 'Run Supabase migrations via CLI (supabase db push --linked) with pre-flight checks',
110
+ },
111
+ {
112
+ title: 'Implement manage-scenarios for budget',
113
+ skill_ref: '/manage-scenarios',
114
+ priority: 3,
115
+ labels: ['budget', 'phase-3'],
116
+ description: 'CRUD for budget projection scenarios (save/load/compare named adjustment sets)',
117
+ },
118
+ {
119
+ title: 'Implement delete-batch for transactions',
120
+ skill_ref: '/delete-batch',
121
+ priority: 3,
122
+ labels: ['transactions', 'phase-3'],
123
+ description: 'Bulk delete transactions by date range or import batch ID with confirmation safeguard',
124
+ },
125
+
126
+ // --- Frontend migration (backlog — future phase) ---
127
+ {
128
+ title: 'Migrate dashboard-returnpro to apps/ as read-only',
129
+ priority: 4,
130
+ labels: ['frontend', 'phase-4'],
131
+ description: 'Move dashboard-returnpro Next.js app into apps/dashboard-returnpro as a read-only frontend consuming CLI skills',
132
+ },
133
+ {
134
+ title: 'Migrate optimalos to apps/ as read-only',
135
+ priority: 4,
136
+ labels: ['frontend', 'phase-4'],
137
+ description: 'Move optimalos Next.js app into apps/optimalos as a read-only frontend consuming CLI skills',
138
+ },
139
+ {
140
+ title: 'Migrate portfolio-2026 to apps/ as read-only',
141
+ priority: 4,
142
+ labels: ['frontend', 'phase-4'],
143
+ description: 'Move portfolio-2026 Next.js app into apps/portfolio-2026 as a read-only frontend consuming CLI skills',
144
+ },
145
+ {
146
+ title: 'Migrate wes-dashboard to apps/ as read-only',
147
+ priority: 4,
148
+ labels: ['frontend', 'phase-4'],
149
+ description: 'Move wes-dashboard Next.js app into apps/wes-dashboard as a read-only frontend consuming CLI skills',
150
+ },
151
+ {
152
+ title: 'Migrate newsletter-preview to apps/ as read-only',
153
+ priority: 4,
154
+ labels: ['frontend', 'phase-4'],
155
+ description: 'Move newsletter-preview Next.js app into apps/newsletter-preview as a read-only frontend consuming CLI skills',
156
+ },
157
+ ]
158
+
159
+ async function main() {
160
+ console.log(`Seeding kanban board for project: ${PROJECT_SLUG}`)
161
+ console.log(`Tasks to seed: ${tasks.length}\n`)
162
+
163
+ // Fetch existing tasks for idempotency check
164
+ const existing = await getBoard(PROJECT_SLUG)
165
+ const existingTitles = new Set(existing.map(t => t.title))
166
+
167
+ console.log(`Existing tasks on board: ${existing.length}`)
168
+
169
+ let created = 0
170
+ let skipped = 0
171
+
172
+ for (const task of tasks) {
173
+ if (existingTitles.has(task.title)) {
174
+ console.log(` SKIP: "${task.title}" (already exists)`)
175
+ skipped++
176
+ continue
177
+ }
178
+
179
+ try {
180
+ const result = await createTask({
181
+ project_slug: PROJECT_SLUG,
182
+ title: task.title,
183
+ description: task.description,
184
+ priority: task.priority,
185
+ skill_ref: task.skill_ref,
186
+ labels: task.labels,
187
+ })
188
+ console.log(` CREATE: "${result.title}" [P${result.priority}] (${result.id})`)
189
+ created++
190
+ } catch (err) {
191
+ const msg = err instanceof Error ? err.message : String(err)
192
+ console.error(` ERROR: "${task.title}" — ${msg}`)
193
+ }
194
+ }
195
+
196
+ console.log(`\nDone. Created: ${created}, Skipped: ${skipped}`)
197
+ console.log(`Total tasks on board: ${existing.length + created}`)
198
+ }
199
+
200
+ main().catch((err) => {
201
+ console.error('Seed failed:', err)
202
+ process.exit(1)
203
+ })
@@ -0,0 +1,21 @@
1
+ import { getBoardByStatus, listProjects } from '../lib/kanban.js'
2
+
3
+ async function main() {
4
+ console.log('Testing kanban lib...')
5
+
6
+ const projects = await listProjects()
7
+ console.log('Projects:', projects.map(p => p.slug).join(', '))
8
+
9
+ const board = await getBoardByStatus('optimal-cli')
10
+ console.log('Board:')
11
+ for (const [status, tasks] of Object.entries(board)) {
12
+ if (tasks.length > 0) {
13
+ console.log(` ${status}: ${tasks.length}`)
14
+ for (const t of tasks) {
15
+ console.log(` - ${t.title} (claimed: ${t.claimed_by || 'none'})`)
16
+ }
17
+ }
18
+ }
19
+ }
20
+
21
+ main().catch(console.error)
@@ -0,0 +1,33 @@
1
+ ---
2
+ name: audit-financials
3
+ description: Compare staged financials against confirmed income statements and report accuracy per month
4
+ ---
5
+
6
+ ## Purpose
7
+ Verifies data accuracy between `stg_financials_raw` (staged from NetSuite XLSM/CSV) and `confirmed_income_statements` (from NetSuite income statement CSVs). This is the most critical financial health check — every data session should start by running this.
8
+
9
+ ## Inputs
10
+ - **months** (optional): Comma-separated YYYY-MM strings to filter (e.g., `2026-01,2025-12`). Omit for all months.
11
+ - **tolerance** (optional): Dollar tolerance for match detection. Default `1.00`.
12
+
13
+ ## Steps
14
+ 1. Call `lib/returnpro/audit.ts::runAuditComparison(months?, tolerance?)` to fetch and compare data
15
+ 2. Paginate all `stg_financials_raw` rows (amount is TEXT — parseFloat)
16
+ 3. Paginate all `confirmed_income_statements` rows
17
+ 4. Aggregate staging by `account_code|YYYY-MM` key
18
+ 5. Compare each overlapping account: exact match, sign-flip match, or mismatch (within tolerance)
19
+ 6. Compute accuracy = (exactMatch + signFlipMatch) / overlap * 100
20
+
21
+ ## Output
22
+ Per-month table:
23
+
24
+ | Month | Confirmed | Staged | Match | Mismatch | Accuracy |
25
+ |-------|-----------|--------|-------|----------|----------|
26
+ | 2026-01 | 189 | 91 | 83 | 8 | 91.2% |
27
+
28
+ Plus total staging rows and confirmed rows counts.
29
+
30
+ Flag any month below 100% accuracy — investigate mismatches and identify root causes.
31
+
32
+ ## Environment
33
+ Requires: `RETURNPRO_SUPABASE_URL`, `RETURNPRO_SUPABASE_SERVICE_KEY`
@@ -0,0 +1,28 @@
1
+ ---
2
+ name: board-create
3
+ description: Create a new task on the kanban board
4
+ ---
5
+
6
+ ## Purpose
7
+ Adds a new task to the project board. Agents use this to break work into trackable subtasks.
8
+
9
+ ## Inputs
10
+ - **project** (optional): Project slug. Default: `optimal-cli-refactor`
11
+ - **title** (required): Task title
12
+ - **description** (optional): Detailed task description
13
+ - **priority** (optional): 1=urgent, 2=high, 3=normal, 4=low. Default: 3
14
+ - **skill** (optional): Skill that should handle this task (e.g. `/audit-financials`)
15
+ - **labels** (optional): Comma-separated labels
16
+ - **blocked_by** (optional): Comma-separated task IDs that must complete first
17
+
18
+ ## Steps
19
+ 1. Call `lib/kanban.ts::createTask(input)` with provided params
20
+ 2. Return the created task ID and title
21
+
22
+ ## Output
23
+ ```
24
+ Created task: {id} — "{title}" (priority {priority}, status backlog)
25
+ ```
26
+
27
+ ## Environment
28
+ Requires: `OPTIMAL_SUPABASE_URL`, `OPTIMAL_SUPABASE_SERVICE_KEY`
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: board-update
3
+ description: Update a task's status, assignment, or metadata on the kanban board
4
+ ---
5
+
6
+ ## Purpose
7
+ Moves tasks through the kanban lifecycle. Agents call this to claim work, mark completion, or flag blockers.
8
+
9
+ ## Inputs
10
+ - **task_id** (required): UUID of the task to update
11
+ - **status** (optional): New status (backlog, ready, in_progress, blocked, review, done, canceled)
12
+ - **agent** (optional): Agent name to assign (e.g. `claude-code`, `carlos`)
13
+ - **priority** (optional): New priority (1-4)
14
+ - **message** (optional): Log message describing the update
15
+
16
+ ## Steps
17
+ 1. Call `lib/kanban.ts::updateTask(taskId, updates)`
18
+ 2. Call `lib/kanban.ts::logActivity(taskId, { agent, action: 'status_change', message })`
19
+ 3. Return updated task summary
20
+
21
+ ## Output
22
+ ```
23
+ Updated task {id}: status → {status}, agent → {agent}
24
+ ```
25
+
26
+ ## Environment
27
+ Requires: `OPTIMAL_SUPABASE_URL`, `OPTIMAL_SUPABASE_SERVICE_KEY`