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
@@ -1,60 +0,0 @@
1
- /**
2
- * Transaction & Staging Batch Deletion — Safe Preview and Execute
3
- *
4
- * Provides safe batch deletion of transactions (OptimalOS) and staging
5
- * financials (ReturnPro) with preview mode defaulting to dryRun=true.
6
- *
7
- * Tables:
8
- * - transactions → OptimalOS Supabase (getSupabase('optimal'))
9
- * - stg_financials_raw → ReturnPro Supabase (getSupabase('returnpro'))
10
- *
11
- * Columns:
12
- * transactions: id, user_id, date, description, amount, category, source, stamp_match_type, created_at
13
- * stg_financials_raw: id, account_code, account_name, amount (TEXT), month (YYYY-MM), source, user_id, created_at
14
- */
15
- import 'dotenv/config';
16
- export interface DeleteBatchOptions {
17
- table: 'transactions' | 'stg_financials_raw';
18
- userId?: string;
19
- filters: {
20
- dateFrom?: string;
21
- dateTo?: string;
22
- source?: string;
23
- category?: string;
24
- accountCode?: string;
25
- month?: string;
26
- };
27
- dryRun?: boolean;
28
- }
29
- export interface DeleteBatchResult {
30
- table: string;
31
- deletedCount: number;
32
- dryRun: boolean;
33
- filters: Record<string, string>;
34
- }
35
- export interface PreviewResult {
36
- table: string;
37
- matchCount: number;
38
- sample: Array<Record<string, unknown>>;
39
- groupedCounts: Record<string, number>;
40
- }
41
- /**
42
- * Preview what would be deleted without touching any data.
43
- *
44
- * Returns:
45
- * - matchCount: total rows matching the filters
46
- * - sample: first 10 matching rows
47
- * - groupedCounts: row counts grouped by `source` (transactions) or `account_code` (staging)
48
- */
49
- export declare function previewBatch(opts: DeleteBatchOptions): Promise<PreviewResult>;
50
- /**
51
- * Delete matching rows in batch — or preview them without deleting (dryRun=true).
52
- *
53
- * Safety: dryRun defaults to TRUE. Caller must explicitly pass dryRun=false
54
- * to execute an actual deletion.
55
- *
56
- * In dryRun mode: counts matching rows and returns deletedCount=0.
57
- * In execute mode: issues a Supabase DELETE with the same filters and returns
58
- * the number of rows deleted.
59
- */
60
- export declare function deleteBatch(opts: DeleteBatchOptions): Promise<DeleteBatchResult>;
@@ -1,203 +0,0 @@
1
- /**
2
- * Transaction & Staging Batch Deletion — Safe Preview and Execute
3
- *
4
- * Provides safe batch deletion of transactions (OptimalOS) and staging
5
- * financials (ReturnPro) with preview mode defaulting to dryRun=true.
6
- *
7
- * Tables:
8
- * - transactions → OptimalOS Supabase (getSupabase('optimal'))
9
- * - stg_financials_raw → ReturnPro Supabase (getSupabase('returnpro'))
10
- *
11
- * Columns:
12
- * transactions: id, user_id, date, description, amount, category, source, stamp_match_type, created_at
13
- * stg_financials_raw: id, account_code, account_name, amount (TEXT), month (YYYY-MM), source, user_id, created_at
14
- */
15
- import 'dotenv/config';
16
- import { getSupabase } from '../supabase.js';
17
- // =============================================================================
18
- // INTERNAL HELPERS
19
- // =============================================================================
20
- /**
21
- * Return the correct Supabase client for the given table.
22
- */
23
- function getClientForTable(table) {
24
- return table === 'transactions'
25
- ? getSupabase('optimal')
26
- : getSupabase('returnpro');
27
- }
28
- /**
29
- * Apply the shared set of filters to a Supabase query builder.
30
- * Works for both SELECT and DELETE queries because both are PostgREST filters.
31
- *
32
- * For `stg_financials_raw`:
33
- * - dateFrom / dateTo are ignored (use `month` instead)
34
- * - month is applied as an eq filter on the `month` column
35
- * - accountCode is applied as an eq filter on `account_code`
36
- *
37
- * For `transactions`:
38
- * - dateFrom / dateTo are applied as gte/lte on `date`
39
- * - source is applied as an eq filter on `source`
40
- * - category is applied as an eq filter on `category`
41
- * - userId is applied as an eq filter on `user_id`
42
- */
43
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
- function applyFilters(query, table, userId, filters) {
45
- let q = query;
46
- if (table === 'transactions') {
47
- if (userId)
48
- q = q.eq('user_id', userId);
49
- if (filters.dateFrom)
50
- q = q.gte('date', filters.dateFrom);
51
- if (filters.dateTo)
52
- q = q.lte('date', filters.dateTo);
53
- if (filters.source)
54
- q = q.eq('source', filters.source);
55
- if (filters.category)
56
- q = q.eq('category', filters.category);
57
- }
58
- else {
59
- // stg_financials_raw
60
- if (userId)
61
- q = q.eq('user_id', userId);
62
- if (filters.month)
63
- q = q.eq('month', filters.month);
64
- if (filters.accountCode)
65
- q = q.eq('account_code', filters.accountCode);
66
- if (filters.source)
67
- q = q.eq('source', filters.source);
68
- }
69
- return q;
70
- }
71
- /**
72
- * Serialize active filters for the result record (human-readable).
73
- */
74
- function serializeFilters(table, userId, filters) {
75
- const out = {};
76
- if (userId)
77
- out.user_id = userId;
78
- if (table === 'transactions') {
79
- if (filters.dateFrom)
80
- out.dateFrom = filters.dateFrom;
81
- if (filters.dateTo)
82
- out.dateTo = filters.dateTo;
83
- if (filters.source)
84
- out.source = filters.source;
85
- if (filters.category)
86
- out.category = filters.category;
87
- }
88
- else {
89
- if (filters.month)
90
- out.month = filters.month;
91
- if (filters.accountCode)
92
- out.accountCode = filters.accountCode;
93
- if (filters.source)
94
- out.source = filters.source;
95
- }
96
- return out;
97
- }
98
- // =============================================================================
99
- // PUBLIC FUNCTIONS
100
- // =============================================================================
101
- /**
102
- * Preview what would be deleted without touching any data.
103
- *
104
- * Returns:
105
- * - matchCount: total rows matching the filters
106
- * - sample: first 10 matching rows
107
- * - groupedCounts: row counts grouped by `source` (transactions) or `account_code` (staging)
108
- */
109
- export async function previewBatch(opts) {
110
- const { table, userId, filters } = opts;
111
- const supabase = getClientForTable(table);
112
- // --- Count matching rows ---
113
- const countQuery = supabase
114
- .from(table)
115
- .select('*', { count: 'exact', head: true });
116
- const countQueryWithFilters = applyFilters(countQuery, table, userId, filters);
117
- const { count, error: countError } = await countQueryWithFilters;
118
- if (countError) {
119
- throw new Error(`previewBatch count error on ${table}: ${countError.message}`);
120
- }
121
- const matchCount = count ?? 0;
122
- // --- Fetch sample rows (first 10) ---
123
- const sampleQuery = supabase
124
- .from(table)
125
- .select('*')
126
- .limit(10);
127
- const sampleQueryWithFilters = applyFilters(sampleQuery, table, userId, filters);
128
- const { data: sampleData, error: sampleError } = await sampleQueryWithFilters;
129
- if (sampleError) {
130
- throw new Error(`previewBatch sample error on ${table}: ${sampleError.message}`);
131
- }
132
- const sample = (sampleData ?? []);
133
- // --- Grouped counts ---
134
- // Group by `source` for transactions, `account_code` for staging
135
- const groupCol = table === 'transactions' ? 'source' : 'account_code';
136
- const groupQuery = supabase
137
- .from(table)
138
- .select(groupCol);
139
- const groupQueryWithFilters = applyFilters(groupQuery, table, userId, filters);
140
- const { data: groupData, error: groupError } = await groupQueryWithFilters;
141
- if (groupError) {
142
- throw new Error(`previewBatch group error on ${table}: ${groupError.message}`);
143
- }
144
- const groupedCounts = {};
145
- for (const row of (groupData ?? [])) {
146
- const key = row[groupCol] ?? '(unknown)';
147
- groupedCounts[key] = (groupedCounts[key] ?? 0) + 1;
148
- }
149
- return {
150
- table,
151
- matchCount,
152
- sample,
153
- groupedCounts,
154
- };
155
- }
156
- /**
157
- * Delete matching rows in batch — or preview them without deleting (dryRun=true).
158
- *
159
- * Safety: dryRun defaults to TRUE. Caller must explicitly pass dryRun=false
160
- * to execute an actual deletion.
161
- *
162
- * In dryRun mode: counts matching rows and returns deletedCount=0.
163
- * In execute mode: issues a Supabase DELETE with the same filters and returns
164
- * the number of rows deleted.
165
- */
166
- export async function deleteBatch(opts) {
167
- const { table, userId, filters } = opts;
168
- const dryRun = opts.dryRun ?? true; // safe by default
169
- const supabase = getClientForTable(table);
170
- const serializedFilters = serializeFilters(table, userId, filters);
171
- if (dryRun) {
172
- // Count matching rows without deleting
173
- const countQuery = supabase
174
- .from(table)
175
- .select('*', { count: 'exact', head: true });
176
- const countQueryWithFilters = applyFilters(countQuery, table, userId, filters);
177
- const { count, error } = await countQueryWithFilters;
178
- if (error) {
179
- throw new Error(`deleteBatch dry-run count error on ${table}: ${error.message}`);
180
- }
181
- return {
182
- table,
183
- deletedCount: 0,
184
- dryRun: true,
185
- filters: serializedFilters,
186
- };
187
- }
188
- // Execute deletion
189
- const deleteQuery = supabase
190
- .from(table)
191
- .delete({ count: 'exact' });
192
- const deleteQueryWithFilters = applyFilters(deleteQuery, table, userId, filters);
193
- const { count, error } = await deleteQueryWithFilters;
194
- if (error) {
195
- throw new Error(`deleteBatch execute error on ${table}: ${error.message}`);
196
- }
197
- return {
198
- table,
199
- deletedCount: count ?? 0,
200
- dryRun: false,
201
- filters: serializedFilters,
202
- };
203
- }
@@ -1,43 +0,0 @@
1
- /**
2
- * Transaction Ingestion — CSV Parsing & Deduplication
3
- *
4
- * Ported from OptimalOS:
5
- * - /home/optimal/optimalos/app/api/csv/ingest/route.ts
6
- * - /home/optimal/optimalos/lib/csv/upload.ts
7
- * - /home/optimal/optimalos/lib/stamp-engine/normalizers/
8
- * - /home/optimal/optimalos/lib/stamp-engine/format-detector.ts
9
- *
10
- * Reads a CSV file from disk, auto-detects bank format, parses into
11
- * normalized transactions, deduplicates against existing rows in Supabase,
12
- * and batch-inserts new records into the `transactions` table.
13
- */
14
- export interface RawTransaction {
15
- date: string;
16
- description: string;
17
- amount: number;
18
- originalCategory?: string;
19
- transactionType?: string;
20
- postDate?: string;
21
- balance?: number;
22
- extendedDetails?: string;
23
- merchantAddress?: string;
24
- }
25
- export type BankFormat = 'chase_checking' | 'chase_credit' | 'discover' | 'amex' | 'generic' | 'unknown';
26
- export interface IngestResult {
27
- inserted: number;
28
- skipped: number;
29
- failed: number;
30
- errors: string[];
31
- format: BankFormat;
32
- }
33
- /**
34
- * Ingest transactions from a CSV file.
35
- *
36
- * 1. Read & detect format
37
- * 2. Parse into normalized transactions
38
- * 3. Deduplicate against existing rows (by hash)
39
- * 4. Batch-insert new rows into `transactions`
40
- *
41
- * @returns count of inserted, skipped (duplicate), and failed rows
42
- */
43
- export declare function ingestTransactions(filePath: string, userId: string): Promise<IngestResult>;