tablewalk 0.0.2 → 0.0.4

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.
package/README.md CHANGED
@@ -1,38 +1,35 @@
1
1
  # tablewalk
2
2
 
3
- **Follow the data.**
3
+ **[Follow the data.](https://tablewalk.pages.dev)** — [tablewalk.pages.dev](https://tablewalk.pages.dev)
4
4
 
5
5
  Most database tools are SQL clients that happen to render a grid. tablewalk
6
- starts somewhere else: a foreign key is a place you can go.
6
+ starts somewhere else: a foreign key is a place you can go. Open a row and it
7
+ shows you every row elsewhere that points at it — the orders on a customer, the
8
+ customer on an order — so you follow the data instead of writing joins to reach
9
+ it. The same machinery answers over **MCP**, so a coding agent sees what you
10
+ see ([jump to setup](#for-coding-agents-mcp)).
7
11
 
8
12
  ```bash
9
- npx tablewalk --demo # a sample database, built and opened
10
- npx tablewalk # asks in the browser
11
- npx tablewalk mydata.sqlite
12
- npx tablewalk postgres://user:pass@localhost/appdb
13
- npx tablewalk mydata.sqlite --export md >> CLAUDE.md # the schema, as a committed brief
14
- npx tablewalk postgres://… --lint --fail-on high # what the shape costs, for CI
15
- npx tablewalk --config ./tablewalk.json --diff staging # is staging the same shape?
13
+ npx tablewalk --demo # build a sample database and open it — an instant try
14
+ npx tablewalk mydata.sqlite # or your own: SQLite, Postgres, MySQL
15
+ npx tablewalk # or nothing, and the page asks for a connection
16
16
  ```
17
17
 
18
- No config file, no saved connection, no schema to define first. Point it at a
19
- database and it reads the catalog on connect.
18
+ No config, no build step, no schema to define first it reads the catalog on
19
+ connect. **Read-only** until you turn editing on, per connection. Runs from
20
+ `npx`, a [Docker image](#docker-and-a-desktop-app), or a desktop app.
20
21
 
21
- Or point it at nothing. `npx tablewalk` on its own starts the server and the
22
- page asks for a connection — a form that takes either a URL or its parts, tests
23
- it before keeping it, and can read the password from an environment variable or
24
- the keychain instead of the string. A connection added that way lasts for the
25
- session; "Save to file…" writes it down, with any typed password moved to the
26
- keychain rather than into the file.
22
+ `npx tablewalk` with no target opens a page that asks for a connection: a form
23
+ that takes a URL or its parts, tests it before keeping it, and reads the
24
+ password from an environment variable or the keychain rather than the string.
25
+ The print-and-exit commands need a target, though there is nothing for
26
+ `--export`, `--lint`, `--diff` or `--mcp` to read without one:
27
27
 
28
- The commands that print and exit still need a target, because there is nothing
29
- for `--export`, `--lint` or `--diff` to read without one, and `--mcp` does too:
30
- an agent on stdio has no dialog to fill in.
31
-
32
- Or `npx tablewalk --demo` — it builds a small sample database (the eleven-table
33
- one the screenshots use) in a temp directory and opens it, so there is
34
- something to walk before you have a database of your own. Nothing of yours is
35
- touched, and the file is thrown away.
28
+ ```bash
29
+ npx tablewalk mydata.sqlite --export md >> CLAUDE.md # the schema, as a committed brief
30
+ npx tablewalk postgres://… --lint --fail-on high # what the shape costs, for CI
31
+ npx tablewalk --config ./tablewalk.json --diff staging # is staging the same shape?
32
+ ```
36
33
 
37
34
  ## The idea
38
35
 
@@ -201,39 +198,45 @@ npx tablewalk --mcp --config ./tablewalk.json # speaks MCP on stdio
201
198
  { "mcpServers": { "tablewalk": { "command": "npx", "args": ["tablewalk", "--mcp", "--config", "./tablewalk.json"] } } }
202
199
  ```
203
200
 
204
- Twenty-three tools. Reading everywhere: `refresh` (re-read the catalog after a migration the schema is cached per open, and the error for an unknown table says when to call it), `schema_summary` (orientation in a few hundred
205
- tokens — hubs, event tables, the deepest reference chain), `tables`, `table`
206
- (columns, DDL, and foreign keys in **both** directions), `find` (where a name
207
- lives, across tables and columns), `query` (the query language, whose errors
208
- carry suggestions and the table's real columns what lets an agent fix a
209
- mistake in one retry), `breakdown` (counts and sums grouped by something —
210
- `invoice count by month invoice_date`), `sql` (read-only, for whatever the
211
- language still cannot say), `record`
212
- (one row, its human name, and everything that points at it), `explain` (the
213
- planner's account of a statement without running it — feed a `query` answer's
214
- `sql` back verbatim), `profile` (what a column actually holds: null share,
215
- distinct count, range, the values that repeat), `lint` (what the shape will
216
- cost: keyless tables, unindexed references, naive timestamps),
217
- `change_impact` (what breaks if you change this table or column asked
218
- *before* the migration is written: what points at it under which delete rule,
219
- whether those columns are NOT NULL, whether an index leads on them, and how
220
- many rows actually hold null, which is the fact that decides whether an
221
- `ALTER` succeeds), `diff` (whether
222
- two connections are still the same shape), `order` (what has to exist before
223
- what, and the reverse for teardown), `fixture` (a real row and the graph it belongs to, ready to replay into a test database), and `connections`. Every result carries a deep link the human beside the agent
224
- can open in the UI, and says what it cost: wall time and rows read.
225
-
226
- Writing only where your config says so. `insert`, `update`, `delete`,
227
- `insert_graph`, `delete_graph` and `revert` are offered only when a connection
228
- is marked `"writable": true` a read-only server does not list tools it would
229
- refuse. `revert` undoes the last write behind the same two-call `confirm` gate
230
- `delete` uses; a `sql` write is recorded but marked unrevertible, because
231
- arbitrary SQL has no general inverse.
232
- `insert` answers with the row as the database stored it, defaults filled and
233
- key assigned; `delete` answers with its impact first — every table pointing at
234
- the row, counts, and `ON DELETE` rules — removing nothing until called again
235
- with `confirm: true`. Write answers carry `rowsInserted` / `rowsWritten` /
236
- `rowsDeleted`.
201
+ Or point it at a database instead of a config file: `"args": ["tablewalk",
202
+ "--mcp", "--db", "postgres://localhost/app"]`. **Twenty-three tools**
203
+ seventeen read-only, and six writes offered only when a connection is
204
+ `"writable": true`. Every answer carries a deep link the human beside the
205
+ agent can open in the UI, and says what it cost in rows and milliseconds.
206
+
207
+ **Reading**
208
+
209
+ | tool | what it answers |
210
+ |---|---|
211
+ | `schema_summary` | orientation in a few hundred tokens hubs, event tables, the deepest reference chain |
212
+ | `tables` / `table` | the list; then a table's columns, DDL, and foreign keys in **both** directions |
213
+ | `find` | where a name lives, across every table and column |
214
+ | `query` | the query language; its errors carry suggestions and the table's real columns, so a mistake is fixed in one retry |
215
+ | `breakdown` | counts and sums grouped by something `invoice count by month invoice_date` |
216
+ | `record` | one row, its human name, and everything that points at it |
217
+ | `sql` | read-only, for whatever the language cannot say |
218
+ | `explain` | the planner's account of a statement without running it — feed a `query` answer's `sql` back |
219
+ | `profile` | what a column actually holds: null share, distinct count, range, the values that repeat |
220
+ | `lint` | what the shape will cost: keyless tables, unindexed references, naive timestamps |
221
+ | `change_impact` | what breaks if you change a table or column asked *before* the migration is written |
222
+ | `diff` | whether two connections are still the same shape |
223
+ | `order` | what has to exist before what, and the reverse for teardown |
224
+ | `fixture` | a real row and the graph it belongs to, ready to replay into a test database |
225
+ | `refresh` | re-read the catalog after a migration |
226
+ | `connections` | list them; the call to start with |
227
+
228
+ **Writing** offered only when a connection is marked `"writable": true`; a
229
+ read-only server does not list tools it would refuse.
230
+
231
+ | tool | what it does |
232
+ |---|---|
233
+ | `insert` | one row; answers with what the database stored, defaults filled and key assigned |
234
+ | `update` | one row, by its primary key |
235
+ | `delete` | reports its impact first — every table pointing at the row, counts, `ON DELETE` rules — and removes nothing until called again with `confirm: true` |
236
+ | `insert_graph` / `delete_graph` | several related rows in one all-or-nothing transaction |
237
+ | `revert` | undo the last write, behind the same two-call `confirm` gate; a raw `sql` write is recorded but marked unrevertible, since it has no general inverse |
238
+
239
+ Write answers carry `rowsInserted` / `rowsWritten` / `rowsDeleted`.
237
240
 
238
241
  A fixture is a graph, so `insert_graph` takes one: several related rows in one
239
242
  transaction, where either all of them land or none does. A row that cannot
@@ -493,10 +496,22 @@ is your own machine; bind wider and it is whoever else can reach you.
493
496
 
494
497
  ## Docker, and a desktop app
495
498
 
496
- There is a Dockerfile and an Electron app in `desktop/`. Both are covered in
497
- [DEPLOY.md](DEPLOY.md), including the two things a container changes about the
499
+ **Container** `ghcr.io/rbaljinder/tablewalk`. It reads its connections from a
500
+ mounted `tablewalk.json` and holds no configuration of its own:
501
+
502
+ ```bash
503
+ docker run --rm -p 127.0.0.1:4111:4111 -v "$PWD:/config:ro" ghcr.io/rbaljinder/tablewalk
504
+ ```
505
+
506
+ [DEPLOY.md](DEPLOY.md) covers the two things a container changes about the
498
507
  security model — binding `0.0.0.0` inside while publishing only to the host's
499
- loopback, and `--allowed-host` for reaching it by any name but `localhost`.
508
+ loopback, and `--allowed-host` for reaching it by a name other than
509
+ `localhost`.
510
+
511
+ **Desktop** — a native app (macOS, Windows, Linux) is attached to each
512
+ [GitHub release](https://github.com/rbaljinder/tablewalk/releases). The builds
513
+ are currently unsigned, so the first open needs an "open anyway" past the OS
514
+ warning; `npx tablewalk` and the container need no such thing.
500
515
 
501
516
  ## Keyboard
502
517
 
@@ -2170,7 +2170,22 @@ async function readJson(req) {
2170
2170
  async function openBrowser(url) {
2171
2171
  const { spawn } = await import('node:child_process');
2172
2172
  const cmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
2173
- spawn(cmd, [url], { detached: true, stdio: 'ignore' }).unref();
2173
+ try {
2174
+ const child = spawn(cmd, [url], { detached: true, stdio: 'ignore' });
2175
+ /* A headless box — a container, a server, an SSH session — has no
2176
+ `xdg-open`, and `spawn` reports that as an `error` event, not a throw.
2177
+ With no handler Node re-raises it as an uncaught exception and takes the
2178
+ whole process down — so `--open`, and `--demo` which implies it, crashed
2179
+ exactly where it was most useful: the container serving the demo. A
2180
+ missing opener is not fatal. The server is already listening and the URL
2181
+ is already printed; opening it is a convenience, not a requirement. */
2182
+ child.once('error', () => { });
2183
+ child.unref();
2184
+ }
2185
+ catch {
2186
+ /* Some platforms throw synchronously instead. Same non-answer: the server
2187
+ stays up, the URL is on screen, the reader opens it themselves. */
2188
+ }
2174
2189
  }
2175
2190
  /* Started only when run, not when imported.
2176
2191
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tablewalk",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Follow the data. A database browser built around the walk: a foreign key is a place you can go.",
5
5
  "license": "MIT",
6
6
  "author": "Baljinder Randhawa",