dirsql 0.4.61__tar.gz → 0.4.63__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 (262) hide show
  1. {dirsql-0.4.61 → dirsql-0.4.63}/Cargo.lock +2 -2
  2. {dirsql-0.4.61 → dirsql-0.4.63}/PKG-INFO +1 -1
  3. {dirsql-0.4.61 → dirsql-0.4.63}/docs/getting-started.md +25 -13
  4. {dirsql-0.4.61 → dirsql-0.4.63}/docs/howto/columns-from-paths.md +14 -11
  5. {dirsql-0.4.61/packages/python → dirsql-0.4.63}/docs/howto/define-tables.md +12 -9
  6. {dirsql-0.4.61/packages/rust → dirsql-0.4.63}/docs/howto/extract-from-contents.md +5 -5
  7. {dirsql-0.4.61 → dirsql-0.4.63}/docs/howto/parse-files-into-columns.md +22 -19
  8. {dirsql-0.4.61/packages/rust → dirsql-0.4.63}/docs/howto/react-to-changes.md +1 -1
  9. {dirsql-0.4.61/packages/rust → dirsql-0.4.63}/docs/howto/search-indexes.md +2 -2
  10. {dirsql-0.4.61/packages/rust → dirsql-0.4.63}/docs/howto/skip-files.md +1 -1
  11. {dirsql-0.4.61/packages/rust → dirsql-0.4.63}/docs/howto/write-a-plugin.md +26 -23
  12. {dirsql-0.4.61/packages/rust → dirsql-0.4.63}/docs/plugins.md +7 -6
  13. {dirsql-0.4.61 → dirsql-0.4.63}/docs/reference/cli.md +13 -10
  14. {dirsql-0.4.61/packages/python → dirsql-0.4.63}/docs/reference/columns.md +4 -4
  15. {dirsql-0.4.61/packages/rust → dirsql-0.4.63}/docs/reference/config.md +7 -7
  16. {dirsql-0.4.61/packages/rust → dirsql-0.4.63}/docs/reference/hooks.md +10 -5
  17. {dirsql-0.4.61/packages/python → dirsql-0.4.63}/docs/reference/path-tables.md +6 -6
  18. {dirsql-0.4.61 → dirsql-0.4.63}/docs/reference/sdk.md +7 -0
  19. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/Cargo.toml +1 -1
  20. {dirsql-0.4.61/packages/rust → dirsql-0.4.63/packages/python}/docs/getting-started.md +25 -13
  21. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/docs/howto/columns-from-paths.md +14 -11
  22. {dirsql-0.4.61 → dirsql-0.4.63/packages/python}/docs/howto/define-tables.md +12 -9
  23. {dirsql-0.4.61 → dirsql-0.4.63/packages/python}/docs/howto/extract-from-contents.md +5 -5
  24. {dirsql-0.4.61/packages/rust → dirsql-0.4.63/packages/python}/docs/howto/parse-files-into-columns.md +22 -19
  25. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/docs/howto/react-to-changes.md +1 -1
  26. {dirsql-0.4.61 → dirsql-0.4.63/packages/python}/docs/howto/search-indexes.md +2 -2
  27. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/docs/howto/skip-files.md +1 -1
  28. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/docs/howto/write-a-plugin.md +26 -23
  29. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/docs/plugins.md +7 -6
  30. {dirsql-0.4.61/packages/rust → dirsql-0.4.63/packages/python}/docs/reference/cli.md +13 -10
  31. {dirsql-0.4.61 → dirsql-0.4.63/packages/python}/docs/reference/columns.md +4 -4
  32. {dirsql-0.4.61 → dirsql-0.4.63/packages/python}/docs/reference/config.md +7 -7
  33. {dirsql-0.4.61 → dirsql-0.4.63/packages/python}/docs/reference/hooks.md +10 -5
  34. {dirsql-0.4.61/packages/rust → dirsql-0.4.63/packages/python}/docs/reference/path-tables.md +6 -6
  35. {dirsql-0.4.61/packages/rust → dirsql-0.4.63/packages/python}/docs/reference/sdk.md +7 -0
  36. dirsql-0.4.63/packages/python/e2e-attestations/claude-1196-join-index.json +7 -0
  37. dirsql-0.4.63/packages/python/e2e-attestations/claude-1215-on-file-batch.json +7 -0
  38. dirsql-0.4.63/packages/python/e2e-attestations/claude-1226-stale-docs.json +7 -0
  39. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/dirsql.toml +1 -1
  40. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/Cargo.toml +1 -1
  41. {dirsql-0.4.61/packages/python → dirsql-0.4.63/packages/rust}/docs/getting-started.md +25 -13
  42. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/docs/howto/columns-from-paths.md +14 -11
  43. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/docs/howto/define-tables.md +12 -9
  44. {dirsql-0.4.61/packages/python → dirsql-0.4.63/packages/rust}/docs/howto/extract-from-contents.md +5 -5
  45. {dirsql-0.4.61/packages/python → dirsql-0.4.63/packages/rust}/docs/howto/parse-files-into-columns.md +22 -19
  46. {dirsql-0.4.61 → dirsql-0.4.63/packages/rust}/docs/howto/react-to-changes.md +1 -1
  47. {dirsql-0.4.61/packages/python → dirsql-0.4.63/packages/rust}/docs/howto/search-indexes.md +2 -2
  48. {dirsql-0.4.61 → dirsql-0.4.63/packages/rust}/docs/howto/skip-files.md +1 -1
  49. {dirsql-0.4.61 → dirsql-0.4.63/packages/rust}/docs/howto/write-a-plugin.md +26 -23
  50. {dirsql-0.4.61 → dirsql-0.4.63/packages/rust}/docs/plugins.md +7 -6
  51. {dirsql-0.4.61/packages/python → dirsql-0.4.63/packages/rust}/docs/reference/cli.md +13 -10
  52. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/docs/reference/columns.md +4 -4
  53. {dirsql-0.4.61/packages/python → dirsql-0.4.63/packages/rust}/docs/reference/config.md +7 -7
  54. {dirsql-0.4.61/packages/python → dirsql-0.4.63/packages/rust}/docs/reference/hooks.md +10 -5
  55. {dirsql-0.4.61 → dirsql-0.4.63/packages/rust}/docs/reference/path-tables.md +6 -6
  56. {dirsql-0.4.61/packages/python → dirsql-0.4.63/packages/rust}/docs/reference/sdk.md +7 -0
  57. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/cli/run.rs +16 -12
  58. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/command.rs +11 -0
  59. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/config.rs +2 -2
  60. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/db.rs +169 -20
  61. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/default_config.toml +7 -6
  62. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/differ.rs +190 -0
  63. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/infer.rs +11 -0
  64. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/lib.rs +772 -251
  65. dirsql-0.4.63/packages/rust/src/on_file.rs +236 -0
  66. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/parsed_vtab.rs +185 -268
  67. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/vtab.rs +38 -0
  68. dirsql-0.4.63/packages/rust/src/vtab_scaffold.rs +1138 -0
  69. dirsql-0.4.61/packages/rust/src/vtab_scaffold.rs +0 -562
  70. {dirsql-0.4.61 → dirsql-0.4.63}/Cargo.toml +0 -0
  71. {dirsql-0.4.61 → dirsql-0.4.63}/README.md +0 -0
  72. {dirsql-0.4.61 → dirsql-0.4.63}/dirsql/__init__.py +0 -0
  73. {dirsql-0.4.61 → dirsql-0.4.63}/dirsql/_async.py +0 -0
  74. {dirsql-0.4.61 → dirsql-0.4.63}/dirsql/_dirsql.pyi +0 -0
  75. {dirsql-0.4.61 → dirsql-0.4.63}/dirsql/cli/__init__.py +0 -0
  76. {dirsql-0.4.61 → dirsql-0.4.63}/dirsql/cli/discover_plugins/__init__.py +0 -0
  77. {dirsql-0.4.61 → dirsql-0.4.63}/dirsql/cli/discover_plugins/discovered_fragments.py +0 -0
  78. {dirsql-0.4.61 → dirsql-0.4.63}/dirsql/cli/discover_plugins/discovery_disabled.py +0 -0
  79. {dirsql-0.4.61 → dirsql-0.4.63}/dirsql/cli/discover_plugins/fragment_path.py +0 -0
  80. {dirsql-0.4.61 → dirsql-0.4.63}/dirsql/cli/discover_plugins/user_passed_config.py +0 -0
  81. {dirsql-0.4.61 → dirsql-0.4.63}/dirsql/cli/discover_plugins/with_discovered_plugins.py +0 -0
  82. {dirsql-0.4.61 → dirsql-0.4.63}/dirsql/cli/main.py +0 -0
  83. {dirsql-0.4.61 → dirsql-0.4.63}/dirsql/cli/resolve_config_extensions.py +0 -0
  84. {dirsql-0.4.61 → dirsql-0.4.63}/dirsql/plan_entry_path.py +0 -0
  85. {dirsql-0.4.61 → dirsql-0.4.63}/dirsql/py.typed +0 -0
  86. {dirsql-0.4.61 → dirsql-0.4.63}/dirsql/read_config.py +0 -0
  87. {dirsql-0.4.61 → dirsql-0.4.63}/dirsql/resolve_configs_extension_specs.py +0 -0
  88. {dirsql-0.4.61 → dirsql-0.4.63}/dirsql/resolve_extension.py +0 -0
  89. {dirsql-0.4.61 → dirsql-0.4.63}/dirsql/resolve_package.py +0 -0
  90. {dirsql-0.4.61 → dirsql-0.4.63}/docs/.claude/CLAUDE.md +0 -0
  91. {dirsql-0.4.61 → dirsql-0.4.63}/docs/.vitepress/config.ts +0 -0
  92. {dirsql-0.4.61 → dirsql-0.4.63}/docs/.vitepress/theme/index.ts +0 -0
  93. {dirsql-0.4.61 → dirsql-0.4.63}/docs/.vitepress/theme/lang.ts +0 -0
  94. {dirsql-0.4.61 → dirsql-0.4.63}/docs/AGENTS.md +0 -0
  95. {dirsql-0.4.61 → dirsql-0.4.63}/docs/explanation.md +0 -0
  96. {dirsql-0.4.61 → dirsql-0.4.63}/docs/howto/embed.md +0 -0
  97. {dirsql-0.4.61 → dirsql-0.4.63}/docs/howto/load-extension.md +0 -0
  98. {dirsql-0.4.61 → dirsql-0.4.63}/docs/howto/persist.md +0 -0
  99. {dirsql-0.4.61 → dirsql-0.4.63}/docs/howto/query-json.md +0 -0
  100. {dirsql-0.4.61 → dirsql-0.4.63}/docs/howto/query-without-config.md +0 -0
  101. {dirsql-0.4.61 → dirsql-0.4.63}/docs/howto/search-by-meaning.md +0 -0
  102. {dirsql-0.4.61 → dirsql-0.4.63}/docs/index.md +0 -0
  103. {dirsql-0.4.61 → dirsql-0.4.63}/docs/package.json +0 -0
  104. {dirsql-0.4.61 → dirsql-0.4.63}/docs/pnpm-lock.yaml +0 -0
  105. {dirsql-0.4.61 → dirsql-0.4.63}/docs/pnpm-workspace.yaml +0 -0
  106. {dirsql-0.4.61 → dirsql-0.4.63}/docs/reference/http-api.md +0 -0
  107. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/CHANGELOG.md +0 -0
  108. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/MIGRATIONS.md +0 -0
  109. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/README.md +0 -0
  110. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
  111. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-07-13-plugin-discovery.md +0 -0
  112. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-07-13-rename-extract-to-on-file.md +0 -0
  113. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-07-13-repeatable-config.md +0 -0
  114. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
  115. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-07-20-drop-implicit-files-table.md +0 -0
  116. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-07-21-remove-glob-captures.md +0 -0
  117. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-07-24-hookless-table-config-error.md +0 -0
  118. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-07-24-remove-stat-fact-injection.md +0 -0
  119. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-07-29-restore-python-310.md +0 -0
  120. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-08-02-release-profile.md +0 -0
  121. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-08-02-scan-failures.md +0 -0
  122. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-08-03-discovery-ext-resolution.md +0 -0
  123. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-08-03-manylinux-dynamic-binary.md +0 -0
  124. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-08-03-python-no-ignore.md +0 -0
  125. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-08-04-cast-lints.md +0 -0
  126. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-08-04-cli-in-process.md +0 -0
  127. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-08-20-curtaincall-dev-dependency.md +0 -0
  128. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-08-20-declared-table-name.md +0 -0
  129. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-09-04-config-paths-from-argv-module.md +0 -0
  130. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-09-05-config-extension-loader-split.md +0 -0
  131. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-09-05-resolve-config-extensions-split.md +0 -0
  132. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-09-05-resolve-extension-split.md +0 -0
  133. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-09-09-launcher-config-path-scan.md +0 -0
  134. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-09-17-extension-resolution-via-core.md +0 -0
  135. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-09-18-export-scan-failure.md +0 -0
  136. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-09-18-flatten-row-event-in-core.md +0 -0
  137. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-09-18-programmatic-extension-plan.md +0 -0
  138. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-09-20-sigint-ends-a-run-in-progress.md +0 -0
  139. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/2026-09-24-query-rows-in-projection-order.md +0 -0
  140. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/changelog.d/README.md +0 -0
  141. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/conftest.py +0 -0
  142. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/docs/.claude/CLAUDE.md +0 -0
  143. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/docs/.vitepress/config.ts +0 -0
  144. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/docs/.vitepress/theme/index.ts +0 -0
  145. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
  146. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/docs/AGENTS.md +0 -0
  147. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/docs/explanation.md +0 -0
  148. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/docs/howto/embed.md +0 -0
  149. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/docs/howto/load-extension.md +0 -0
  150. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/docs/howto/persist.md +0 -0
  151. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/docs/howto/query-json.md +0 -0
  152. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/docs/howto/query-without-config.md +0 -0
  153. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/docs/howto/search-by-meaning.md +0 -0
  154. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/docs/index.md +0 -0
  155. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/docs/package.json +0 -0
  156. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/docs/pnpm-lock.yaml +0 -0
  157. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/docs/pnpm-workspace.yaml +0 -0
  158. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/docs/reference/http-api.md +0 -0
  159. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/cc-keen-knuth-f11gyq.json +0 -0
  160. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1056-py-ofpf9.json +0 -0
  161. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1066-p1-resolve-extension.json +0 -0
  162. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1078-argv-config-paths-core.json +0 -0
  163. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1079-p2-resolve-config-extensions.json +0 -0
  164. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1080-share-vtab-scaffolding.json +0 -0
  165. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1081-indicatif-progress.json +0 -0
  166. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1096-watch-moved-dir.json +0 -0
  167. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1109-watch-moved-out.json +0 -0
  168. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1119-ext-plan.json +0 -0
  169. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1120-py-ext.json +0 -0
  170. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1122-ext-cleanup.json +0 -0
  171. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1130-codec-crates.json +0 -0
  172. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1131-flatten-row-event.json +0 -0
  173. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1139-sse-flatten.json +0 -0
  174. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1140-scan-failure-export.json +0 -0
  175. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1148-clippy-all-targets.json +0 -0
  176. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1164-sigint-fatal.json +0 -0
  177. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1170-projection-order.json +0 -0
  178. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1172-on-file-path.json +0 -0
  179. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1173-py-projection-order.json +0 -0
  180. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1184-win-rel-paths.json +0 -0
  181. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1185-win-abs-table-names.json +0 -0
  182. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1186-win-test-harness.json +0 -0
  183. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1201-single-walk.json +0 -0
  184. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1204-embed-batch.json +0 -0
  185. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1207-win-argv-quote.json +0 -0
  186. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-1208-win-nonverbatim-paths.json +0 -0
  187. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-819-oneshot-timeout.json +0 -0
  188. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-827-unquote-doubled-quotes.json +0 -0
  189. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-epic-953-slice2-reedline.json +0 -0
  190. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-epic-953-slice3-format.json +0 -0
  191. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-epic-953-stacked-prs-x59wm3.json +0 -0
  192. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-github-issue-825-e4mhhp.json +0 -0
  193. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-issue-951-0eease.json +0 -0
  194. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-issue-962-0jjrfv.json +0 -0
  195. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-issue-986-red-test-e7sjrb.json +0 -0
  196. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-tackle-1034-cachesplit.json +0 -0
  197. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-tackle-1042-py12.json +0 -0
  198. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-tackle-956-xzen74.json +0 -0
  199. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-tackle-957-lrm0z6-worker-calls.json +0 -0
  200. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-tackle-957-lrm0z6.json +0 -0
  201. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/e2e-attestations/claude-tackle-975-n8mc2h.json +0 -0
  202. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/migrations.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
  203. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/migrations.d/2026-07-13-rename-extract-to-on-file.md +0 -0
  204. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/migrations.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
  205. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/migrations.d/2026-07-20-drop-implicit-files-table.md +0 -0
  206. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/migrations.d/2026-07-21-remove-glob-captures.md +0 -0
  207. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/migrations.d/2026-07-24-hookless-table-config-error.md +0 -0
  208. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/migrations.d/2026-07-24-remove-stat-fact-injection.md +0 -0
  209. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/migrations.d/2026-07-29-restore-python-310.md +0 -0
  210. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/migrations.d/2026-08-04-cli-in-process.md +0 -0
  211. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/migrations.d/2026-08-20-declared-table-name.md +0 -0
  212. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/migrations.d/2026-09-20-sigint-ends-a-run-in-progress.md +0 -0
  213. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/migrations.d/2026-09-24-query-rows-in-projection-order.md +0 -0
  214. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/migrations.d/README.md +0 -0
  215. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/src/lib.rs +0 -0
  216. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/testing-conventions.toml +0 -0
  217. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/tests/__init__.py +0 -0
  218. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/tests/conftest.py +0 -0
  219. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/tests/e2e/__init__.py +0 -0
  220. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/__init__.py +0 -0
  221. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/tests/e2e/speed_of_light.py +0 -0
  222. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/tests/integration/__init__.py +0 -0
  223. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/tests/integration/binding/__init__.py +0 -0
  224. {dirsql-0.4.61 → dirsql-0.4.63}/packages/python/tests/integration/hermetic/__init__.py +0 -0
  225. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/CHANGELOG.md +0 -0
  226. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/MIGRATIONS.md +0 -0
  227. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/README.md +0 -0
  228. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/benches/db_bench.rs +0 -0
  229. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/benches/differ_bench.rs +0 -0
  230. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/benches/matcher_bench.rs +0 -0
  231. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/benches/scanner_bench.rs +0 -0
  232. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/docs/explanation.md +0 -0
  233. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/docs/howto/embed.md +0 -0
  234. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/docs/howto/load-extension.md +0 -0
  235. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/docs/howto/persist.md +0 -0
  236. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/docs/howto/query-json.md +0 -0
  237. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/docs/howto/query-without-config.md +0 -0
  238. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/docs/howto/search-by-meaning.md +0 -0
  239. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/docs/index.md +0 -0
  240. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/docs/reference/http-api.md +0 -0
  241. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/bin/dirsql.rs +0 -0
  242. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/cli/execute.rs +0 -0
  243. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/cli/init.rs +0 -0
  244. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/cli/mod.rs +0 -0
  245. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/cli/repl.rs +0 -0
  246. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/cli/router.rs +0 -0
  247. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/cli/serialize.rs +0 -0
  248. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/cli/server.rs +0 -0
  249. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/cli/table.rs +0 -0
  250. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/extension_resolution.rs +0 -0
  251. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/functions.rs +0 -0
  252. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/launcher.rs +0 -0
  253. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/matcher.rs +0 -0
  254. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/parsed_cache.rs +0 -0
  255. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/path_table.rs +0 -0
  256. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/persist.rs +0 -0
  257. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/progress.rs +0 -0
  258. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/row_event_flat.rs +0 -0
  259. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/scanner.rs +0 -0
  260. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/sql_literal.rs +0 -0
  261. {dirsql-0.4.61 → dirsql-0.4.63}/packages/rust/src/watcher.rs +0 -0
  262. {dirsql-0.4.61 → dirsql-0.4.63}/pyproject.toml +0 -0
@@ -552,7 +552,7 @@ checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
552
552
 
553
553
  [[package]]
554
554
  name = "dirsql"
555
- version = "0.4.61"
555
+ version = "0.4.63"
556
556
  dependencies = [
557
557
  "assert_cmd",
558
558
  "axum",
@@ -600,7 +600,7 @@ dependencies = [
600
600
 
601
601
  [[package]]
602
602
  name = "dirsql-py-ext"
603
- version = "0.4.61"
603
+ version = "0.4.63"
604
604
  dependencies = [
605
605
  "dirsql",
606
606
  "pyo3",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dirsql
3
- Version: 0.4.61
3
+ Version: 0.4.63
4
4
  Requires-Dist: bin-shim>=0.1
5
5
  Summary: Ephemeral SQL index over a local directory
6
6
  Keywords: sql,filesystem,directory,sqlite,index
@@ -81,11 +81,15 @@ The first run downloads the package (`npx` asks for confirmation — answer
81
81
  `y`; `uvx` prints download progress), then prints the result:
82
82
 
83
83
  ```
84
- [{"files":3}]
84
+ files
85
+ -----
86
+ 3
87
+
88
+ 1 row
85
89
  ```
86
90
 
87
91
  Three files, three rows. That one command scanned the directory, handed
88
- SQLite one row per file, ran your SQL, and printed the answer as JSON.
92
+ SQLite one row per file, ran your SQL, and printed the answer.
89
93
 
90
94
  There is no named table here — you never declared one. `'./'` is a
91
95
  [path-table](./reference/path-tables.md): a quoted path written where a table
@@ -94,8 +98,9 @@ in. The path *is* the query.
94
98
 
95
99
  ## 3. Select some columns
96
100
 
97
- The response is always a JSON array of row objects, so from here on we pipe
98
- it through `jq` to pretty-print. Ask for two columns instead of a count:
101
+ At a terminal `dirsql` prints a table; when its output is piped, it prints a
102
+ JSON array of row objects instead. From here on we pipe it through `jq` to
103
+ pretty-print. Ask for two columns instead of a count:
99
104
 
100
105
  ::: code-group
101
106
 
@@ -151,11 +156,17 @@ folder name), and prints them as a JSON row:
151
156
  ```bash
152
157
  cat > note.sh <<'EOF'
153
158
  #!/usr/bin/env sh
154
- title=$(sed -n 's/^# //p' "$1" | head -n1)
155
- author=$(basename "$(dirname "$1")")
156
- printf '[{"title":%s,"author":%s}]' \
157
- "$(jq -Rn --arg t "$title" '$t')" \
158
- "$(jq -Rn --arg a "$author" '$a')"
159
+ printf '['
160
+ sep=''
161
+ for f; do
162
+ title=$(sed -n 's/^# //p' "$f" | head -n1)
163
+ author=$(basename "$(dirname "$f")")
164
+ printf '%s{"title":%s,"author":%s}' "$sep" \
165
+ "$(jq -Rn --arg t "$title" '$t')" \
166
+ "$(jq -Rn --arg a "$author" '$a')"
167
+ sep=','
168
+ done
169
+ printf ']'
159
170
  EOF
160
171
  ```
161
172
 
@@ -167,7 +178,7 @@ cat > .dirsql.toml <<'EOF'
167
178
  name = "notes"
168
179
  ddl = "CREATE TABLE notes (title TEXT, author TEXT)"
169
180
  glob = "notes/**/*.md"
170
- on-file = "sh note.sh {path}"
181
+ on-file = "sh note.sh"
171
182
  EOF
172
183
  ```
173
184
 
@@ -176,9 +187,10 @@ Three keys define the table:
176
187
  - `glob` selects which files feed the table — every `.md` at any depth under
177
188
  `notes/`, relative to the directory the config sits in.
178
189
  - `ddl` is ordinary `CREATE TABLE` SQL naming the columns you want to keep.
179
- - `on-file` is the command run once per matched file; `{path}` is the file's
180
- path, and its printed JSON row becomes the file's row. The columns are
181
- exactly what it emits — `title` from the heading, `author` from the folder.
190
+ - `on-file` is the command run once for the table, with every matched file's
191
+ path appended as an argument; the JSON array it prints is the table's rows.
192
+ The columns are exactly what it emits — `title` from the heading, `author`
193
+ from the folder.
182
194
 
183
195
  ## 5. Query the table
184
196
 
@@ -15,18 +15,21 @@ photos/2024/11/hike.jpg
15
15
  photos/2025/01/snow.jpg
16
16
  ```
17
17
 
18
- Write a small parser, `pathcols.py`, that turns the path into a row. It
19
- receives the file's absolute path as its argument and prints a JSON array of
20
- row objects:
18
+ Write a small parser, `pathcols.py`, that turns each path into a row. It
19
+ receives the matched files' absolute paths as its arguments and prints one
20
+ JSON array of row objects:
21
21
 
22
22
  ```python
23
23
  #!/usr/bin/env python3
24
24
  import json, os, sys
25
25
 
26
- parts = sys.argv[1].split(os.sep)
27
- # .../photos/<year>/<month>/<file>
28
- print(json.dumps([{"year": parts[-3], "month": parts[-2],
29
- "basename": os.path.basename(sys.argv[1])}]))
26
+ rows = []
27
+ for path in sys.argv[1:]:
28
+ parts = path.split(os.sep)
29
+ # .../photos/<year>/<month>/<file>
30
+ rows.append({"year": parts[-3], "month": parts[-2],
31
+ "basename": os.path.basename(path)})
32
+ print(json.dumps(rows))
30
33
  ```
31
34
 
32
35
  Point a table at it in `.dirsql.toml`:
@@ -36,12 +39,12 @@ Point a table at it in `.dirsql.toml`:
36
39
  name = "photos"
37
40
  ddl = "CREATE TABLE photos (year TEXT, month TEXT, basename TEXT)"
38
41
  glob = "photos/*/*/*.jpg"
39
- on-file = "python3 pathcols.py {path}"
42
+ on-file = "python3 pathcols.py"
40
43
  ```
41
44
 
42
- The hook emits every column the table has — dirsql injects nothing. `{path}`
43
- is the matched file's absolute path, one of the placeholders in the
44
- [command hook contract](../reference/hooks.md#on-file). A column appears only
45
+ The hook emits every column the table has — dirsql injects nothing. The
46
+ matched files' absolute paths arrive as the command's trailing arguments, per
47
+ the [command hook contract](../reference/hooks.md#on-file). A column appears only
45
48
  because the DDL declares it *and* the hook emits it.
46
49
 
47
50
  ## 2. Query the derived columns
@@ -10,16 +10,19 @@ persist across restarts, instead of repeating an ad-hoc
10
10
  Suppose your blog posts live under `posts/`, one markdown file each. A named
11
11
  table needs three keys: a `glob` that selects the files, a `ddl` that names
12
12
  the columns, and an [`on-file`](../reference/config.md#table) hook that emits
13
- each file's rows. Put a small parser next to the config — `extract.py`, which
14
- reads a post's title line and prints a JSON array of row objects:
13
+ the table's rows. Put a small parser next to the config — `extract.py`, which
14
+ reads each post's title line and prints one JSON array of row objects:
15
15
 
16
16
  ```python
17
17
  #!/usr/bin/env python3
18
18
  import json, os, sys
19
19
 
20
- text = open(sys.argv[1], encoding="utf-8").read()
21
- title = next((l[2:].strip() for l in text.splitlines() if l.startswith("# ")), None)
22
- print(json.dumps([{"title": title, "slug": os.path.basename(sys.argv[1])[:-3]}]))
20
+ rows = []
21
+ for path in sys.argv[1:]:
22
+ text = open(path, encoding="utf-8").read()
23
+ title = next((l[2:].strip() for l in text.splitlines() if l.startswith("# ")), None)
24
+ rows.append({"title": title, "slug": os.path.basename(path)[:-3]})
25
+ print(json.dumps(rows))
23
26
  ```
24
27
 
25
28
  Then declare the table in `.dirsql.toml`:
@@ -29,15 +32,15 @@ Then declare the table in `.dirsql.toml`:
29
32
  name = "posts"
30
33
  ddl = "CREATE TABLE posts (title TEXT, slug TEXT)"
31
34
  glob = "posts/**/*.md"
32
- on-file = "python3 extract.py {path}"
35
+ on-file = "python3 extract.py"
33
36
  ```
34
37
 
35
38
  - `glob` selects the files: every `.md` under `posts/`, at any depth, relative
36
39
  to the directory containing the config.
37
40
  - `ddl` is a plain SQLite `CREATE TABLE` naming the columns you want to keep.
38
41
  - `on-file` is **required** — it is where the table's rows come from. dirsql
39
- injects nothing; the hook emits every column, reading the file (it has
40
- `{path}`) and deriving whatever it needs. A `[[table]]` with no `on-file` is
42
+ injects nothing; the hook emits every column, reading the files (their
43
+ paths are its arguments) and deriving whatever it needs. A `[[table]]` with no `on-file` is
41
44
  a [config error](../reference/config.md#parse-errors). For plain stat
42
45
  columns with no code, query the path directly with a path-table instead.
43
46
 
@@ -75,7 +78,7 @@ two triggers:
75
78
  [[table]]
76
79
  name = "posts"
77
80
  glob = "posts/**/*.md"
78
- on-file = "python3 extract.py {path}"
81
+ on-file = "python3 extract.py"
79
82
  ddl = '''
80
83
  CREATE TABLE posts (title TEXT, slug TEXT, body TEXT);
81
84
  CREATE INDEX posts_slug ON posts(slug);
@@ -21,11 +21,11 @@ stdout works. With [`jq`](https://jqlang.org/):
21
21
  name = "books"
22
22
  ddl = "CREATE TABLE books (title TEXT, author TEXT, year INTEGER)"
23
23
  glob = "books/*.json"
24
- on-file = "jq -c '[{title, author, year}]' {path}"
24
+ on-file = "jq -c -n '[inputs | {title, author, year}]'"
25
25
  ```
26
26
 
27
- `{path}` is the matched file's absolute path — one of the
28
- placeholders defined by the
27
+ The command runs once for the table, with every matched file's absolute path
28
+ appended as an argument, and prints one array for all of them — the
29
29
  [command hook contract](../reference/hooks.md#on-file), which also covers
30
30
  the argv splitting, working directory, stdout protocol, and timeout shared
31
31
  by every hook.
@@ -45,7 +45,7 @@ dirsql query "SELECT title, author, year FROM books ORDER BY year" -c ./.dirsql.
45
45
 
46
46
  The table's columns are exactly what the command emits, narrowed to the DDL —
47
47
  `dirsql` adds nothing. To include the file's `path`, have the command emit it
48
- (it has `{path}`); dirsql will not merge it in for you.
48
+ (it has the path); dirsql will not merge it in for you.
49
49
 
50
50
  ## Multiple rows per file
51
51
 
@@ -57,7 +57,7 @@ row per line, slurp it:
57
57
  name = "events"
58
58
  ddl = "CREATE TABLE events (event TEXT, user TEXT)"
59
59
  glob = "logs/*.jsonl"
60
- on-file = "jq -c -s '.' {path}"
60
+ on-file = "jq -c -s '.'"
61
61
  ```
62
62
 
63
63
  ```bash
@@ -32,40 +32,43 @@ To get them, you need a parser.
32
32
 
33
33
  ## 2. Attach a parser with `--on-file`
34
34
 
35
- Any program that reads one file and prints a **JSON array of row objects** on
36
- stdout is a parser. Here is a small one, `extract.py`, that reads a post's
37
- frontmatter:
35
+ Any program that reads the files named by its arguments and prints a **JSON
36
+ array of row objects** on stdout is a parser. Here is a small one,
37
+ `extract.py`, that reads each post's frontmatter:
38
38
 
39
39
  ```python
40
40
  #!/usr/bin/env python3
41
41
  import json, re, sys
42
42
 
43
- text = open(sys.argv[1], encoding="utf-8").read()
44
- m = re.match(r"^---\n(.*?)\n---", text, re.DOTALL)
45
- fields = dict(
46
- (k.strip(), v.strip())
47
- for k, _, v in (line.partition(":") for line in (m.group(1).splitlines() if m else []))
48
- )
49
- print(json.dumps([{"title": fields.get("title"), "author": fields.get("author")}]))
43
+ rows = []
44
+ for path in sys.argv[1:]:
45
+ text = open(path, encoding="utf-8").read()
46
+ m = re.match(r"^---\n(.*?)\n---", text, re.DOTALL)
47
+ fields = dict(
48
+ (k.strip(), v.strip())
49
+ for k, _, v in (line.partition(":") for line in (m.group(1).splitlines() if m else []))
50
+ )
51
+ rows.append({"title": fields.get("title"), "author": fields.get("author")})
52
+ print(json.dumps(rows))
50
53
  ```
51
54
 
52
55
  Point the path-table at it with `--on-file`:
53
56
 
54
57
  ```bash
55
58
  dirsql query "SELECT title, author FROM './posts/*.md' ORDER BY title" \
56
- --on-file 'python3 extract.py {path}'
59
+ --on-file 'python3 extract.py'
57
60
  ```
58
61
 
59
62
  ```json
60
63
  [{"author":"Ada Lovelace","title":"Hello World"},{"author":"Alan Turing","title":"On Recursion"}]
61
64
  ```
62
65
 
63
- Now the parser's output *is* the table. `--on-file` runs the command once per
64
- matched file; `{path}` is the file's absolute path, one of the placeholders in
65
- the shared [`on-file` hook contract](../reference/hooks.md#on-file) (argv
66
- splitting, timeout, and per-file failure isolation all come from there). The
67
- stat columns are no longer reachable — a parser that wants the path emits it,
68
- since it already has `{path}`. See
66
+ Now the parser's output *is* the table. `--on-file` runs the command once,
67
+ with every matched file's absolute path appended as an argument, under the
68
+ shared [`on-file` hook contract](../reference/hooks.md#on-file) (argv
69
+ splitting, timeout, and failure semantics all come from there). The stat
70
+ columns are no longer reachable — a parser that wants the path emits it, since
71
+ it already has the path. See
69
72
  [Parsing rows with `--on-file`](../reference/path-tables.md#parsing-rows-with-on-file)
70
73
  for the full behavior.
71
74
 
@@ -87,7 +90,7 @@ command in verbatim:
87
90
  name = "posts"
88
91
  ddl = "CREATE TABLE posts (title TEXT, author TEXT)"
89
92
  glob = "posts/*.md"
90
- on-file = "python3 extract.py {path}"
93
+ on-file = "python3 extract.py"
91
94
  ```
92
95
 
93
96
  The `on-file` value is byte-for-byte the string you passed to `--on-file`. The
@@ -109,7 +112,7 @@ indexed on build, kept fresh by the watcher, survives restarts with
109
112
  [many tables](./define-tables.md) — each with its own `on-file` — where the flag
110
113
  gives every path-table one parser. In both spellings the table's columns are
111
114
  exactly what the parser emits: `dirsql` merges no filesystem facts back on. A
112
- row that needs the file's `path` emits it (the parser has `{path}`).
115
+ row that needs the file's `path` emits it (the parser has the path).
113
116
 
114
117
  ## Going further
115
118
 
@@ -17,7 +17,7 @@ prints each file's basename:
17
17
  name = "files"
18
18
  ddl = "CREATE TABLE files (basename TEXT)"
19
19
  glob = "**/*"
20
- on-file = '''sh -c 'printf "[{\"basename\":\"%s\"}]" "${1##*/}"' sh {path}'''
20
+ on-file = '''sh -c 'printf "["; sep=""; for p; do printf "%s{\"basename\":\"%s\"}" "$sep" "${p##*/}"; sep=","; done; printf "]"' sh'''
21
21
  ```
22
22
 
23
23
  ## 1. Open the stream
@@ -31,7 +31,7 @@ external-content index beside the row table and let the two triggers feed it:
31
31
  [[table]]
32
32
  name = "notes"
33
33
  glob = "notes/**/*.md"
34
- on-file = "python3 extract.py {path}"
34
+ on-file = "python3 extract.py"
35
35
  ddl = '''
36
36
  CREATE TABLE notes (slug TEXT, title TEXT, body TEXT);
37
37
 
@@ -150,7 +150,7 @@ inserted vector for the "embedding" column. Expected 8 dimensions but received 4
150
150
  [[table]]
151
151
  name = "notes"
152
152
  glob = "notes/**/*.md"
153
- on-file = "python3 extract.py {path}"
153
+ on-file = "python3 extract.py"
154
154
  ddl = '''
155
155
  CREATE TABLE notes (slug TEXT, title TEXT, body TEXT);
156
156
 
@@ -25,7 +25,7 @@ ignore = ["notes/drafts/**", "**/*.tmp"]
25
25
  name = "notes"
26
26
  ddl = "CREATE TABLE notes (basename TEXT)"
27
27
  glob = "notes/**/*"
28
- on-file = '''sh -c 'printf "[{\"basename\":\"%s\"}]" "${1##*/}"' sh {path}'''
28
+ on-file = '''sh -c 'printf "["; sep=""; for p; do printf "%s{\"basename\":\"%s\"}" "$sep" "${p##*/}"; sep=","; done; printf "]"' sh'''
29
29
  ```
30
30
 
31
31
  Patterns match against root-relative paths, the same way table globs do. An
@@ -60,11 +60,12 @@ its `embed()` SQL function),
60
60
  and [`ignore`](../reference/config.md#dirsql-keys).
61
61
 
62
62
  There are **no plugin-specific keys and no plugin-specific restrictions**. The
63
- config schema is content-only: the index `root` and `--persist` are
64
- [runner-owned flags](../reference/config.md#dirsql-keys) (`--root`, `--persist
65
- [PATH]`), decided by whoever runs `dirsql`, never by a config file — so a
66
- plugin has nothing to say about them. Whatever you can put in your own
67
- `.dirsql.toml`, a plugin can put in its fragment, and vice-versa.
63
+ config schema is content-only: the index root and persistence are
64
+ [runner-owned](../reference/config.md#dirsql-keys) — the root is the directory
65
+ `dirsql` is run from (or an SDK's explicit `root`), persistence is the
66
+ `--persist [PATH]` flag — decided by whoever runs `dirsql`, never by a config
67
+ file, so a plugin has nothing to say about them. Whatever you can put in your
68
+ own `.dirsql.toml`, a plugin can put in its fragment, and vice-versa.
68
69
 
69
70
  ## Hook commands
70
71
 
@@ -72,11 +73,11 @@ Both hook command styles from the [hook contract](../reference/hooks.md) work
72
73
  in a plugin fragment:
73
74
 
74
75
  - **Console scripts** — a `bin`-style entry point your package installs on
75
- `PATH` (`embed-file {path} {root}`). Recommended for published plugins: the command
76
+ `PATH` (`embed-file {root}`). Recommended for published plugins: the command
76
77
  is bound to your package's interpreter and dependencies, and it is
77
78
  language-neutral (the fragment names a command, not a Python file).
78
79
  - **Relative scripts** — a path resolved against the fragment's own directory
79
- (`uv run python embed.py {path}`). Convenient while developing the plugin
80
+ (`uv run python embed.py {root}`). Convenient while developing the plugin
80
81
  in-tree.
81
82
 
82
83
  Two facts from the [execution contract](../reference/hooks.md#execution-contract)
@@ -84,12 +85,12 @@ matter most for a published plugin:
84
85
 
85
86
  - **A hook runs in its declaring config's directory.** For a plugin that is
86
87
  the installed fragment's directory — inside **site-packages**. That is a
87
- read-only, shared location: **run from it, never write to it.** Use the
88
- absolute [`{path}`](../reference/hooks.md#on-file) placeholder to read the
89
- matched file, and [`{root}`](../reference/hooks.md#on-file) to reach the
90
- user's project directory. Write any cache to `{root}` or a real cache dir,
88
+ read-only, shared location: **run from it, never write to it.** Read the
89
+ matched files from the absolute paths the hook appends as arguments, and use
90
+ [`{root}`](../reference/hooks.md#on-file) to reach the user's project
91
+ directory. Write any cache to `{root}` or a real cache dir,
91
92
  never next to the fragment.
92
- - **`{path}` is absolute and `{root}` is the index root**, so a command is
93
+ - **The paths are absolute and `{root}` is the index root**, so a command is
93
94
  self-sufficient from any working directory — it works whether the plugin
94
95
  lives in the project or in site-packages.
95
96
 
@@ -111,28 +112,30 @@ entrypoint = "sqlite3_vec_init"
111
112
  name = "notes"
112
113
  ddl = "CREATE TABLE notes (path TEXT, text TEXT, embedding TEXT)"
113
114
  glob = "notes/*.md"
114
- on-file = "uv run --with model2vec python embed.py {path} {root}"
115
+ on-file = "uv run --with model2vec python embed.py {root}"
115
116
  ```
116
117
 
117
- `embed.py` turns one file into one row carrying its path, text, and embedding.
118
- dirsql injects no columns, so the script emits the path itself, from the
119
- `{path}`/`{root}` the hook passes in:
118
+ `embed.py` turns each file into one row carrying its path, text, and
119
+ embedding. dirsql injects no columns, so the script emits the path itself,
120
+ from the `{root}` the hook passes in and the paths it appends:
120
121
 
121
122
  ```python
122
- """Embed one file's text; print a dirsql row array on stdout."""
123
+ """Embed each file's text; print one dirsql row array on stdout."""
123
124
  import json
124
125
  import os
125
126
  import sys
126
127
 
127
128
  from model2vec import StaticModel
128
129
 
129
- path, root = sys.argv[1], sys.argv[2]
130
- text = open(path, encoding="utf-8").read()
130
+ root, paths = sys.argv[1], sys.argv[2:]
131
+ texts = [open(path, encoding="utf-8").read() for path in paths]
131
132
  model = StaticModel.from_pretrained("minishlab/potion-base-8M")
132
- vector = model.encode([text])[0]
133
- row = {"path": os.path.relpath(path, root), "text": text,
134
- "embedding": json.dumps([round(float(x), 6) for x in vector])}
135
- print(json.dumps([row]))
133
+ rows = [
134
+ {"path": os.path.relpath(path, root), "text": text,
135
+ "embedding": json.dumps([round(float(x), 6) for x in vector])}
136
+ for path, text, vector in zip(paths, texts, model.encode(texts))
137
+ ]
138
+ print(json.dumps(rows))
136
139
  ```
137
140
 
138
141
  The relative `embed.py` above resolves against the fragment directory, which
@@ -93,9 +93,10 @@ scope.
93
93
  ### Vector cache
94
94
 
95
95
  Computed vectors are cached at `~/.cache/dirsql/embeddings/` (respecting
96
- `XDG_CACHE_HOME`), keyed on the SHA-256 of the value bytes plus the model
97
- identifier. Changing either recomputes — switching models never serves stale
98
- vectors — and re-running a query over unchanged files is cache hits all the
99
- way. There is no eviction: the directory is **safe to wipe at any time**; the
100
- only cost is re-embedding. The cache never lives inside a queried tree — the
101
- worker writes nothing into the directories you query.
96
+ `XDG_CACHE_HOME`): one SQLite database per model identifier, each row keyed
97
+ on the SHA-256 of the value bytes. Changing the value or the model recomputes
98
+ — switching models never serves stale vectors — and re-running a query over
99
+ unchanged files is cache hits all the way, a whole batch answered by one
100
+ query. There is no eviction: the directory is **safe to wipe at any time**;
101
+ the only cost is re-embedding. The cache never lives inside a queried tree —
102
+ the worker writes nothing into the directories you query.
@@ -1,11 +1,13 @@
1
1
  # CLI
2
2
 
3
- Query is the default: `dirsql "<sql>"` runs one query and prints JSON rows.
3
+ Query is the default: `dirsql "<sql>"` runs one query and prints the rows —
4
+ a table at a terminal, a JSON array when piped (see
5
+ [Output format](#output-format)).
4
6
  The `dirsql` binary has these modes:
5
7
 
6
8
  | Invocation | Behavior |
7
9
  |---|---|
8
- | `dirsql "<sql>"` | Run one query over the directory and print the rows as JSON. The default; identical to `dirsql query "<sql>"`. |
10
+ | `dirsql "<sql>"` | Run one query over the directory and print the rows. The default; identical to `dirsql query "<sql>"`. |
9
11
  | `dirsql query "<sql>"` | Explicit synonym for the default one-shot query. |
10
12
  | `dirsql server` | Start a long-lived HTTP server exposing a SQL view of a directory. See [HTTP API](./http-api.md). |
11
13
  | `dirsql init` | Generate a `.dirsql.toml`. |
@@ -53,7 +55,7 @@ pipeline, same flags, same output. See that section for config discovery,
53
55
 
54
56
  ```bash
55
57
  dirsql
56
- # dirsql 0.2.7 — this directory is a database.
58
+ # dirsql <version> — this directory is a database.
57
59
  #
58
60
  # SELECT basename, size FROM './' ORDER BY size DESC LIMIT 5;
59
61
  # SELECT path FROM './**/*.md' WHERE content LIKE '%TODO%';
@@ -331,9 +333,10 @@ is a hard error — `error: the subcommand 'query' cannot be used with
331
333
  for the server they follow the subcommand: `dirsql server -c <cfg>`.)
332
334
  :::
333
335
 
334
- The subcommand builds the index, runs the SQL, prints the result rows as a
335
- JSON array on stdout (byte-identical to the [`POST /query`](./http-api.md)
336
- response body), and exits `0`.
336
+ The subcommand builds the index, runs the SQL, prints the result rows on
337
+ stdout — a table at a terminal, otherwise a JSON array byte-identical to the
338
+ [`POST /query`](./http-api.md) response body (see
339
+ [Output format](#output-format)) — and exits `0`.
337
340
 
338
341
  `dirsql query` is a thin adapter over the **same query pipeline the server
339
342
  uses**, so behavior is identical to `POST /query` by construction:
@@ -367,12 +370,12 @@ array of row objects) instead of the stat columns:
367
370
 
368
371
  ```sh
369
372
  dirsql query "SELECT title, author FROM './posts/*.md'" \
370
- --on-file 'extract.py {path}'
373
+ --on-file 'extract.py'
371
374
  ```
372
375
 
373
376
  The command follows the [`on-file` hook contract](./hooks.md#on-file) — argv
374
- splitting, `{path}`/`{root}` placeholders, per-file failure isolation, and the
375
- timeout. The parser's output is the whole schema; the stat columns are not
377
+ splitting, the `{root}` placeholder, every matched path as a trailing
378
+ argument, the failure semantics, and the timeout. The parser's output is the whole schema; the stat columns are not
376
379
  reachable on a parsed path-table. `--on-file` may be given **at most once** (a
377
380
  repeat is an error pointing at config files) and never touches config-declared
378
381
  tables. It is a `query`-only flag — server mode rejects it as an unknown
@@ -413,7 +416,7 @@ The output does **not** auto-load. Once you've tweaked it, pass it explicitly
413
416
  to run against it:
414
417
 
415
418
  ```bash
416
- dirsql "SELECT * FROM files" -c ./.dirsql.toml
419
+ dirsql "SELECT * FROM records" -c ./.dirsql.toml
417
420
  ```
418
421
 
419
422
  ### Flags
@@ -12,8 +12,8 @@ A named [`[[table]]`](./config.md#table) — or an SDK
12
12
  [`on-file` hook](./hooks.md#on-file) emits, narrowed to the columns the DDL
13
13
  declares. dirsql adds nothing on top: no `path`, no `size`, no value derived
14
14
  from the filename. A hook that wants any of those computes them itself. The
15
- hook receives the file's `{path}` (an SDK `on_file` callback receives the same
16
- path as its argument) and may stat or read the file however it likes.
15
+ hook receives every matched file's path as an argument (an SDK `on_file`
16
+ callback receives one path) and may stat or read the files however it likes.
17
17
 
18
18
  The hook is **required**. A `[[table]]` with no `on-file` is a
19
19
  [config-load error](./config.md#parse-errors): with nothing supplying columns,
@@ -58,12 +58,12 @@ ORDER BY mtime DESC;
58
58
  [Attaching a parser](./path-tables.md#parsing-rows-with-on-file) to a
59
59
  path-table with `--on-file` **replaces** these stat columns with the parser's
60
60
  own output — the two modes stay cleanly separate, exactly as for a named
61
- table. A parser that wants the path emits it; it has `{path}`.
61
+ table. A parser that wants the path emits it; it has the paths.
62
62
 
63
63
  ## Deriving columns from the path
64
64
 
65
65
  To turn path segments (an author, a year, a thread id) into columns, a hook
66
- splits `{path}` and emits the pieces — the same as any other column it
66
+ splits the path and emits the pieces — the same as any other column it
67
67
  produces. dirsql does not do this for you: a `{name}` segment in a glob is
68
68
  rewritten to `*` and matches a single path segment, but captures no value.
69
69
  [Derive columns from file paths](../howto/columns-from-paths.md) walks a
@@ -221,12 +221,12 @@ what its required `on-file` command emits — dirsql injects nothing (see
221
221
  | `name` | yes | The table's SQL name — the name you query it by. Declared, never derived from `ddl`: dirsql does not read the DDL text. The `ddl` must create a table by this name; if it doesn't, loading fails. |
222
222
  | `ddl` | yes | A SQL batch, run verbatim — any number of statements. It must create a table called `name`; that table holds the file rows, and only the columns it declares are kept (keys the `on-file` command emits that are not declared are dropped). The rest of the batch is yours: indexes, virtual tables, triggers. See [Batch `ddl`](#batch-ddl). |
223
223
  | `glob` | yes | Glob pattern matched against root-relative paths. Every table whose glob matches a file receives that file's rows — a file can populate multiple tables. A `{name}` segment is rewritten to `*` (it matches one path segment but captures nothing). |
224
- | `on-file` | **yes** | A command run once per matched file; its stdout (a JSON array of row objects) becomes the file's rows. Must be non-empty. A `[[table]]` with no `on-file` is a load error (see [parse errors](#parse-errors)). See [Command hooks](./hooks.md#on-file). |
224
+ | `on-file` | **yes** | A command run once per table, with every matched file's absolute path appended as a trailing argument; its stdout (one JSON array of row objects) is the table's rows. Must be non-empty. A `[[table]]` with no `on-file` is a load error (see [parse errors](#parse-errors)). See [Command hooks](./hooks.md#on-file). |
225
225
  | `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 `on-file` output. When `false`, extra keys are dropped and missing columns become `NULL`. |
226
226
 
227
227
  `on-file` is required because a table's rows come from nowhere else. dirsql
228
228
  does not read file contents or merge filesystem facts on your behalf: the
229
- command reads the file (it receives `{path}`) and prints the rows, and those
229
+ command reads the files (it receives their paths as arguments) and prints the rows, and those
230
230
  rows — filtered to the DDL — are the table. For plain stat columns with no
231
231
  command, query the path directly with a [path-table](./path-tables.md)
232
232
  instead of declaring a table.
@@ -236,13 +236,13 @@ instead of declaring a table.
236
236
  name = "comments"
237
237
  ddl = "CREATE TABLE comments (path TEXT, author TEXT, body TEXT)"
238
238
  glob = "_comments/*/*.jsonl"
239
- on-file = "jq -c -s '.' {path}"
239
+ on-file = "jq -c -s '.'"
240
240
 
241
241
  [[table]]
242
242
  name = "papers"
243
243
  ddl = "CREATE TABLE papers (paper_id TEXT, title TEXT)"
244
244
  glob = "**/meta.json"
245
- on-file = "uv run python extract_papers.py {path}"
245
+ on-file = "uv run python extract_papers.py"
246
246
  strict = true
247
247
  ```
248
248
 
@@ -255,7 +255,7 @@ statement:
255
255
  [[table]]
256
256
  name = "messages"
257
257
  glob = "sessions/*/messages/*.json"
258
- on-file = "jq -c '.' {path}"
258
+ on-file = "jq -c -s add"
259
259
  ddl = '''
260
260
  CREATE TABLE messages (session TEXT, idx INT, role TEXT, text TEXT);
261
261
  CREATE INDEX messages_session ON messages(session);
@@ -419,11 +419,11 @@ timeout = "600s"
419
419
  name = "comments"
420
420
  ddl = "CREATE TABLE comments (author TEXT, body TEXT)"
421
421
  glob = "_comments/*/*.jsonl"
422
- on-file = "jq -c -s '.' {path}"
422
+ on-file = "jq -c -s '.'"
423
423
 
424
424
  [[table]]
425
425
  name = "documents"
426
426
  ddl = "CREATE TABLE documents (title TEXT, summary TEXT)"
427
427
  glob = "**/index.md"
428
- on-file = "uv run python extract_doc.py {path}"
428
+ on-file = "uv run python extract_doc.py"
429
429
  ```