$npx -y skills add ya5huk/findash --skill fetch-bank-dataUse when the user says "fetch bank data", "pull from bank", "fetch hapoalim", "fetch cal", "fetch credit card", "pull from cal", or any morning-equivalent. Pulls fresh transactions + balances from Bank Hapoalim and Cal via israeli-bank-scrapers, reasons about each source's data
| 1 | # fetch-bank-data |
| 2 | |
| 3 | You pull fresh data from the user's customer-facing bank + credit-card sites and stage reasoned files in local `inbox/staging/fetched/`. **Sync owns ingestion** — your job ends when the pairs are staged; sync ingests them into SQLite and deletes them after commit. Fetched data never goes to Drive: SQLite is its only persistence. You do not touch SQLite (beyond read-only queries), do not call the dashboard, do not send Telegram. Adding more issuers later (`max`, `isracard`, `amex`) is just one more `[section]` in `.secrets/findash` + one more mapping line below. |
| 4 | |
| 5 | ## Where things live |
| 6 | |
| 7 | - Scraper wrapper: `scripts/fetch_bank.js` (parameterized by `--company`) |
| 8 | - Node deps: `scripts/package.json` (`israeli-bank-scrapers` + `puppeteer`; run `cd scripts && npm install` once) |
| 9 | - Credentials: the `[hapoalim]` / `[cal]` sections of `.secrets/findash` (chmod 600) |
| 10 | - Per-company Chromium profile (persists trusted-device cookies + soft anti-bot state): `~/.cache/findash/chromium-profile/<companyId>/` |
| 11 | - Staging dir — the fetch→sync handoff: `inbox/staging/fetched/` |
| 12 | - SQLite (read-only here, for date-range + own-account vocabulary): `data/finance.db` |
| 13 | - Doc-type shapes sync will see: [`docs/doc-types/full-statements.md`](../../docs/doc-types/full-statements.md) (`bank_api_dump`, `bank_api_notes`, `cal_api_dump`, `cal_api_notes`) |
| 14 | |
| 15 | ## Sources |
| 16 | |
| 17 | | company | scraper `companyId` | secrets section | env vars consumed by the script | |
| 18 | |------------|---------------------|-----------------|------------------------------------------| |
| 19 | | `hapoalim` | `hapoalim` | `[hapoalim]` | `HAPOALIM_USER_CODE`, `HAPOALIM_PASSWORD` | |
| 20 | | `cal` | `visaCal` | `[cal]` | `CAL_USERNAME`, `CAL_PASSWORD` | |
| 21 | |
| 22 | Both sections are `key=value` lines under their `[…]` header in `.secrets/findash`. Hapoalim uses `user_code=` and `password=`. Cal uses `username=` and `password=` (not `user_code` — matches Cal's login UI and the library's credential shape). |
| 23 | |
| 24 | Both also accept `START_DATE` from env (ISO `YYYY-MM-DD`) — the script falls back to 60 days back if unset. |
| 25 | |
| 26 | ## Flow |
| 27 | |
| 28 | Run **all configured sources in parallel** unless the user explicitly named one ("fetch cal", "pull from hapoalim" → just that one). For each source: |
| 29 | |
| 30 | ### 1. Skip if no secrets |
| 31 | |
| 32 | If a source has no credentials (no `[<source>]` section in `.secrets/findash`), silently skip that source and name it in the final summary. A one-bank user still gets a working skill. |
| 33 | |
| 34 | ### 2. Pick a start date |
| 35 | |
| 36 | Query SQLite for the latest transaction date on accounts at that institution: |
| 37 | |
| 38 | ```sql |
| 39 | SELECT MAX(t.date) |
| 40 | FROM transactions t JOIN accounts a ON a.id = t.account_id |
| 41 | WHERE a.institution = ?; -- 'Bank Hapoalim' or 'Cal' |
| 42 | ``` |
| 43 | |
| 44 | Subtract a few days (3–5) for overlap safety — sync dedups, so re-sending recent rows is harmless. If the query returns NULL (no data yet for that institution), fall back to 60 days back. Cal-specific note: the first-ever Cal fetch has no `accounts` row at all — institution will not match anything; that's fine, the same 60-day fallback applies. |
| 45 | |
| 46 | ### 3. Run the scraper |
| 47 | |
| 48 | The scraper reads credentials from `.secrets/findash` **itself** — never put them on the command line. That keeps the unattended `claude -p` run allowlist-safe (the command stays a clean `node scripts/fetch_bank.js …` prefix) and your password out of the transcript. Pass the step-2 window as a flag and read the JSON straight from stdout — **no `>` redirection** (the unattended run can't write to arbitrary paths): |
| 49 | |
| 50 | ```bash |
| 51 | node scripts/fetch_bank.js --company=hapoalim --start-date=YYYY-MM-DD |
| 52 | ``` |
| 53 | |
| 54 | `--company=visaCal` for Cal. Substitute the step-2 start date for `YYYY-MM-DD` (the skill auto-computes one — this flag is just the override). Omit `--start-date` to default to 60 days back. Read the result from the command's stdout. The script exits: |
| 55 | - `0` — success, full library result as JSON on stdout |
| 56 | - `1` — scrape ran but `success: false`, `errorType` / `errorMessage` on stderr |
| 57 | - `2` — missing creds / Node too old / Puppeteer launch failure |
| 58 | |
| 59 | On `1` or `2`: surface the error verbatim, recommend `--setup` (see "When things break" below), and proceed to the next source. **Don't write any files for the failing source.** |
| 60 | |
| 61 | ### 4. Reason about the data |
| 62 | |
| 63 | This is the whole point of the skill. The script returns raw library output; you interpret it. Different vocabulary per source kind: |
| 64 | |
| 65 | **Bank-account observations (Hapoalim):** |
| 66 | |
| 67 | - **Round-trip** — same-magnitude opposite-sign txns within ~14 days, sa |