plasalid 0.2.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 (153) hide show
  1. package/LICENSE +213 -0
  2. package/README.md +176 -0
  3. package/dist/accounts/taxonomy.d.ts +31 -0
  4. package/dist/accounts/taxonomy.js +189 -0
  5. package/dist/ai/agent.d.ts +43 -0
  6. package/dist/ai/agent.js +155 -0
  7. package/dist/ai/context.d.ts +4 -0
  8. package/dist/ai/context.js +33 -0
  9. package/dist/ai/memory.d.ts +14 -0
  10. package/dist/ai/memory.js +12 -0
  11. package/dist/ai/provider.d.ts +67 -0
  12. package/dist/ai/provider.js +5 -0
  13. package/dist/ai/providers/anthropic.d.ts +5 -0
  14. package/dist/ai/providers/anthropic.js +49 -0
  15. package/dist/ai/providers/index.d.ts +2 -0
  16. package/dist/ai/providers/index.js +12 -0
  17. package/dist/ai/providers/openai-compat.d.ts +5 -0
  18. package/dist/ai/providers/openai-compat.js +147 -0
  19. package/dist/ai/providers/openai.d.ts +5 -0
  20. package/dist/ai/providers/openai.js +147 -0
  21. package/dist/ai/redactor.d.ts +2 -0
  22. package/dist/ai/redactor.js +91 -0
  23. package/dist/ai/sanitize.d.ts +14 -0
  24. package/dist/ai/sanitize.js +25 -0
  25. package/dist/ai/system-prompt.d.ts +13 -0
  26. package/dist/ai/system-prompt.js +174 -0
  27. package/dist/ai/thai-taxonomy-hint.d.ts +8 -0
  28. package/dist/ai/thai-taxonomy-hint.js +22 -0
  29. package/dist/ai/thinking-phrases.d.ts +7 -0
  30. package/dist/ai/thinking-phrases.js +15 -0
  31. package/dist/ai/thinking.d.ts +7 -0
  32. package/dist/ai/thinking.js +15 -0
  33. package/dist/ai/tools/common.d.ts +2 -0
  34. package/dist/ai/tools/common.js +83 -0
  35. package/dist/ai/tools/index.d.ts +8 -0
  36. package/dist/ai/tools/index.js +34 -0
  37. package/dist/ai/tools/ingest.d.ts +2 -0
  38. package/dist/ai/tools/ingest.js +202 -0
  39. package/dist/ai/tools/read.d.ts +2 -0
  40. package/dist/ai/tools/read.js +123 -0
  41. package/dist/ai/tools/reconcile.d.ts +2 -0
  42. package/dist/ai/tools/reconcile.js +227 -0
  43. package/dist/ai/tools/scan.d.ts +2 -0
  44. package/dist/ai/tools/scan.js +24 -0
  45. package/dist/ai/tools/types.d.ts +26 -0
  46. package/dist/ai/tools/types.js +1 -0
  47. package/dist/ai/tools.d.ts +18 -0
  48. package/dist/ai/tools.js +402 -0
  49. package/dist/cli/chat.d.ts +1 -0
  50. package/dist/cli/chat.js +28 -0
  51. package/dist/cli/commands/accounts.d.ts +1 -0
  52. package/dist/cli/commands/accounts.js +86 -0
  53. package/dist/cli/commands/data.d.ts +1 -0
  54. package/dist/cli/commands/data.js +28 -0
  55. package/dist/cli/commands/reconcile.d.ts +2 -0
  56. package/dist/cli/commands/reconcile.js +15 -0
  57. package/dist/cli/commands/revert.d.ts +1 -0
  58. package/dist/cli/commands/revert.js +68 -0
  59. package/dist/cli/commands/scan.d.ts +4 -0
  60. package/dist/cli/commands/scan.js +45 -0
  61. package/dist/cli/commands/status.d.ts +1 -0
  62. package/dist/cli/commands/status.js +22 -0
  63. package/dist/cli/commands/transactions.d.ts +8 -0
  64. package/dist/cli/commands/transactions.js +92 -0
  65. package/dist/cli/commands/undo.d.ts +1 -0
  66. package/dist/cli/commands/undo.js +38 -0
  67. package/dist/cli/commands.d.ts +14 -0
  68. package/dist/cli/commands.js +196 -0
  69. package/dist/cli/format.d.ts +8 -0
  70. package/dist/cli/format.js +109 -0
  71. package/dist/cli/index.d.ts +2 -0
  72. package/dist/cli/index.js +126 -0
  73. package/dist/cli/ink/ChatApp.d.ts +8 -0
  74. package/dist/cli/ink/ChatApp.js +94 -0
  75. package/dist/cli/ink/PromptFrame.d.ts +10 -0
  76. package/dist/cli/ink/PromptFrame.js +11 -0
  77. package/dist/cli/ink/TextInput.d.ts +13 -0
  78. package/dist/cli/ink/TextInput.js +24 -0
  79. package/dist/cli/ink/hooks/useAgent.d.ts +27 -0
  80. package/dist/cli/ink/hooks/useAgent.js +65 -0
  81. package/dist/cli/ink/hooks/useCtrlCExit.d.ts +16 -0
  82. package/dist/cli/ink/hooks/useCtrlCExit.js +43 -0
  83. package/dist/cli/ink/hooks/useFooterText.d.ts +2 -0
  84. package/dist/cli/ink/hooks/useFooterText.js +43 -0
  85. package/dist/cli/ink/hooks/useTextInput.d.ts +32 -0
  86. package/dist/cli/ink/hooks/useTextInput.js +356 -0
  87. package/dist/cli/ink/messages/AssistantMessage.d.ts +3 -0
  88. package/dist/cli/ink/messages/AssistantMessage.js +6 -0
  89. package/dist/cli/ink/messages/ErrorMessage.d.ts +4 -0
  90. package/dist/cli/ink/messages/ErrorMessage.js +6 -0
  91. package/dist/cli/ink/messages/InterruptedMessage.d.ts +1 -0
  92. package/dist/cli/ink/messages/InterruptedMessage.js +6 -0
  93. package/dist/cli/ink/messages/ThinkingLine.d.ts +12 -0
  94. package/dist/cli/ink/messages/ThinkingLine.js +23 -0
  95. package/dist/cli/ink/messages/UserMessage.d.ts +4 -0
  96. package/dist/cli/ink/messages/UserMessage.js +15 -0
  97. package/dist/cli/ink/mount.d.ts +6 -0
  98. package/dist/cli/ink/mount.js +12 -0
  99. package/dist/cli/logo.d.ts +1 -0
  100. package/dist/cli/logo.js +20 -0
  101. package/dist/cli/setup.d.ts +2 -0
  102. package/dist/cli/setup.js +210 -0
  103. package/dist/cli/ux.d.ts +38 -0
  104. package/dist/cli/ux.js +104 -0
  105. package/dist/config.d.ts +21 -0
  106. package/dist/config.js +66 -0
  107. package/dist/currency.d.ts +6 -0
  108. package/dist/currency.js +19 -0
  109. package/dist/db/connection.d.ts +5 -0
  110. package/dist/db/connection.js +45 -0
  111. package/dist/db/encryption.d.ts +11 -0
  112. package/dist/db/encryption.js +45 -0
  113. package/dist/db/helpers.d.ts +16 -0
  114. package/dist/db/helpers.js +45 -0
  115. package/dist/db/queries/account_balance.d.ts +61 -0
  116. package/dist/db/queries/account_balance.js +146 -0
  117. package/dist/db/queries/journal.d.ts +95 -0
  118. package/dist/db/queries/journal.js +204 -0
  119. package/dist/db/queries/search.d.ts +7 -0
  120. package/dist/db/queries/search.js +19 -0
  121. package/dist/db/schema.d.ts +2 -0
  122. package/dist/db/schema.js +95 -0
  123. package/dist/index.d.ts +1 -0
  124. package/dist/index.js +1 -0
  125. package/dist/parser/pdf.d.ts +14 -0
  126. package/dist/parser/pdf.js +40 -0
  127. package/dist/parser/pipeline.d.ts +44 -0
  128. package/dist/parser/pipeline.js +160 -0
  129. package/dist/parser/prompts.d.ts +8 -0
  130. package/dist/parser/prompts.js +20 -0
  131. package/dist/parser/walker.d.ts +8 -0
  132. package/dist/parser/walker.js +42 -0
  133. package/dist/reconciler/pipeline.d.ts +17 -0
  134. package/dist/reconciler/pipeline.js +45 -0
  135. package/dist/reconciler/prompts.d.ts +12 -0
  136. package/dist/reconciler/prompts.js +22 -0
  137. package/dist/scanner/password-store.d.ts +34 -0
  138. package/dist/scanner/password-store.js +83 -0
  139. package/dist/scanner/pdf-unlock.d.ts +17 -0
  140. package/dist/scanner/pdf-unlock.js +48 -0
  141. package/dist/scanner/pdf.d.ts +17 -0
  142. package/dist/scanner/pdf.js +36 -0
  143. package/dist/scanner/pipeline.d.ts +32 -0
  144. package/dist/scanner/pipeline.js +137 -0
  145. package/dist/scanner/prompts.d.ts +8 -0
  146. package/dist/scanner/prompts.js +20 -0
  147. package/dist/scanner/state-machine.d.ts +60 -0
  148. package/dist/scanner/state-machine.js +64 -0
  149. package/dist/scanner/unlock.d.ts +24 -0
  150. package/dist/scanner/unlock.js +122 -0
  151. package/dist/scanner/walker.d.ts +8 -0
  152. package/dist/scanner/walker.js +42 -0
  153. package/package.json +65 -0
package/LICENSE ADDED
@@ -0,0 +1,213 @@
1
+ "Commons Clause" License Condition v1.0
2
+
3
+ The Software is provided to you by the Licensor under the License, as defined
4
+ below, subject to the following condition.
5
+
6
+ Without limiting other conditions in the License, the grant of rights under
7
+ the License will not include, and the License does not grant to you, the right
8
+ to Sell the Software.
9
+
10
+ For purposes of the foregoing, "Sell" means practicing any or all of the
11
+ rights granted to you under the License to provide to third parties, for a fee
12
+ or other consideration (including without limitation fees for hosting or
13
+ consulting/support services related to the Software), a product or service
14
+ whose value derives, entirely or substantially, from the functionality of the
15
+ Software. Any license notice or attribution required by the License must also
16
+ include this Commons Clause License Condition notice.
17
+
18
+ Software: Plasalid
19
+ License: Apache License, Version 2.0
20
+ Licensor: Phureewat A
21
+
22
+ ----------------------------------------------------------------------
23
+
24
+ Apache License
25
+ Version 2.0, January 2004
26
+ http://www.apache.org/licenses/
27
+
28
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
29
+
30
+ 1. Definitions.
31
+
32
+ "License" shall mean the terms and conditions for use, reproduction,
33
+ and distribution as defined by Sections 1 through 9 of this document.
34
+
35
+ "Licensor" shall mean the copyright owner or entity authorized by
36
+ the copyright owner that is granting the License.
37
+
38
+ "Legal Entity" shall mean the union of the acting entity and all
39
+ other entities that control, are controlled by, or are under common
40
+ control with that entity. For the purposes of this definition,
41
+ "control" means (i) the power, direct or indirect, to cause the
42
+ direction or management of such entity, whether by contract or
43
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
44
+ outstanding shares, or (iii) beneficial ownership of such entity.
45
+
46
+ "You" (or "Your") shall mean an individual or Legal Entity
47
+ exercising permissions granted by this License.
48
+
49
+ "Source" form shall mean the preferred form for making modifications,
50
+ including but not limited to software source code, documentation
51
+ source, and configuration files.
52
+
53
+ "Object" form shall mean any form resulting from mechanical
54
+ transformation or translation of a Source form, including but
55
+ not limited to compiled object code, generated documentation,
56
+ and conversions to other media types.
57
+
58
+ "Work" shall mean the work of authorship, whether in Source or
59
+ Object form, made available under the License, as indicated by a
60
+ copyright notice that is included in or attached to the work
61
+ (an example is provided in the Appendix below).
62
+
63
+ "Derivative Works" shall mean any work, whether in Source or Object
64
+ form, that is based on (or derived from) the Work and for which the
65
+ editorial revisions, annotations, elaborations, or other modifications
66
+ represent, as a whole, an original work of authorship. For the purposes
67
+ of this License, Derivative Works shall not include works that remain
68
+ separable from, or merely link (or bind by name) to the interfaces of,
69
+ the Work and Derivative Works thereof.
70
+
71
+ "Contribution" shall mean any work of authorship, including
72
+ the original version of the Work and any modifications or additions
73
+ to that Work or Derivative Works thereof, that is intentionally
74
+ submitted to Licensor for inclusion in the Work by the copyright owner
75
+ or by an individual or Legal Entity authorized to submit on behalf of
76
+ the copyright owner. For the purposes of this definition, "submitted"
77
+ means any form of electronic, verbal, or written communication sent
78
+ to the Licensor or its representatives, including but not limited to
79
+ communication on electronic mailing lists, source code control systems,
80
+ and issue tracking systems that are managed by, or on behalf of, the
81
+ Licensor for the purpose of discussing and improving the Work, but
82
+ excluding communication that is conspicuously marked or otherwise
83
+ designated in writing by the copyright owner as "Not a Contribution."
84
+
85
+ "Contributor" shall mean Licensor and any individual or Legal Entity
86
+ on behalf of whom a Contribution has been received by Licensor and
87
+ subsequently incorporated within the Work.
88
+
89
+ 2. Grant of Copyright License. Subject to the terms and conditions of
90
+ this License, each Contributor hereby grants to You a perpetual,
91
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
92
+ copyright license to reproduce, prepare Derivative Works of,
93
+ publicly display, publicly perform, sublicense, and distribute the
94
+ Work and such Derivative Works in Source or Object form.
95
+
96
+ 3. Grant of Patent License. Subject to the terms and conditions of
97
+ this License, each Contributor hereby grants to You a perpetual,
98
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
99
+ (except as stated in this section) patent license to make, have made,
100
+ use, offer to sell, sell, import, and otherwise transfer the Work,
101
+ where such license applies only to those patent claims licensable
102
+ by such Contributor that are necessarily infringed by their
103
+ Contribution(s) alone or by combination of their Contribution(s)
104
+ with the Work to which such Contribution(s) was submitted. If You
105
+ institute patent litigation against any entity (including a
106
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
107
+ or a Contribution incorporated within the Work constitutes direct
108
+ or contributory patent infringement, then any patent licenses
109
+ granted to You under this License for that Work shall terminate
110
+ as of the date such litigation is filed.
111
+
112
+ 4. Redistribution. You may reproduce and distribute copies of the
113
+ Work or Derivative Works thereof in any medium, with or without
114
+ modifications, and in Source or Object form, provided that You
115
+ meet the following conditions:
116
+
117
+ (a) You must give any other recipients of the Work or
118
+ Derivative Works a copy of this License; and
119
+
120
+ (b) You must cause any modified files to carry prominent notices
121
+ stating that You changed the files; and
122
+
123
+ (c) You must retain, in the Source form of any Derivative Works
124
+ that You distribute, all copyright, patent, trademark, and
125
+ attribution notices from the Source form of the Work,
126
+ excluding those notices that do not pertain to any part of
127
+ the Derivative Works; and
128
+
129
+ (d) If the Work includes a "NOTICE" text file as part of its
130
+ distribution, then any Derivative Works that You distribute must
131
+ include a readable copy of the attribution notices contained
132
+ within such NOTICE file, excluding those notices that do not
133
+ pertain to any part of the Derivative Works, in at least one
134
+ of the following places: within a NOTICE text file distributed
135
+ as part of the Derivative Works; within the Source form or
136
+ documentation, if provided along with the Derivative Works; or,
137
+ within a display generated by the Derivative Works, if and
138
+ wherever such third-party notices normally appear. The contents
139
+ of the NOTICE file are for informational purposes only and
140
+ do not modify the License. You may add Your own attribution
141
+ notices within Derivative Works that You distribute, alongside
142
+ or as an addendum to the NOTICE text from the Work, provided
143
+ that such additional attribution notices cannot be construed
144
+ as modifying the License.
145
+
146
+ You may add Your own copyright statement to Your modifications and
147
+ may provide additional or different license terms and conditions
148
+ for use, reproduction, or distribution of Your modifications, or
149
+ for any such Derivative Works as a whole, provided Your use,
150
+ reproduction, and distribution of the Work otherwise complies with
151
+ the conditions stated in this License.
152
+
153
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
154
+ any Contribution intentionally submitted for inclusion in the Work
155
+ by You to the Licensor shall be under the terms and conditions of
156
+ this License, without any additional terms or conditions.
157
+ Notwithstanding the above, nothing herein shall supersede or modify
158
+ the terms of any separate license agreement you may have executed
159
+ with Licensor regarding such Contributions.
160
+
161
+ 6. Trademarks. This License does not grant permission to use the trade
162
+ names, trademarks, service marks, or product names of the Licensor,
163
+ except as required for describing the origin of the Work and
164
+ reproducing the content of the NOTICE file.
165
+
166
+ 7. Disclaimer of Warranty. Unless required by applicable law or
167
+ agreed to in writing, Licensor provides the Work (and each
168
+ Contributor provides its Contributions) on an "AS IS" BASIS,
169
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
170
+ implied, including, without limitation, any warranties or conditions
171
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
172
+ PARTICULAR PURPOSE. You are solely responsible for determining the
173
+ appropriateness of using or redistributing the Work and assume any
174
+ risks associated with Your exercise of permissions under this License.
175
+
176
+ 8. Limitation of Liability. In no event and under no legal theory,
177
+ whether in tort (including negligence), contract, or otherwise,
178
+ unless required by applicable law (such as deliberate and grossly
179
+ negligent acts) or agreed to in writing, shall any Contributor be
180
+ liable to You for damages, including any direct, indirect, special,
181
+ incidental, or consequential damages of any character arising as a
182
+ result of this License or out of the use or inability to use the
183
+ Work (including but not limited to damages for loss of goodwill,
184
+ work stoppage, computer failure or malfunction, or any and all
185
+ other commercial damages or losses), even if such Contributor
186
+ has been advised of the possibility of such damages.
187
+
188
+ 9. Accepting Warranty or Additional Liability. While redistributing
189
+ the Work or Derivative Works thereof, You may accept Support,
190
+ Warranty, Indemnity, or other liability obligations and/or rights
191
+ consistent with this License. However, in accepting such obligations,
192
+ You may only act on Your own behalf and on Your sole responsibility,
193
+ not on behalf of any other Contributor, and only if You agree to
194
+ indemnify, defend, and hold each Contributor harmless for any
195
+ liability incurred by, or claims asserted against, such Contributor
196
+ by reason of your accepting any such warranty or additional liability.
197
+
198
+ END OF TERMS AND CONDITIONS
199
+
200
+ Copyright 2026 Phureewat A
201
+
202
+ Licensed under the Apache License, Version 2.0 (the "License") with the
203
+ Commons Clause License Condition v1.0 as stated above; you may not use
204
+ this file except in compliance with the License.
205
+ You may obtain a copy of the License at
206
+
207
+ http://www.apache.org/licenses/LICENSE-2.0
208
+
209
+ Unless required by applicable law or agreed to in writing, software
210
+ distributed under the License is distributed on an "AS IS" BASIS,
211
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
212
+ See the License for the specific language governing permissions and
213
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,176 @@
1
+ <h1 align="center">Plasalid</h1>
2
+
3
+ <p align="center">
4
+ <strong>Talk to your money</strong>
5
+ </p>
6
+
7
+ <p align="center">
8
+ A local-first AI that reads every line of your transactions and coaches you the best move.
9
+ </p>
10
+
11
+
12
+ <br />
13
+
14
+ Plasalid lets you actually *talk* to your money. Drop the bank and credit-card statements you already receive into a folder; Plasalid scans them into a queryable, double-entry database on your own machine. Then chat with it — an AI partner that has read every line and tells you, sharply and proactively, what's going on with your money. Local, private, yours.
15
+
16
+ Plasalid exists because in markets like Thailand there's no Plaid: bank data is locked behind statement PDFs, and most people can't afford a real financial advisor either. Plasalid closes both gaps. Your statements become a database, and an AI that knows that database becomes the money coach you'd otherwise have to hire. Your machine, your encryption key, your data.
17
+
18
+ In Thailand, personal finance isn't taught in school. Fee-based advisors are out of reach for most households. The loudest "advice" channels are bank salespeople pitching their employer's products. Over 5 million people are already flagged as non-performing borrowers, and a generation that wants to manage money better has nowhere accessible to learn how. Plasalid's bet is that capable AI changes this — the same intelligence that can read a statement can also explain what the numbers mean, surface what's about to go wrong, and coach someone through real decisions about debt, budget, and savings. At zero marginal cost, in your language, on your own machine.
19
+
20
+ Plasalid can also scales upward in case financial survival isn't your question — help you setup a goal for vacation trip, building an emergency fund, choosing investments, planning a down payment or retirement, working toward the freedom to walk away from a bad job. From getting out of debt to financial freedom, Plasalid grows with you.
21
+
22
+ ## Features
23
+
24
+ ### Your personal money coach
25
+
26
+ - **Sees every balance, every transaction** — Plasalid's chat reads from your bank and credit-card statements, not generic categories. "Where did ฿14k go in March?" gets a specific answer.
27
+ - **Sharp and proactive** — Leads with the insight, not the breakdown. Flags concerning patterns (overdraft trajectory, unusual spending, payments due soon) even if you didn't ask.
28
+ - **Has a point of view** — When you ask "what should I do?", you get a recommendation, not a list of options.
29
+ - **Remembers what matters** — Persists biographical context (family, employer, goals) and per-statement scanning hints across sessions, so each conversation starts smarter than the last.
30
+
31
+ ### A data harness AI can plug into
32
+
33
+ - **The missing aggregator** — In markets without Plaid, there's no bank API that easy to access. Plasalid turns the documents you already receive into a database that machine can read, so the data layer stops being the blocker.
34
+ - **Composable substrate** — Plasalid's local SQLite is plain, queryable double-entry data. Any tool that can read SQLite — Claude Code, MCP servers, your own scripts, dashboards — can build automations, alerts, exporters, or personalized analyses on top, with no further integration work.
35
+ - **No vendor lock, no rate limits** — Standard accounts and journal lines, your encryption key, your machine. Nothing to revoke, throttle, or paywall.
36
+ - **BYO model** — Pick Anthropic (Claude) or any OpenAI-compatible server (Ollama, OpenAI, LM Studio, vLLM, …) at setup time. Local models keep the conversation 100% on your machine.
37
+
38
+ ### Drop documents in, get structured data out
39
+
40
+ - **Encrypted PDFs handled inline** — Statement password-protected? Plasalid prompts you once, then remembers the password (encrypted at rest) under a filename pattern so the next month's statement unlocks silently.
41
+ - **Asks instead of guessing** — Ambiguous row? The scanner pauses and prompts you.
42
+ - **Idempotent scan** — Files are hashed; re-running `plasalid scan` skips what it already scanned. `--force` cascade-deletes prior records before re-scanning.
43
+ - **Learns your statements** — Per-bank scanning hints persist across runs (the AI saves them in a local memory table) so each new statement scans more accurately than the last.
44
+
45
+ ### Correctness, not vibes
46
+
47
+ - **Double-entry bookkeeping** — Every transaction balances enforced by standard double-entry accounting.
48
+ - **Account metadata preserved** — Bank, masked number, statement day, due day, points.
49
+ - **Dates normalized** — ISO Gregorian; Buddhist-Era dates converted automatically.
50
+ - **Reconcile pass** — `plasalid reconcile` surfaces duplicate entries, similar accounts, and unused accounts; merges, renames, and deletes happen only after explicit confirmation. `--dry-run` previews without writing.
51
+
52
+ ### Your data never leaves your machine
53
+
54
+ - **Encrypted local database** — All data stays on your machine in an AES-256 encrypted SQLite database.
55
+ - **PII masking** — Names, national IDs, phones, full account/card numbers scrubbed before anything reaches the AI.
56
+ - **No telemetry. No analytics.** Only outbound traffic is to your configured AI provider.
57
+
58
+
59
+ ## Install
60
+
61
+ ```bash
62
+ npm install -g plasalid
63
+ ```
64
+
65
+ Requires Node ≥ 18.
66
+
67
+ ## Quick Start
68
+
69
+ ```bash
70
+ plasalid setup
71
+ ```
72
+
73
+ Then:
74
+
75
+ 1. Drop any PDFs anywhere under `~/.plasalid/data/`. Subfolders are allowed but not interpreted — the AI infers account type from the document.
76
+ 2. Run `plasalid scan` and answer any clarifying questions inline.
77
+ 3. Run `plasalid` to chat with what was scanned.
78
+
79
+ Other day-to-day commands:
80
+
81
+ - `plasalid scan <regex>` — only scan files whose path matches the regex.
82
+ - `plasalid scan <regex> --force` — re-scan matching files (replaces prior records).
83
+ - `plasalid reconcile --dry-run` — periodically surface duplicate entries and similar accounts; re-run without `--dry-run` to apply fixes interactively.
84
+ - `plasalid revert <regex>` — delete scanned files matching the regex and every journal entry derived from them.
85
+
86
+ ## Commands
87
+
88
+ Run `plasalid --help` to see all available commands.
89
+
90
+ ```bash
91
+ plasalid # Interactive TUI chat with your local data
92
+ plasalid setup # Configure API key, encryption, and data directory
93
+ plasalid data # Open the Plasalid data folder in your OS file explorer
94
+ plasalid accounts # Show the chart of accounts with balances
95
+ plasalid status # Net worth and this-month income/expense totals
96
+ plasalid transactions # List journal lines (filter by --account, --from, --to, --query, --limit)
97
+ plasalid scan [regex] [--force] # Scan new PDFs; --force cascade-deletes prior records before re-scanning
98
+ plasalid revert <regex> # Delete scanned files matching <regex> and their journal entries
99
+ plasalid reconcile [--dry-run] # Review the journal: duplicates, similar accounts, unused accounts (--account, --from, --to also accepted)
100
+ ```
101
+
102
+ ## How It Works
103
+
104
+ ```
105
+ Bank statements · Credit-card statements
106
+
107
+ (drop PDFs)
108
+
109
+ ┌──────────▼──────────┐
110
+ │ ~/.plasalid/data/ │
111
+ └──────────┬──────────┘
112
+
113
+ plasalid scan
114
+
115
+ Claude API (PII-redacted)
116
+
117
+ ┌──────────▼──────────┐
118
+ │ Encrypted DB │◀──── plasalid reconcile
119
+ └──────────┬──────────┘
120
+
121
+ plasalid · chat
122
+ ```
123
+
124
+ Two outbound calls: the AI provider during scan, and the AI provider during chat. Both are PII-redacted. Your financial data is never stored off your machine. No telemetry. No analytics.
125
+
126
+ ## Security & Privacy
127
+
128
+ - All financial data stored locally in `~/.plasalid/db.sqlite`
129
+ - Database encrypted with AES-256 (libsql)
130
+ - Config file stored with `0600` permissions
131
+ - PII redacted before sending to any AI provider
132
+ - Encrypted-PDF passwords are AES-GCM-encrypted inside `db.sqlite` under a filename pattern; never written to disk in plaintext.
133
+ - Only outbound traffic is to your configured AI provider
134
+
135
+ ## Configuration
136
+
137
+ Plasalid stores everything in `~/.plasalid/`:
138
+
139
+ ```
140
+ ~/.plasalid/
141
+ config.json # API keys and preferences (0600 permissions)
142
+ context.md # Persistent personal context
143
+ db.sqlite # Encrypted SQLite database
144
+ data/ # Drop any PDFs here (subfolders allowed; AI classifies)
145
+ ```
146
+
147
+ `db.sqlite` holds the journal, chart of accounts, scan history, persisted long-term memories, and AES-GCM-encrypted PDF passwords keyed by filename pattern. Everything is wrapped in libsql's AES-256 page encryption.
148
+
149
+ ### Environment Variables
150
+
151
+ ```bash
152
+ ANTHROPIC_API_KEY= # Anthropic API key (required when provider is anthropic)
153
+ PLASALID_MODEL= # Model name; default for Anthropic: claude-sonnet-4-6
154
+ PLASALID_PROVIDER= # anthropic | openai-compatible. Default: anthropic
155
+ OPENAI_COMPATIBLE_BASE_URL= # e.g. http://localhost:11434/v1 (Ollama)
156
+ OPENAI_COMPATIBLE_API_KEY= # API key for the OpenAI-compatible server (often unused)
157
+ PLASALID_DB_ENCRYPTION_KEY= # DB encryption passphrase
158
+ PLASALID_DB_PATH= # Default: ~/.plasalid/db.sqlite
159
+ PLASALID_DATA_DIR= # Default: ~/.plasalid/data
160
+ ```
161
+
162
+ ## Contributing
163
+
164
+ ```bash
165
+ git clone https://github.com/phureewat29/plasalid
166
+ cd plasalid
167
+ npm install
168
+ npm run build
169
+ npm link # makes 'plasalid' available globally
170
+ ```
171
+
172
+ ## License
173
+
174
+ Plasalid is released under the [Apache License 2.0 with the Commons Clause](./LICENSE).
175
+
176
+ You're free to use, copy, modify, distribute, and fork it. The Commons Clause adds one restriction: **you may not Sell the Software** — that is, you may not provide a paid product or service whose value derives entirely or substantially from Plasalid's functionality (including paid hosting or support). For commercial-resale rights, contact the copyright holder to negotiate a separate license.
@@ -0,0 +1,31 @@
1
+ export type AccountType = "asset" | "liability" | "income" | "expense" | "equity";
2
+ export type InstitutionKind = "bank" | "card_issuer" | "wallet" | "payment_rail" | "broker" | "crypto_exchange" | "insurer" | "gov" | "telco" | "utility";
3
+ export interface ThaiInstitution {
4
+ code: string;
5
+ label: string;
6
+ kind: InstitutionKind;
7
+ /** Optional disambiguating note for the AI (mergers, rebrands, regulatory status). */
8
+ notes?: string;
9
+ }
10
+ export declare const THAI_BANKS: ThaiInstitution[];
11
+ export declare const THAI_CARD_ISSUERS: ThaiInstitution[];
12
+ export declare const THAI_WALLETS: ThaiInstitution[];
13
+ export declare const THAI_PAYMENT_RAILS: ThaiInstitution[];
14
+ export declare const THAI_BROKERS: ThaiInstitution[];
15
+ export declare const THAI_CRYPTO_EXCHANGES: ThaiInstitution[];
16
+ export declare const THAI_INSURERS: ThaiInstitution[];
17
+ export declare const THAI_GOV: ThaiInstitution[];
18
+ export declare const THAI_UTILITIES: ThaiInstitution[];
19
+ export declare const THAI_TELCOS: ThaiInstitution[];
20
+ export declare const ALL_THAI_INSTITUTIONS: ThaiInstitution[];
21
+ export declare const ACCOUNT_TYPE_DESCRIPTIONS: Record<AccountType, string>;
22
+ export declare const SUGGESTED_ASSET_SUBTYPES: string[];
23
+ export declare const SUGGESTED_LIABILITY_SUBTYPES: string[];
24
+ export declare const SUGGESTED_EXPENSE_SUBTYPES: string[];
25
+ export declare const SUGGESTED_INCOME_SUBTYPES: string[];
26
+ /**
27
+ * Stringified Thai taxonomy block for the scan/reconcile system prompts.
28
+ * Lists known Thai institutions and suggested subtypes so the model picks
29
+ * consistent `bank_name` and `subtype` values across statements.
30
+ */
31
+ export declare function getThaiTaxonomyHint(): string;
@@ -0,0 +1,189 @@
1
+ export const THAI_BANKS = [
2
+ { code: "KBANK", label: "Kasikornbank", kind: "bank" },
3
+ { code: "SCB", label: "Siam Commercial Bank", kind: "bank" },
4
+ { code: "BBL", label: "Bangkok Bank", kind: "bank" },
5
+ { code: "KTB", label: "Krungthai Bank", kind: "bank", notes: "State-owned." },
6
+ { code: "BAY", label: "Krungsri (Bank of Ayudhya)", kind: "bank" },
7
+ { code: "TTB", label: "TMBThanachart Bank", kind: "bank", notes: "Result of TMB + Thanachart merger." },
8
+ { code: "UOB-TH", label: "UOB Thailand", kind: "bank", notes: "Absorbed Citi Thailand's consumer banking (incl. cards) in Nov 2022." },
9
+ { code: "CIMB-TH", label: "CIMB Thai", kind: "bank" },
10
+ { code: "GHB", label: "Government Housing Bank", kind: "bank", notes: "State-owned, mortgage focus." },
11
+ { code: "GSB", label: "Government Savings Bank", kind: "bank", notes: "State-owned." },
12
+ { code: "LH-BANK", label: "Land and Houses Bank", kind: "bank" },
13
+ { code: "KKP", label: "Kiatnakin Phatra Bank", kind: "bank", notes: "Merged 2020 from KK + Phatra." },
14
+ { code: "TISCO", label: "TISCO Bank", kind: "bank" },
15
+ { code: "IBT", label: "Islamic Bank of Thailand", kind: "bank", notes: "State-owned; serves all customers." },
16
+ { code: "ICBC-TH", label: "ICBC (Thai)", kind: "bank", notes: "Subsidiary of ICBC China." },
17
+ { code: "BAAC", label: "Bank for Agriculture and Agricultural Cooperatives", kind: "bank", notes: "State-owned, rural finance." },
18
+ ];
19
+ export const THAI_CARD_ISSUERS = [
20
+ { code: "KTC", label: "Krungthai Card", kind: "card_issuer", notes: "Listed subsidiary of KTB." },
21
+ { code: "AEON", label: "AEON Thana Sinsap", kind: "card_issuer" },
22
+ { code: "FIRSTCHOICE", label: "Krungsri First Choice", kind: "card_issuer" },
23
+ { code: "CITI-TH", label: "Citibank Thailand", kind: "card_issuer", notes: "Consumer cards migrated to UOB-TH in Nov 2022; only historical statements still reference Citi." },
24
+ { code: "AMEX-TH", label: "American Express Thailand", kind: "card_issuer" },
25
+ { code: "CARDX", label: "CardX", kind: "card_issuer", notes: "SCB X consumer-card spinoff; issues cards previously branded SCB." },
26
+ { code: "DINERS", label: "Diners Club Thailand", kind: "card_issuer" },
27
+ { code: "UOB-TH", label: "UOB Thailand (Cards)", kind: "card_issuer", notes: "Same legal entity as the bank UOB-TH; now issues both its own card line (UOB Yolo, UOB Premier) and the migrated Citi consumer cards." },
28
+ ];
29
+ export const THAI_WALLETS = [
30
+ { code: "TRUEMONEY", label: "TrueMoney Wallet", kind: "wallet" },
31
+ { code: "LINEPAY", label: "Rabbit LINE Pay", kind: "wallet" },
32
+ { code: "SHOPEEPAY", label: "ShopeePay", kind: "wallet" },
33
+ { code: "GRABPAY", label: "GrabPay (Thailand)", kind: "wallet" },
34
+ { code: "DOLFIN", label: "Dolfin Wallet", kind: "wallet", notes: "Central Group + JD.com joint venture." },
35
+ { code: "MPAY", label: "mPay", kind: "wallet", notes: "AIS-operated." },
36
+ { code: "PAOTANG", label: "Paotang", kind: "wallet", notes: "Krungthai-operated; government-benefits and tax e-wallet." },
37
+ ];
38
+ export const THAI_PAYMENT_RAILS = [
39
+ { code: "PROMPTPAY", label: "PromptPay", kind: "payment_rail", notes: "National 24/7 interbank rail; appears on transfer slips, not an issuer." },
40
+ ];
41
+ export const THAI_BROKERS = [
42
+ { code: "INNOVESTX", label: "InnovestX Securities", kind: "broker", notes: "Former SCBS; SCBX subsidiary." },
43
+ { code: "BLS", label: "Bualuang Securities", kind: "broker", notes: "BBL subsidiary." },
44
+ { code: "KS", label: "Kasikorn Securities", kind: "broker", notes: "KBANK subsidiary." },
45
+ { code: "KGI-TH", label: "KGI Securities (Thailand)", kind: "broker" },
46
+ { code: "MAYBANK-SEC", label: "Maybank Securities (Thailand)", kind: "broker" },
47
+ { code: "ASP", label: "Asia Plus Securities", kind: "broker" },
48
+ { code: "TISCO-SEC", label: "TISCO Securities", kind: "broker" },
49
+ { code: "KSS", label: "Krungsri Securities", kind: "broker", notes: "BAY subsidiary." },
50
+ { code: "KKPS", label: "Kiatnakin Phatra Securities", kind: "broker", notes: "KKP subsidiary." },
51
+ { code: "LH-SEC", label: "Land and Houses Securities", kind: "broker" },
52
+ { code: "FINANSIA", label: "Finansia Syrus Securities", kind: "broker" },
53
+ { code: "YUANTA-TH", label: "Yuanta Securities (Thailand)", kind: "broker" },
54
+ { code: "DBSVICKERS", label: "DBS Vickers Securities (Thailand)", kind: "broker" },
55
+ { code: "KTBST", label: "Krungthai Xspring Securities", kind: "broker", notes: "Formerly KTBST; KTB-affiliated." },
56
+ ];
57
+ export const THAI_CRYPTO_EXCHANGES = [
58
+ { code: "BITKUB", label: "Bitkub Exchange", kind: "crypto_exchange", notes: "SEC-licensed; dominant market share." },
59
+ { code: "UPBIT-TH", label: "Upbit Thailand", kind: "crypto_exchange", notes: "SEC-licensed; subsidiary of South Korean Upbit." },
60
+ { code: "ORBIX", label: "Orbix Trade", kind: "crypto_exchange", notes: "Former Satang Pro; rebranded under KBank ownership." },
61
+ { code: "GULF-BINANCE", label: "Binance Thailand (Gulf Binance)", kind: "crypto_exchange", notes: "Binance + Gulf Innova JV; SEC-licensed." },
62
+ { code: "KUCOIN-TH", label: "KuCoin Thailand", kind: "crypto_exchange", notes: "Former ERX; rebranded 2025 after KuCoin acquisition." },
63
+ { code: "WAANX", label: "WaanX", kind: "crypto_exchange", notes: "SEC-licensed." },
64
+ { code: "TDX", label: "Thai Digital Assets Exchange", kind: "crypto_exchange", notes: "SET Group subsidiary." },
65
+ { code: "GMO-Z-EX", label: "Z.com EX (GMO-Z.com)", kind: "crypto_exchange", notes: "Japanese GMO subsidiary." },
66
+ { code: "ZIPMEX", label: "Zipmex", kind: "crypto_exchange", notes: "Defunct since Nov 2023; statements only appear in historical files." },
67
+ ];
68
+ export const THAI_INSURERS = [
69
+ // Life
70
+ { code: "AIA-TH", label: "AIA Thailand", kind: "insurer", notes: "Life; market leader." },
71
+ { code: "MUANG-THAI-LIFE", label: "Muang Thai Life Insurance", kind: "insurer", notes: "Life." },
72
+ { code: "THAI-LIFE", label: "Thai Life Insurance", kind: "insurer", notes: "Life; listed (TLI)." },
73
+ { code: "FWD-TH", label: "FWD Thailand", kind: "insurer", notes: "Life." },
74
+ { code: "ALLIANZ-AYUDHYA-LIFE", label: "Allianz Ayudhya Assurance", kind: "insurer", notes: "Life; Allianz + BAY JV." },
75
+ { code: "KTAXA", label: "Krungthai-AXA Life", kind: "insurer", notes: "Life; KTB + AXA JV." },
76
+ { code: "BANGKOK-LIFE", label: "Bangkok Life Assurance", kind: "insurer", notes: "Life." },
77
+ { code: "CHUBB-SAMAGGI", label: "Chubb Samaggi Insurance", kind: "insurer", notes: "Life and non-life." },
78
+ { code: "GENERALI-TH", label: "Generali Thailand", kind: "insurer", notes: "Life." },
79
+ // Non-life
80
+ { code: "DHIPAYA", label: "Dhipaya Insurance", kind: "insurer", notes: "Non-life." },
81
+ { code: "BANGKOK-INS", label: "Bangkok Insurance", kind: "insurer", notes: "Non-life." },
82
+ { code: "ALLIANZ-AYUDHYA-GENERAL", label: "Allianz Ayudhya General Insurance", kind: "insurer", notes: "Non-life." },
83
+ { code: "VIRIYAH", label: "Viriyah Insurance", kind: "insurer", notes: "Non-life; #1 motor insurer." },
84
+ { code: "TOKIO-MARINE", label: "Tokio Marine (Thailand)", kind: "insurer", notes: "Non-life." },
85
+ ];
86
+ export const THAI_GOV = [
87
+ { code: "REVDEPT", label: "Revenue Department (กรมสรรพากร)", kind: "gov", notes: "Income tax, VAT, excise." },
88
+ { code: "SSO", label: "Social Security Office (สำนักงานประกันสังคม)", kind: "gov", notes: "Employee + self-employed social security contributions." },
89
+ { code: "BOT", label: "Bank of Thailand", kind: "gov", notes: "Central bank; rarely on consumer statements outside of regulatory letters." },
90
+ { code: "MOF", label: "Ministry of Finance", kind: "gov" },
91
+ { code: "LDT", label: "Department of Lands (กรมที่ดิน)", kind: "gov", notes: "Land registration, property tax." },
92
+ { code: "CUSTOMS", label: "Customs Department (กรมศุลกากร)", kind: "gov", notes: "Import/export duties." },
93
+ ];
94
+ export const THAI_UTILITIES = [
95
+ { code: "MEA", label: "Metropolitan Electricity Authority (กฟน.)", kind: "utility", notes: "Electricity for Bangkok, Nonthaburi, Samut Prakan." },
96
+ { code: "PEA", label: "Provincial Electricity Authority (กฟภ.)", kind: "utility", notes: "Electricity for the rest of Thailand outside MEA's area." },
97
+ { code: "MWA", label: "Metropolitan Waterworks Authority (กปน.)", kind: "utility", notes: "Water for Bangkok, Nonthaburi, Samut Prakan." },
98
+ { code: "PWA", label: "Provincial Waterworks Authority (กปภ.)", kind: "utility", notes: "Water for the rest of Thailand." },
99
+ { code: "EGAT", label: "Electricity Generating Authority of Thailand (กฟผ.)", kind: "utility", notes: "Power generation; rarely appears on consumer bills directly." },
100
+ ];
101
+ export const THAI_TELCOS = [
102
+ { code: "AIS", label: "Advanced Info Service (AIS)", kind: "telco" },
103
+ { code: "TRUE-CORP", label: "True Corporation", kind: "telco", notes: "Merged entity of True + dtac since March 2023." },
104
+ { code: "TRUEMOVE", label: "TrueMove H", kind: "telco", notes: "Brand retained under TRUE-CORP per NBTC ruling." },
105
+ { code: "DTAC", label: "dtac", kind: "telco", notes: "Brand retained under TRUE-CORP per NBTC ruling." },
106
+ { code: "NT", label: "National Telecom (NT)", kind: "telco", notes: "Former TOT; state-owned, minimal consumer presence." },
107
+ ];
108
+ export const ALL_THAI_INSTITUTIONS = [
109
+ ...THAI_BANKS,
110
+ ...THAI_CARD_ISSUERS,
111
+ ...THAI_WALLETS,
112
+ ...THAI_PAYMENT_RAILS,
113
+ ...THAI_BROKERS,
114
+ ...THAI_CRYPTO_EXCHANGES,
115
+ ...THAI_INSURERS,
116
+ ...THAI_GOV,
117
+ ...THAI_TELCOS,
118
+ ...THAI_UTILITIES,
119
+ ];
120
+ export const ACCOUNT_TYPE_DESCRIPTIONS = {
121
+ asset: "Bank accounts, cash, prepaid wallets, receivables.",
122
+ liability: "Credit cards, loans, mortgages, money the user owes.",
123
+ income: "Salary, side income, dividends, refunds.",
124
+ expense: "Spending categories (food, transport, utilities, etc.).",
125
+ equity: "Owner's equity / opening balance equity (for reconciliation).",
126
+ };
127
+ export const SUGGESTED_ASSET_SUBTYPES = [
128
+ "bank",
129
+ "cash",
130
+ "wallet",
131
+ "prepaid_card",
132
+ "brokerage",
133
+ "crypto",
134
+ "receivable",
135
+ ];
136
+ export const SUGGESTED_LIABILITY_SUBTYPES = [
137
+ "credit_card",
138
+ "home_loan",
139
+ "auto_loan",
140
+ "personal_loan",
141
+ "student_loan",
142
+ "revolving",
143
+ "deferred_income",
144
+ ];
145
+ export const SUGGESTED_EXPENSE_SUBTYPES = [
146
+ "food",
147
+ "transport",
148
+ "utilities",
149
+ "rent",
150
+ "housing",
151
+ "healthcare",
152
+ "entertainment",
153
+ "shopping",
154
+ "subscriptions",
155
+ "education",
156
+ "travel",
157
+ "fees_and_interest",
158
+ "tax",
159
+ "insurance",
160
+ "other",
161
+ ];
162
+ export const SUGGESTED_INCOME_SUBTYPES = [
163
+ "salary",
164
+ "bonus",
165
+ "freelance",
166
+ "interest",
167
+ "dividend",
168
+ "refund",
169
+ "other",
170
+ ];
171
+ /**
172
+ * Stringified Thai taxonomy block for the scan/reconcile system prompts.
173
+ * Lists known Thai institutions and suggested subtypes so the model picks
174
+ * consistent `bank_name` and `subtype` values across statements.
175
+ */
176
+ export function getThaiTaxonomyHint() {
177
+ const institutions = ALL_THAI_INSTITUTIONS
178
+ .map(i => `${i.code} (${i.label}, ${i.kind})${i.notes ? ` — ${i.notes}` : ""}`)
179
+ .join("\n");
180
+ return [
181
+ `Known Thai institutions:`,
182
+ institutions,
183
+ ``,
184
+ `Suggested asset subtypes: ${SUGGESTED_ASSET_SUBTYPES.join(", ")}`,
185
+ `Suggested liability subtypes: ${SUGGESTED_LIABILITY_SUBTYPES.join(", ")}`,
186
+ `Suggested expense subtypes: ${SUGGESTED_EXPENSE_SUBTYPES.join(", ")}`,
187
+ `Suggested income subtypes: ${SUGGESTED_INCOME_SUBTYPES.join(", ")}`,
188
+ ].join("\n");
189
+ }
@@ -0,0 +1,43 @@
1
+ import type Database from "libsql";
2
+ import { type ScanPromptOptions, type ReconcilePromptOptions } from "./system-prompt.js";
3
+ import { type AgentExecutionContext } from "./tools/index.js";
4
+ import type { NormalizedMessage } from "./provider.js";
5
+ export type ProgressCallback = (event: {
6
+ phase: "tool" | "responding";
7
+ toolName?: string;
8
+ toolCount: number;
9
+ elapsedMs: number;
10
+ }) => void;
11
+ export declare class AbortedError extends Error {
12
+ constructor();
13
+ }
14
+ /**
15
+ * Conversational chat used by the Ink TUI. Reuses conversation_history for context
16
+ * continuity, redacts PII on the way out, restores it on the way in for display.
17
+ */
18
+ export declare function handleChatMessage(db: Database.Database, userMessage: string, onProgress?: ProgressCallback, signal?: AbortSignal): Promise<string>;
19
+ /**
20
+ * Scan-time agent loop. Caller supplies the initial user message (which carries
21
+ * the PDF as a content block) and a AgentExecutionContext that scopes the file
22
+ * id, scanner version, and interactivity for ask_user.
23
+ */
24
+ export declare function runScanAgent(opts: {
25
+ db: Database.Database;
26
+ initialMessages: NormalizedMessage[];
27
+ prompt: ScanPromptOptions;
28
+ agentCtx: AgentExecutionContext;
29
+ onProgress?: ProgressCallback;
30
+ signal?: AbortSignal;
31
+ }): Promise<string>;
32
+ /**
33
+ * Reconcile-time agent loop. Walks the existing journal with the reconcile
34
+ * tool profile (read tools + write/merge/delete primitives).
35
+ */
36
+ export declare function runReconcileAgent(opts: {
37
+ db: Database.Database;
38
+ initialMessages: NormalizedMessage[];
39
+ prompt: ReconcilePromptOptions;
40
+ agentCtx: AgentExecutionContext;
41
+ onProgress?: ProgressCallback;
42
+ signal?: AbortSignal;
43
+ }): Promise<string>;