dirsql 0.3.73__tar.gz → 0.3.75__tar.gz

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 (141) hide show
  1. {dirsql-0.3.73 → dirsql-0.3.75}/Cargo.lock +1 -1
  2. {dirsql-0.3.73 → dirsql-0.3.75}/PKG-INFO +1 -1
  3. {dirsql-0.3.73 → dirsql-0.3.75}/docs/AGENTS.md +1 -1
  4. {dirsql-0.3.73 → dirsql-0.3.75}/docs/getting-started.md +18 -18
  5. {dirsql-0.3.73/packages/rust → dirsql-0.3.75}/docs/howto/columns-from-paths.md +5 -5
  6. {dirsql-0.3.73/packages/python → dirsql-0.3.75}/docs/howto/define-tables.md +4 -4
  7. {dirsql-0.3.73 → dirsql-0.3.75}/docs/howto/embed.md +1 -1
  8. {dirsql-0.3.73/packages/python → dirsql-0.3.75}/docs/howto/extract-from-contents.md +5 -5
  9. {dirsql-0.3.73/packages/rust → dirsql-0.3.75}/docs/howto/react-to-changes.md +1 -1
  10. {dirsql-0.3.73/packages/rust → dirsql-0.3.75}/docs/howto/search-by-meaning.md +4 -4
  11. {dirsql-0.3.73/packages/rust → dirsql-0.3.75}/docs/howto/skip-files.md +3 -3
  12. {dirsql-0.3.73/packages/rust → dirsql-0.3.75}/docs/reference/cli.md +16 -22
  13. dirsql-0.3.75/docs/reference/columns.md +80 -0
  14. {dirsql-0.3.73/packages/rust → dirsql-0.3.75}/docs/reference/config.md +4 -4
  15. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/Cargo.toml +1 -1
  16. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/AGENTS.md +1 -1
  17. {dirsql-0.3.73/packages/rust → dirsql-0.3.75/packages/python}/docs/getting-started.md +18 -18
  18. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/howto/columns-from-paths.md +5 -5
  19. {dirsql-0.3.73/packages/rust → dirsql-0.3.75/packages/python}/docs/howto/define-tables.md +4 -4
  20. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/howto/embed.md +1 -1
  21. {dirsql-0.3.73/packages/rust → dirsql-0.3.75/packages/python}/docs/howto/extract-from-contents.md +5 -5
  22. {dirsql-0.3.73 → dirsql-0.3.75/packages/python}/docs/howto/react-to-changes.md +1 -1
  23. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/howto/search-by-meaning.md +4 -4
  24. {dirsql-0.3.73 → dirsql-0.3.75/packages/python}/docs/howto/skip-files.md +3 -3
  25. {dirsql-0.3.73 → dirsql-0.3.75/packages/python}/docs/reference/cli.md +16 -22
  26. dirsql-0.3.75/packages/python/docs/reference/columns.md +80 -0
  27. {dirsql-0.3.73 → dirsql-0.3.75/packages/python}/docs/reference/config.md +4 -4
  28. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/e2e-attestation.json +2 -2
  29. {dirsql-0.3.73/packages/python → dirsql-0.3.75/packages/rust}/docs/getting-started.md +18 -18
  30. {dirsql-0.3.73 → dirsql-0.3.75/packages/rust}/docs/howto/columns-from-paths.md +5 -5
  31. {dirsql-0.3.73 → dirsql-0.3.75/packages/rust}/docs/howto/define-tables.md +4 -4
  32. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/docs/howto/embed.md +1 -1
  33. {dirsql-0.3.73 → dirsql-0.3.75/packages/rust}/docs/howto/extract-from-contents.md +5 -5
  34. {dirsql-0.3.73/packages/python → dirsql-0.3.75/packages/rust}/docs/howto/react-to-changes.md +1 -1
  35. {dirsql-0.3.73 → dirsql-0.3.75/packages/rust}/docs/howto/search-by-meaning.md +4 -4
  36. {dirsql-0.3.73/packages/python → dirsql-0.3.75/packages/rust}/docs/howto/skip-files.md +3 -3
  37. {dirsql-0.3.73/packages/python → dirsql-0.3.75/packages/rust}/docs/reference/cli.md +16 -22
  38. dirsql-0.3.75/packages/rust/docs/reference/columns.md +80 -0
  39. {dirsql-0.3.73/packages/python → dirsql-0.3.75/packages/rust}/docs/reference/config.md +4 -4
  40. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/src/bin/dirsql.rs +18 -23
  41. dirsql-0.3.75/packages/rust/src/cli/init.rs +95 -0
  42. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/src/cli/mod.rs +11 -0
  43. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/src/config.rs +28 -28
  44. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/src/db.rs +1 -1
  45. dirsql-0.3.75/packages/rust/src/default_config.toml +3 -0
  46. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/src/lib.rs +24 -24
  47. dirsql-0.3.73/docs/reference/columns.md +0 -68
  48. dirsql-0.3.73/packages/python/docs/reference/columns.md +0 -68
  49. dirsql-0.3.73/packages/rust/docs/reference/columns.md +0 -68
  50. dirsql-0.3.73/packages/rust/src/cli/init.rs +0 -207
  51. {dirsql-0.3.73 → dirsql-0.3.75}/Cargo.toml +0 -0
  52. {dirsql-0.3.73 → dirsql-0.3.75}/README.md +0 -0
  53. {dirsql-0.3.73 → dirsql-0.3.75}/dirsql/__init__.py +0 -0
  54. {dirsql-0.3.73 → dirsql-0.3.75}/dirsql/_async.py +0 -0
  55. {dirsql-0.3.73 → dirsql-0.3.75}/dirsql/_dirsql.pyi +0 -0
  56. {dirsql-0.3.73 → dirsql-0.3.75}/dirsql/cli/__init__.py +0 -0
  57. {dirsql-0.3.73 → dirsql-0.3.75}/dirsql/cli/binary_path.py +0 -0
  58. {dirsql-0.3.73 → dirsql-0.3.75}/dirsql/cli/is_windows.py +0 -0
  59. {dirsql-0.3.73 → dirsql-0.3.75}/dirsql/cli/main.py +0 -0
  60. {dirsql-0.3.73 → dirsql-0.3.75}/dirsql/cli/resolve_config_extensions.py +0 -0
  61. {dirsql-0.3.73 → dirsql-0.3.75}/dirsql/py.typed +0 -0
  62. {dirsql-0.3.73 → dirsql-0.3.75}/dirsql/resolve_config_extensions.py +0 -0
  63. {dirsql-0.3.73 → dirsql-0.3.75}/dirsql/resolve_extension.py +0 -0
  64. {dirsql-0.3.73 → dirsql-0.3.75}/docs/.claude/CLAUDE.md +0 -0
  65. {dirsql-0.3.73 → dirsql-0.3.75}/docs/.vitepress/config.ts +0 -0
  66. {dirsql-0.3.73 → dirsql-0.3.75}/docs/.vitepress/theme/index.ts +0 -0
  67. {dirsql-0.3.73 → dirsql-0.3.75}/docs/.vitepress/theme/lang.ts +0 -0
  68. {dirsql-0.3.73 → dirsql-0.3.75}/docs/explanation.md +0 -0
  69. {dirsql-0.3.73 → dirsql-0.3.75}/docs/howto/load-extension.md +0 -0
  70. {dirsql-0.3.73 → dirsql-0.3.75}/docs/howto/persist.md +0 -0
  71. {dirsql-0.3.73 → dirsql-0.3.75}/docs/index.md +0 -0
  72. {dirsql-0.3.73 → dirsql-0.3.75}/docs/migrations.md +0 -0
  73. {dirsql-0.3.73 → dirsql-0.3.75}/docs/package.json +0 -0
  74. {dirsql-0.3.73 → dirsql-0.3.75}/docs/playwright.config.ts +0 -0
  75. {dirsql-0.3.73 → dirsql-0.3.75}/docs/pnpm-lock.yaml +0 -0
  76. {dirsql-0.3.73 → dirsql-0.3.75}/docs/pnpm-workspace.yaml +0 -0
  77. {dirsql-0.3.73 → dirsql-0.3.75}/docs/reference/hooks.md +0 -0
  78. {dirsql-0.3.73 → dirsql-0.3.75}/docs/reference/http-api.md +0 -0
  79. {dirsql-0.3.73 → dirsql-0.3.75}/docs/reference/sdk.md +0 -0
  80. {dirsql-0.3.73 → dirsql-0.3.75}/docs/tests/integration/home.spec.ts +0 -0
  81. {dirsql-0.3.73 → dirsql-0.3.75}/docs/tests/integration/language-flag.spec.ts +0 -0
  82. {dirsql-0.3.73 → dirsql-0.3.75}/docs/tests/integration/sidebar.spec.ts +0 -0
  83. {dirsql-0.3.73 → dirsql-0.3.75}/docs/tests/unit/config.test.ts +0 -0
  84. {dirsql-0.3.73 → dirsql-0.3.75}/docs/tests/unit/lang.test.ts +0 -0
  85. {dirsql-0.3.73 → dirsql-0.3.75}/docs/vitest.config.ts +0 -0
  86. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/README.md +0 -0
  87. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/conftest.py +0 -0
  88. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/.claude/CLAUDE.md +0 -0
  89. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/.vitepress/config.ts +0 -0
  90. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/.vitepress/theme/index.ts +0 -0
  91. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
  92. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/explanation.md +0 -0
  93. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/howto/load-extension.md +0 -0
  94. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/howto/persist.md +0 -0
  95. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/index.md +0 -0
  96. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/migrations.md +0 -0
  97. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/package.json +0 -0
  98. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/playwright.config.ts +0 -0
  99. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/pnpm-lock.yaml +0 -0
  100. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/pnpm-workspace.yaml +0 -0
  101. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/reference/hooks.md +0 -0
  102. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/reference/http-api.md +0 -0
  103. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/reference/sdk.md +0 -0
  104. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/tests/integration/home.spec.ts +0 -0
  105. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/tests/integration/language-flag.spec.ts +0 -0
  106. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/tests/integration/sidebar.spec.ts +0 -0
  107. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/tests/unit/config.test.ts +0 -0
  108. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/tests/unit/lang.test.ts +0 -0
  109. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/docs/vitest.config.ts +0 -0
  110. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/src/lib.rs +0 -0
  111. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/tests/__init__.py +0 -0
  112. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/tests/binding/__init__.py +0 -0
  113. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/tests/conftest.py +0 -0
  114. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/tests/e2e/__init__.py +0 -0
  115. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/tests/integration/__init__.py +0 -0
  116. {dirsql-0.3.73 → dirsql-0.3.75}/packages/python/tests/smoke/__init__.py +0 -0
  117. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/Cargo.toml +0 -0
  118. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/README.md +0 -0
  119. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/benches/db_bench.rs +0 -0
  120. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/benches/differ_bench.rs +0 -0
  121. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/benches/matcher_bench.rs +0 -0
  122. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/benches/scanner_bench.rs +0 -0
  123. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/docs/explanation.md +0 -0
  124. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/docs/howto/load-extension.md +0 -0
  125. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/docs/howto/persist.md +0 -0
  126. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/docs/index.md +0 -0
  127. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/docs/migrations.md +0 -0
  128. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/docs/reference/hooks.md +0 -0
  129. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/docs/reference/http-api.md +0 -0
  130. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/docs/reference/sdk.md +0 -0
  131. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/src/cli/execute.rs +0 -0
  132. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/src/cli/router.rs +0 -0
  133. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/src/cli/serialize.rs +0 -0
  134. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/src/cli/server.rs +0 -0
  135. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/src/command.rs +0 -0
  136. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/src/differ.rs +0 -0
  137. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/src/matcher.rs +0 -0
  138. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/src/persist.rs +0 -0
  139. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/src/scanner.rs +0 -0
  140. {dirsql-0.3.73 → dirsql-0.3.75}/packages/rust/src/watcher.rs +0 -0
  141. {dirsql-0.3.73 → dirsql-0.3.75}/pyproject.toml +0 -0
@@ -500,7 +500,7 @@ dependencies = [
500
500
 
501
501
  [[package]]
502
502
  name = "dirsql-py-ext"
503
- version = "0.3.73"
503
+ version = "0.3.75"
504
504
  dependencies = [
505
505
  "dirsql",
506
506
  "pyo3",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dirsql
3
- Version: 0.3.73
3
+ Version: 0.3.75
4
4
  Summary: Ephemeral SQL index over a local directory
5
5
  Keywords: sql,filesystem,directory,sqlite,index
6
6
  Author: Kevin Scott
@@ -57,7 +57,7 @@ migrated -- a page survives only if a slot wants its content):
57
57
  `dirsql` in an application.
58
58
  - **Reference** (`reference/`) -- CLI flags and defaults; the complete
59
59
  `.dirsql.toml` schema; the command hook contract (placeholders, stdout
60
- protocol, exit codes, timeouts); virtual columns and glob captures; the
60
+ protocol, exit codes, timeouts); stat columns and glob captures; the
61
61
  HTTP API; the SDK page (`reference/sdk.md`, one page with
62
62
  Python/TypeScript/Rust code-groups -- the sole SDK home); plus the
63
63
  Migrations include (`migrations.md`).
@@ -112,30 +112,30 @@ through `jq` to pretty-print. Now select some columns:
112
112
  ```bash
113
113
  curl -s http://localhost:7117/query \
114
114
  -H 'content-type: application/json' \
115
- -d '{"sql":"SELECT _path, _size FROM files ORDER BY _path"}' \
115
+ -d '{"sql":"SELECT path, size FROM files ORDER BY path"}' \
116
116
  | jq
117
117
  ```
118
118
 
119
119
  ```json
120
120
  [
121
121
  {
122
- "_path": "notes/alice/ideas.md",
123
- "_size": 52
122
+ "path": "notes/alice/ideas.md",
123
+ "size": 52
124
124
  },
125
125
  {
126
- "_path": "notes/alice/welcome.md",
127
- "_size": 66
126
+ "path": "notes/alice/welcome.md",
127
+ "size": 66
128
128
  },
129
129
  {
130
- "_path": "notes/bob/reading-list.md",
131
- "_size": 41
130
+ "path": "notes/bob/reading-list.md",
131
+ "size": 41
132
132
  }
133
133
  ]
134
134
  ```
135
135
 
136
- `_path` and `_size` are two of the built-in file columns `dirsql` collects
137
- for every file — see [virtual columns](./reference/columns.md#virtual-columns)
138
- for the full list. (The `_size` values are byte counts; they match the
136
+ `path` and `size` are two of the built-in file columns `dirsql` collects
137
+ for every file — see [stat columns](./reference/columns.md#stat-columns)
138
+ for the full list. (The `size` values are byte counts; they match the
139
139
  output above because you pasted the files exactly.)
140
140
 
141
141
  You have a working SQL database over your files. Next, teach it the
@@ -152,7 +152,7 @@ In your second terminal, still inside `my-notes`, create a `.dirsql.toml`:
152
152
  ```bash
153
153
  cat > .dirsql.toml <<'EOF'
154
154
  [[table]]
155
- ddl = "CREATE TABLE notes (author TEXT, _basename TEXT, _size INTEGER)"
155
+ ddl = "CREATE TABLE notes (author TEXT, basename TEXT, size INTEGER)"
156
156
  glob = "notes/{author}/*.md"
157
157
  EOF
158
158
  ```
@@ -192,25 +192,25 @@ terminal:
192
192
  ```bash
193
193
  curl -s http://localhost:7117/query \
194
194
  -H 'content-type: application/json' \
195
- -d '{"sql":"SELECT author, _basename, _size FROM notes ORDER BY author, _basename"}' \
195
+ -d '{"sql":"SELECT author, basename, size FROM notes ORDER BY author, basename"}' \
196
196
  | jq
197
197
  ```
198
198
 
199
199
  ```json
200
200
  [
201
201
  {
202
- "_basename": "ideas.md",
203
- "_size": 52,
202
+ "basename": "ideas.md",
203
+ "size": 52,
204
204
  "author": "alice"
205
205
  },
206
206
  {
207
- "_basename": "welcome.md",
208
- "_size": 66,
207
+ "basename": "welcome.md",
208
+ "size": 66,
209
209
  "author": "alice"
210
210
  },
211
211
  {
212
- "_basename": "reading-list.md",
213
- "_size": 41,
212
+ "basename": "reading-list.md",
213
+ "size": 41,
214
214
  "author": "bob"
215
215
  }
216
216
  ]
@@ -18,7 +18,7 @@ Capture both directory levels in `.dirsql.toml`:
18
18
 
19
19
  ```toml
20
20
  [[table]]
21
- ddl = "CREATE TABLE photos (year TEXT, month TEXT, _basename TEXT)"
21
+ ddl = "CREATE TABLE photos (year TEXT, month TEXT, basename TEXT)"
22
22
  glob = "photos/{year}/{month}/*.jpg"
23
23
  ```
24
24
 
@@ -30,11 +30,11 @@ within one path segment) are in
30
30
  ## 2. Query the captured columns
31
31
 
32
32
  ```bash
33
- dirsql query "SELECT year, month, _basename FROM photos ORDER BY year, month"
33
+ dirsql query "SELECT year, month, basename FROM photos ORDER BY year, month"
34
34
  ```
35
35
 
36
36
  ```json
37
- [{"_basename":"beach.jpg","month":"05","year":"2024"},{"_basename":"hike.jpg","month":"11","year":"2024"},{"_basename":"snow.jpg","month":"01","year":"2025"}]
37
+ [{"basename":"beach.jpg","month":"05","year":"2024"},{"basename":"hike.jpg","month":"11","year":"2024"},{"basename":"snow.jpg","month":"01","year":"2025"}]
38
38
  ```
39
39
 
40
40
  Captures are real SQL columns, so aggregation works:
@@ -49,8 +49,8 @@ dirsql query "SELECT year, COUNT(*) AS photos FROM photos GROUP BY year"
49
49
 
50
50
  ## Going further
51
51
 
52
- - Captures combine freely with [virtual columns](../reference/columns.md#virtual-columns)
53
- (`_basename` above) — both are filesystem facts merged onto every row.
52
+ - Captures combine freely with [stat columns](../reference/columns.md#stat-columns)
53
+ (`basename` above) — both are filesystem facts merged onto every row.
54
54
  - The [tutorial](../getting-started.md) walks the same idea with an
55
55
  `{author}` capture, starting from zero.
56
56
  - When the value you need lives inside the file rather than in its path,
@@ -13,14 +13,14 @@ directory you want to index, create a `.dirsql.toml` with one
13
13
 
14
14
  ```toml
15
15
  [[table]]
16
- ddl = "CREATE TABLE posts (_path TEXT, _size INTEGER, _mtime INTEGER)"
16
+ ddl = "CREATE TABLE posts (path TEXT, size INTEGER, mtime INTEGER)"
17
17
  glob = "posts/**/*.md"
18
18
  ```
19
19
 
20
20
  - `glob` selects the files: every `.md` under `posts/`, at any depth,
21
21
  relative to the directory containing the config.
22
22
  - `ddl` is a plain SQLite `CREATE TABLE` naming the columns you want. Here
23
- all three are [virtual columns](../reference/columns.md#virtual-columns) —
23
+ all three are [stat columns](../reference/columns.md#stat-columns) —
24
24
  filesystem facts `dirsql` computes for every file. Facts are opt-in by
25
25
  DDL: only the ones you declare become columns.
26
26
 
@@ -29,11 +29,11 @@ glob = "posts/**/*.md"
29
29
  Each matched file is one row:
30
30
 
31
31
  ```bash
32
- dirsql query "SELECT _path, _size FROM posts ORDER BY _path"
32
+ dirsql query "SELECT path, size FROM posts ORDER BY path"
33
33
  ```
34
34
 
35
35
  ```json
36
- [{"_path":"posts/2024/hello.md","_size":21},{"_path":"posts/2025/again.md","_size":55}]
36
+ [{"path":"posts/2024/hello.md","size":21},{"path":"posts/2025/again.md","size":55}]
37
37
  ```
38
38
 
39
39
  Files that don't match the glob (a `README.txt` next to `posts/`, say) are
@@ -35,7 +35,7 @@ comments/t2/c1.json # {"body": "following up", "author": "alice"}
35
35
 
36
36
  Unlike a config-file table, a programmatic table takes an `extract`
37
37
  callback — your code reads each matched file and returns its rows, with
38
- [glob captures and virtual columns](../reference/columns.md) merged on
38
+ [glob captures and stat columns](../reference/columns.md) merged on
39
39
  automatically (here, `{thread}` from the path):
40
40
 
41
41
  ::: code-group
@@ -18,7 +18,7 @@ stdout works. With [`jq`](https://jqlang.org/):
18
18
 
19
19
  ```toml
20
20
  [[table]]
21
- ddl = "CREATE TABLE books (title TEXT, author TEXT, year INTEGER, _path TEXT)"
21
+ ddl = "CREATE TABLE books (title TEXT, author TEXT, year INTEGER, path TEXT)"
22
22
  glob = "books/*.json"
23
23
  on-file = "jq -c '[{title, author, year}]' {path}"
24
24
  ```
@@ -32,14 +32,14 @@ by every hook.
32
32
  ## 2. Query the extracted columns
33
33
 
34
34
  ```bash
35
- dirsql query "SELECT title, author, year, _path FROM books ORDER BY year"
35
+ dirsql query "SELECT title, author, year, path FROM books ORDER BY year"
36
36
  ```
37
37
 
38
38
  ```json
39
- [{"_path":"books/bleak-house.json","author":"Charles Dickens","title":"Bleak House","year":1852},{"_path":"books/middlemarch.json","author":"George Eliot","title":"Middlemarch","year":1871}]
39
+ [{"path":"books/bleak-house.json","author":"Charles Dickens","title":"Bleak House","year":1852},{"path":"books/middlemarch.json","author":"George Eliot","title":"Middlemarch","year":1871}]
40
40
  ```
41
41
 
42
- Filesystem facts are still merged onto every row — `_path` above comes from
42
+ Filesystem facts are still merged onto every row — `path` above comes from
43
43
  `dirsql`, not from `jq`. When the command emits a key that collides with a
44
44
  fact, the command wins
45
45
  ([precedence](../reference/columns.md#precedence)).
@@ -51,7 +51,7 @@ row per line, slurp it:
51
51
 
52
52
  ```toml
53
53
  [[table]]
54
- ddl = "CREATE TABLE events (event TEXT, user TEXT, _path TEXT)"
54
+ ddl = "CREATE TABLE events (event TEXT, user TEXT, path TEXT)"
55
55
  glob = "logs/*.jsonl"
56
56
  on-file = "jq -c -s '.' {path}"
57
57
  ```
@@ -33,7 +33,7 @@ The stream delivers the resulting row change:
33
33
 
34
34
  ```
35
35
  event: row
36
- data: {"action":"insert","file_path":"inbox/two.txt","old_row":null,"row":{"_basename":"two.txt","_ctime":1783170226,"_dir":"inbox","_ext":"txt","_mtime":1783170226,"_path":"inbox/two.txt","_size":7},"table":"files"}
36
+ data: {"action":"insert","file_path":"inbox/two.txt","old_row":null,"row":{"basename":"two.txt","ctime":1783170226,"dir":"inbox","ext":"txt","mtime":1783170226,"path":"inbox/two.txt","size":7},"table":"files"}
37
37
  ```
38
38
 
39
39
  Edits arrive as `update` events carrying both the old and new row;
@@ -66,7 +66,7 @@ model = StaticModel.from_pretrained("minishlab/potion-base-8M")
66
66
  vector = model.encode([body["q"]])[0]
67
67
  needle = json.dumps([round(float(x), 6) for x in vector])
68
68
  print(
69
- "SELECT _path, ROUND(vec_distance_cosine(embedding, '%s'), 3) AS distance "
69
+ "SELECT path, ROUND(vec_distance_cosine(embedding, '%s'), 3) AS distance "
70
70
  "FROM notes ORDER BY distance LIMIT 3" % needle
71
71
  )
72
72
  ```
@@ -89,7 +89,7 @@ path = "sqlite_vec" # Python module name; see note below
89
89
  entrypoint = "sqlite3_vec_init"
90
90
 
91
91
  [[table]]
92
- ddl = "CREATE TABLE notes (_path TEXT, text TEXT, embedding TEXT)"
92
+ ddl = "CREATE TABLE notes (path TEXT, text TEXT, embedding TEXT)"
93
93
  glob = "notes/*.md"
94
94
  on-file = "uv run --with model2vec python embed.py {path}"
95
95
  ```
@@ -110,7 +110,7 @@ uvx --with sqlite-vec dirsql query '{"q": "how do I cook pasta?"}'
110
110
  ```
111
111
 
112
112
  ```json
113
- [{"_path":"notes/pasta.md","distance":0.315},{"_path":"notes/tomatoes.md","distance":0.881},{"_path":"notes/branches.md","distance":0.92}]
113
+ [{"path":"notes/pasta.md","distance":0.315},{"path":"notes/tomatoes.md","distance":0.881},{"path":"notes/branches.md","distance":0.92}]
114
114
  ```
115
115
 
116
116
  ```bash
@@ -118,7 +118,7 @@ uvx --with sqlite-vec dirsql query '{"q": "reviewing code on github"}'
118
118
  ```
119
119
 
120
120
  ```json
121
- [{"_path":"notes/branches.md","distance":0.51},{"_path":"notes/pasta.md","distance":1.033},{"_path":"notes/tomatoes.md","distance":1.074}]
121
+ [{"path":"notes/branches.md","distance":0.51},{"path":"notes/pasta.md","distance":1.033},{"path":"notes/tomatoes.md","distance":1.074}]
122
122
  ```
123
123
 
124
124
  Neither question shares a keyword with its top note — "cook" appears
@@ -22,7 +22,7 @@ Exclude the noise in `.dirsql.toml`:
22
22
  ignore = ["notes/drafts/**", "**/*.tmp"]
23
23
 
24
24
  [[table]]
25
- ddl = "CREATE TABLE notes (_path TEXT)"
25
+ ddl = "CREATE TABLE notes (path TEXT)"
26
26
  glob = "notes/**/*"
27
27
  ```
28
28
 
@@ -32,11 +32,11 @@ ignored file never reaches any table — even one whose glob would match it.
32
32
  ## 2. Confirm what made it in
33
33
 
34
34
  ```bash
35
- dirsql query "SELECT _path FROM notes ORDER BY _path"
35
+ dirsql query "SELECT path FROM notes ORDER BY path"
36
36
  ```
37
37
 
38
38
  ```json
39
- [{"_path":"notes/final.md"}]
39
+ [{"path":"notes/final.md"}]
40
40
  ```
41
41
 
42
42
  ## Notes
@@ -5,8 +5,8 @@ The `dirsql` binary has three modes:
5
5
  | Invocation | Behavior |
6
6
  |---|---|
7
7
  | `dirsql` (no subcommand) | Start a long-lived HTTP server exposing a SQL view of a directory. See [HTTP API](./http-api.md). |
8
- | `dirsql query "<sql>"` | Build the index, run one query, print the rows as JSON, exit. No server, no watch. |
9
- | `dirsql init` | Generate a starter `.dirsql.toml` by running `claude` over a directory. |
8
+ | `dirsql query "<sql>"` | Query the file system as JSON |
9
+ | `dirsql init` | Generate a `.dirsql.toml` |
10
10
 
11
11
  ## Installation
12
12
 
@@ -68,12 +68,12 @@ default `./.dirsql.toml`) with a single table named `files`:
68
68
 
69
69
  - Glob: `**/*` — every file under the root, at any depth, no ignores.
70
70
  - One row per file, with all seven
71
- [virtual columns](./columns.md): `_path`, `_basename`, `_dir`, `_ext`,
72
- `_size`, `_mtime`, `_ctime`.
71
+ [stat columns](./columns.md): `path`, `basename`, `dir`, `ext`,
72
+ `size`, `mtime`, `ctime`.
73
73
 
74
74
  ```bash
75
75
  curl -s localhost:7117/query -H 'content-type: application/json' \
76
- -d '{"sql":"SELECT _basename, _size FROM files ORDER BY _size DESC LIMIT 5"}'
76
+ -d '{"sql":"SELECT basename, size FROM files ORDER BY size DESC LIMIT 5"}'
77
77
  ```
78
78
 
79
79
  A config file, when present, fully overrules this default. A *missing*
@@ -100,12 +100,11 @@ with a JSON body describing the failure:
100
100
 
101
101
  ## `dirsql query`
102
102
 
103
- Run one SQL query from the shell — for ad-hoc inspection, scripting, and
104
- docs verification snippets — without booting the server and `curl`ing it:
103
+ Run a SQL query from the shell:
105
104
 
106
105
  ```bash
107
- dirsql query "SELECT _basename, _size FROM files ORDER BY _size DESC LIMIT 5"
108
- # [{"_basename":"model.bin","_size":104857600}, …]
106
+ dirsql query "SELECT basename, size FROM files ORDER BY size DESC LIMIT 5"
107
+ # [{"basename":"model.bin","size":104857600}, …]
109
108
 
110
109
  dirsql query "SELECT COUNT(*) AS n FROM posts" | jq '.[0].n'
111
110
  ```
@@ -140,34 +139,29 @@ stderr, with exit code `1`.
140
139
 
141
140
  ## `dirsql init`
142
141
 
143
- Generates a `.dirsql.toml` by running the `claude` CLI over the target
144
- directory. The generated config contains only filesystem-fact tables
145
- (`[[table]]` entries whose columns come from [glob captures and virtual
146
- columns](./columns.md)) — never content-derived columns.
142
+ Writes a starter `.dirsql.toml`:
147
143
 
148
144
  ```bash
149
145
  dirsql init
150
146
  ```
151
147
 
148
+ You can further tweak this config as needed.
149
+
152
150
  ### Flags
153
151
 
154
152
  | Flag | Default | Description |
155
153
  |---|---|---|
156
- | `--root <path>` | current directory | Directory to scan. |
157
- | `--output <path>` | `<root>/.dirsql.toml` | Where to write the generated config. |
154
+ | `--root <path>` | current directory | Directory the default `--output` path is resolved against. |
155
+ | `--output <path>` | `<root>/.dirsql.toml` | Where to write the config. |
158
156
  | `--force` | off | Overwrite the output file if it already exists. |
159
157
 
160
158
  ### Requirements and failure modes
161
159
 
162
- `init` requires `claude` on `PATH`, signed in; there is no separate API key.
163
160
  All failures exit `1` with a message on stderr:
164
161
 
165
162
  | Condition | Behavior |
166
163
  |---|---|
167
- | Output file exists and `--force` not passed | Fails before invoking `claude` (no LLM call is made). |
168
- | `claude` not found on `PATH` | Fails with a pointer to the Claude Code install docs. |
169
- | `claude` exits non-zero | Fails with `claude`'s stderr; no partial config is written. |
170
- | `claude` produces non-UTF-8 output | Fails; nothing is written. |
164
+ | Output file exists and `--force` not passed | Fails; nothing is written. |
165
+ | Output path unwritable (e.g. missing parent directory) | Fails with the underlying I/O error. |
171
166
 
172
- On success, `claude`'s stdout is written verbatim to the output path and
173
- `init` exits `0`.
167
+ On success, `init` exits `0`.
@@ -0,0 +1,80 @@
1
+ # Stat columns and glob captures
2
+
3
+ Every table — config-defined or programmatic — gets filesystem facts merged
4
+ onto its rows automatically: seven **stat columns** derived from the file's
5
+ path and stat metadata, plus one column per **`{name}` capture** in the
6
+ table's glob.
7
+
8
+ These are ordinary, physically stored `TEXT`/`INTEGER` columns, computed
9
+ once per file at scan time and written like any other column value — not
10
+ SQLite's `GENERATED ... VIRTUAL` columns (computed on the fly, never stored)
11
+ and not part of a `CREATE VIRTUAL TABLE` (dirsql tables are always real
12
+ tables). "Stat" describes where the value comes from — the file's path and
13
+ `stat` metadata, as opposed to its content — not how it's stored.
14
+
15
+ Facts are **opt-in by DDL**: only facts whose name appears as a column in
16
+ the table's `CREATE TABLE` are populated; the rest are silently dropped.
17
+ Declaring them requires nothing else. The names below aren't a protected or
18
+ enforced namespace — nothing stops you from declaring a column with one of
19
+ these names for an unrelated purpose, in which case dirsql's computed value
20
+ lands there like any other fact (unless your own row source — an `on-file`
21
+ command or SDK `extract` callback — supplies its own value for that name;
22
+ see [Precedence](#precedence)).
23
+
24
+ ## Stat columns
25
+
26
+ | Column | Type | Value |
27
+ |---|---|---|
28
+ | `path` | TEXT | The file's path relative to the scan root (e.g. `posts/hello.md`). |
29
+ | `basename` | TEXT | The filename, including extension (`hello.md`). |
30
+ | `dir` | TEXT | The parent directory relative to the root (`posts`); the empty string for files directly under the root. |
31
+ | `ext` | TEXT | The file extension without the leading dot (`md`). Original case is preserved — `Photo.JPG` yields `JPG`; use `LOWER(ext)` for case-insensitive matching. `NULL` when the file has no extension. |
32
+ | `size` | INTEGER | File size in bytes. |
33
+ | `mtime` | INTEGER | Last-modified time, Unix seconds. |
34
+ | `ctime` | INTEGER | Creation (birth) time, Unix seconds. `NULL` when the platform or filesystem cannot supply it. |
35
+
36
+ A fact that cannot be computed (an unreadable file's `size`/`mtime`/
37
+ `ctime`, a missing extension's `ext`) is absent from the row: `NULL` in
38
+ the default relaxed mode, a missing-column error for a
39
+ [`strict`](./config.md#table) table that declares it.
40
+
41
+ ```sql
42
+ SELECT basename, size
43
+ FROM posts
44
+ WHERE mtime > strftime('%s', '2024-01-01')
45
+ ORDER BY mtime DESC;
46
+ ```
47
+
48
+ ## Glob captures
49
+
50
+ A `{name}` segment in a table's glob captures part of each matched path as
51
+ a TEXT column named `name`:
52
+
53
+ ```toml
54
+ [[table]]
55
+ ddl = "CREATE TABLE comments (thread_id TEXT, basename TEXT, mtime INTEGER)"
56
+ glob = "_comments/{thread_id}/*.jsonl"
57
+ ```
58
+
59
+ A file at `_comments/abc123/2024-05-05.jsonl` produces a row with
60
+ `thread_id = "abc123"`.
61
+
62
+ - A capture name must be a valid identifier: a letter or underscore
63
+ followed by letters, digits, or underscores (`[a-zA-Z_][a-zA-Z0-9_]*`).
64
+ - A capture matches **within a single path segment** — one or more
65
+ characters, never a `/`. For matching purposes, `{name}` behaves like
66
+ `*`.
67
+ - A glob may contain multiple captures (`{year}/{month}/*.jpg`).
68
+ - Like stat columns, a capture populates a column only when the DDL
69
+ declares a column of the same name.
70
+
71
+ ## Precedence
72
+
73
+ Values produced by a table's own row source — an `on-file` command's JSON
74
+ output or an SDK `extract` callback's return value — **win** over
75
+ auto-injected facts of the same name. An extract that explicitly emits
76
+ `path` is honored.
77
+
78
+ Injection order per row: stat columns first, then glob captures, then
79
+ the row source's own values, each layer overwriting the previous, all
80
+ filtered to the DDL's declared columns.
@@ -97,7 +97,7 @@ per-file command.
97
97
  |---|---|---|
98
98
  | `ddl` | yes | A SQLite `CREATE TABLE` statement. The table name is parsed from it. Only columns declared here are populated; auto-injected facts not in the DDL are dropped. |
99
99
  | `glob` | yes | Glob pattern matched against root-relative paths. May contain `{name}` [capture segments](./columns.md#glob-captures). First matching table wins when a file matches several globs. |
100
- | `strict` | no (default `false`) | When `true`, rows whose keys do not exactly match the declared columns are rejected with an error: extra keys error, and every declared column must be supplied (by the command/extract output, a glob capture, or a virtual column). When `false`, extra keys are dropped and missing columns become `NULL`. |
100
+ | `strict` | no (default `false`) | When `true`, rows whose keys do not exactly match the declared columns are rejected with an error: extra keys error, and every declared column must be supplied (by the command/extract output, a glob capture, or a stat column). When `false`, extra keys are dropped and missing columns become `NULL`. |
101
101
  | `on-file` | no | A command run once per matched file; its stdout (a JSON array of row objects) becomes the file's rows. Must be non-empty. See [Command hooks](./hooks.md#on-file). |
102
102
 
103
103
  Without `on-file`, a table produces exactly one row per matched file, built
@@ -108,7 +108,7 @@ callback.
108
108
 
109
109
  ```toml
110
110
  [[table]]
111
- ddl = "CREATE TABLE comments (thread_id TEXT, _basename TEXT, _mtime INTEGER)"
111
+ ddl = "CREATE TABLE comments (thread_id TEXT, basename TEXT, mtime INTEGER)"
112
112
  glob = "_comments/{thread_id}/*.jsonl"
113
113
 
114
114
  [[table]]
@@ -157,10 +157,10 @@ path = "sqlite_vec" # Python module name; on Node use the
157
157
  entrypoint = "sqlite3_vec_init"
158
158
 
159
159
  [[table]]
160
- ddl = "CREATE TABLE comments (thread_id TEXT, _basename TEXT, _mtime INTEGER)"
160
+ ddl = "CREATE TABLE comments (thread_id TEXT, basename TEXT, mtime INTEGER)"
161
161
  glob = "_comments/{thread_id}/*.jsonl"
162
162
 
163
163
  [[table]]
164
- ddl = "CREATE TABLE documents (_path TEXT, _basename TEXT, _size INTEGER)"
164
+ ddl = "CREATE TABLE documents (path TEXT, basename TEXT, size INTEGER)"
165
165
  glob = "**/index.md"
166
166
  ```
@@ -4,7 +4,7 @@ name = "dirsql-py-ext"
4
4
  # pypi/maturin handler can rewrite it via `write-version` before
5
5
  # `maturin build`. `pyproject.toml` declares `dynamic = ["version"]`
6
6
  # and maturin reads this field. Mirrors `packages/rust/Cargo.toml`.
7
- version = "0.3.73"
7
+ version = "0.3.75"
8
8
  edition.workspace = true
9
9
  publish = false
10
10
  readme = "README.md"
@@ -57,7 +57,7 @@ migrated -- a page survives only if a slot wants its content):
57
57
  `dirsql` in an application.
58
58
  - **Reference** (`reference/`) -- CLI flags and defaults; the complete
59
59
  `.dirsql.toml` schema; the command hook contract (placeholders, stdout
60
- protocol, exit codes, timeouts); virtual columns and glob captures; the
60
+ protocol, exit codes, timeouts); stat columns and glob captures; the
61
61
  HTTP API; the SDK page (`reference/sdk.md`, one page with
62
62
  Python/TypeScript/Rust code-groups -- the sole SDK home); plus the
63
63
  Migrations include (`migrations.md`).
@@ -112,30 +112,30 @@ through `jq` to pretty-print. Now select some columns:
112
112
  ```bash
113
113
  curl -s http://localhost:7117/query \
114
114
  -H 'content-type: application/json' \
115
- -d '{"sql":"SELECT _path, _size FROM files ORDER BY _path"}' \
115
+ -d '{"sql":"SELECT path, size FROM files ORDER BY path"}' \
116
116
  | jq
117
117
  ```
118
118
 
119
119
  ```json
120
120
  [
121
121
  {
122
- "_path": "notes/alice/ideas.md",
123
- "_size": 52
122
+ "path": "notes/alice/ideas.md",
123
+ "size": 52
124
124
  },
125
125
  {
126
- "_path": "notes/alice/welcome.md",
127
- "_size": 66
126
+ "path": "notes/alice/welcome.md",
127
+ "size": 66
128
128
  },
129
129
  {
130
- "_path": "notes/bob/reading-list.md",
131
- "_size": 41
130
+ "path": "notes/bob/reading-list.md",
131
+ "size": 41
132
132
  }
133
133
  ]
134
134
  ```
135
135
 
136
- `_path` and `_size` are two of the built-in file columns `dirsql` collects
137
- for every file — see [virtual columns](./reference/columns.md#virtual-columns)
138
- for the full list. (The `_size` values are byte counts; they match the
136
+ `path` and `size` are two of the built-in file columns `dirsql` collects
137
+ for every file — see [stat columns](./reference/columns.md#stat-columns)
138
+ for the full list. (The `size` values are byte counts; they match the
139
139
  output above because you pasted the files exactly.)
140
140
 
141
141
  You have a working SQL database over your files. Next, teach it the
@@ -152,7 +152,7 @@ In your second terminal, still inside `my-notes`, create a `.dirsql.toml`:
152
152
  ```bash
153
153
  cat > .dirsql.toml <<'EOF'
154
154
  [[table]]
155
- ddl = "CREATE TABLE notes (author TEXT, _basename TEXT, _size INTEGER)"
155
+ ddl = "CREATE TABLE notes (author TEXT, basename TEXT, size INTEGER)"
156
156
  glob = "notes/{author}/*.md"
157
157
  EOF
158
158
  ```
@@ -192,25 +192,25 @@ terminal:
192
192
  ```bash
193
193
  curl -s http://localhost:7117/query \
194
194
  -H 'content-type: application/json' \
195
- -d '{"sql":"SELECT author, _basename, _size FROM notes ORDER BY author, _basename"}' \
195
+ -d '{"sql":"SELECT author, basename, size FROM notes ORDER BY author, basename"}' \
196
196
  | jq
197
197
  ```
198
198
 
199
199
  ```json
200
200
  [
201
201
  {
202
- "_basename": "ideas.md",
203
- "_size": 52,
202
+ "basename": "ideas.md",
203
+ "size": 52,
204
204
  "author": "alice"
205
205
  },
206
206
  {
207
- "_basename": "welcome.md",
208
- "_size": 66,
207
+ "basename": "welcome.md",
208
+ "size": 66,
209
209
  "author": "alice"
210
210
  },
211
211
  {
212
- "_basename": "reading-list.md",
213
- "_size": 41,
212
+ "basename": "reading-list.md",
213
+ "size": 41,
214
214
  "author": "bob"
215
215
  }
216
216
  ]
@@ -18,7 +18,7 @@ Capture both directory levels in `.dirsql.toml`:
18
18
 
19
19
  ```toml
20
20
  [[table]]
21
- ddl = "CREATE TABLE photos (year TEXT, month TEXT, _basename TEXT)"
21
+ ddl = "CREATE TABLE photos (year TEXT, month TEXT, basename TEXT)"
22
22
  glob = "photos/{year}/{month}/*.jpg"
23
23
  ```
24
24
 
@@ -30,11 +30,11 @@ within one path segment) are in
30
30
  ## 2. Query the captured columns
31
31
 
32
32
  ```bash
33
- dirsql query "SELECT year, month, _basename FROM photos ORDER BY year, month"
33
+ dirsql query "SELECT year, month, basename FROM photos ORDER BY year, month"
34
34
  ```
35
35
 
36
36
  ```json
37
- [{"_basename":"beach.jpg","month":"05","year":"2024"},{"_basename":"hike.jpg","month":"11","year":"2024"},{"_basename":"snow.jpg","month":"01","year":"2025"}]
37
+ [{"basename":"beach.jpg","month":"05","year":"2024"},{"basename":"hike.jpg","month":"11","year":"2024"},{"basename":"snow.jpg","month":"01","year":"2025"}]
38
38
  ```
39
39
 
40
40
  Captures are real SQL columns, so aggregation works:
@@ -49,8 +49,8 @@ dirsql query "SELECT year, COUNT(*) AS photos FROM photos GROUP BY year"
49
49
 
50
50
  ## Going further
51
51
 
52
- - Captures combine freely with [virtual columns](../reference/columns.md#virtual-columns)
53
- (`_basename` above) — both are filesystem facts merged onto every row.
52
+ - Captures combine freely with [stat columns](../reference/columns.md#stat-columns)
53
+ (`basename` above) — both are filesystem facts merged onto every row.
54
54
  - The [tutorial](../getting-started.md) walks the same idea with an
55
55
  `{author}` capture, starting from zero.
56
56
  - When the value you need lives inside the file rather than in its path,