dirsql 0.4.16__tar.gz → 0.4.18__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 (246) hide show
  1. {dirsql-0.4.16 → dirsql-0.4.18}/Cargo.lock +1 -2
  2. {dirsql-0.4.16 → dirsql-0.4.18}/PKG-INFO +1 -1
  3. {dirsql-0.4.16 → dirsql-0.4.18}/docs/.vitepress/config.ts +1 -0
  4. dirsql-0.4.18/docs/howto/search-by-meaning.md +114 -0
  5. {dirsql-0.4.16/packages/python → dirsql-0.4.18}/docs/howto/write-a-plugin.md +6 -6
  6. dirsql-0.4.18/docs/plugins.md +100 -0
  7. {dirsql-0.4.16/packages/python → dirsql-0.4.18}/docs/reference/cli.md +16 -11
  8. {dirsql-0.4.16 → dirsql-0.4.18}/docs/reference/config.md +102 -16
  9. {dirsql-0.4.16/packages/rust → dirsql-0.4.18}/docs/reference/hooks.md +21 -9
  10. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/Cargo.toml +1 -1
  11. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/.vitepress/config.ts +1 -0
  12. dirsql-0.4.18/packages/python/docs/howto/search-by-meaning.md +114 -0
  13. {dirsql-0.4.16 → dirsql-0.4.18/packages/python}/docs/howto/write-a-plugin.md +6 -6
  14. dirsql-0.4.18/packages/python/docs/plugins.md +100 -0
  15. {dirsql-0.4.16/packages/rust → dirsql-0.4.18/packages/python}/docs/reference/cli.md +16 -11
  16. {dirsql-0.4.16/packages/rust → dirsql-0.4.18/packages/python}/docs/reference/config.md +102 -16
  17. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/reference/hooks.md +21 -9
  18. dirsql-0.4.18/packages/python/e2e-attestations/claude-819-oneshot-timeout.json +7 -0
  19. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/Cargo.toml +1 -4
  20. dirsql-0.4.18/packages/rust/changelog.d/2026-08-10-dirsql-function.md +1 -0
  21. dirsql-0.4.18/packages/rust/changelog.d/2026-08-11-oneshot-query-no-timeout.md +3 -0
  22. dirsql-0.4.18/packages/rust/changelog.d/2026-08-11-retire-hook-timeout.md +3 -0
  23. dirsql-0.4.18/packages/rust/docs/howto/search-by-meaning.md +114 -0
  24. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/docs/howto/write-a-plugin.md +6 -6
  25. dirsql-0.4.18/packages/rust/docs/plugins.md +100 -0
  26. {dirsql-0.4.16 → dirsql-0.4.18/packages/rust}/docs/reference/cli.md +16 -11
  27. {dirsql-0.4.16/packages/python → dirsql-0.4.18/packages/rust}/docs/reference/config.md +102 -16
  28. {dirsql-0.4.16 → dirsql-0.4.18/packages/rust}/docs/reference/hooks.md +21 -9
  29. dirsql-0.4.18/packages/rust/migrations.d/2026-08-11-oneshot-query-no-timeout.md +46 -0
  30. dirsql-0.4.18/packages/rust/migrations.d/2026-08-11-retire-hook-timeout.md +51 -0
  31. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/src/cli/execute.rs +54 -14
  32. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/src/cli/router.rs +1 -1
  33. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/src/cli/run.rs +9 -7
  34. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/src/command.rs +21 -93
  35. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/src/config.rs +542 -117
  36. dirsql-0.4.18/packages/rust/src/functions.rs +771 -0
  37. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/src/lib.rs +200 -71
  38. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/src/parsed_vtab.rs +2 -2
  39. dirsql-0.4.16/docs/plugins.md +0 -100
  40. dirsql-0.4.16/packages/python/docs/plugins.md +0 -100
  41. dirsql-0.4.16/packages/python/e2e-attestations/chore-766-narrow-pedantic.json +0 -7
  42. dirsql-0.4.16/packages/python/e2e-attestations/claude-737-cli-run-shim.json +0 -7
  43. dirsql-0.4.16/packages/python/e2e-attestations/claude-738-pypi-in-process.json +0 -7
  44. dirsql-0.4.16/packages/python/e2e-attestations/claude-739-npm-in-process.json +0 -7
  45. dirsql-0.4.16/packages/python/e2e-attestations/claude-772-extension-without-config.json +0 -7
  46. dirsql-0.4.16/packages/python/e2e-attestations/claude-803-remove-query-hooks.json +0 -7
  47. dirsql-0.4.16/packages/rust/docs/plugins.md +0 -100
  48. {dirsql-0.4.16 → dirsql-0.4.18}/Cargo.toml +0 -0
  49. {dirsql-0.4.16 → dirsql-0.4.18}/README.md +0 -0
  50. {dirsql-0.4.16 → dirsql-0.4.18}/dirsql/__init__.py +0 -0
  51. {dirsql-0.4.16 → dirsql-0.4.18}/dirsql/_async.py +0 -0
  52. {dirsql-0.4.16 → dirsql-0.4.18}/dirsql/_dirsql.pyi +0 -0
  53. {dirsql-0.4.16 → dirsql-0.4.18}/dirsql/cli/__init__.py +0 -0
  54. {dirsql-0.4.16 → dirsql-0.4.18}/dirsql/cli/discover_plugins/__init__.py +0 -0
  55. {dirsql-0.4.16 → dirsql-0.4.18}/dirsql/cli/discover_plugins/discovered_fragments.py +0 -0
  56. {dirsql-0.4.16 → dirsql-0.4.18}/dirsql/cli/discover_plugins/discovery_disabled.py +0 -0
  57. {dirsql-0.4.16 → dirsql-0.4.18}/dirsql/cli/discover_plugins/fragment_path.py +0 -0
  58. {dirsql-0.4.16 → dirsql-0.4.18}/dirsql/cli/discover_plugins/user_passed_config.py +0 -0
  59. {dirsql-0.4.16 → dirsql-0.4.18}/dirsql/cli/discover_plugins/with_discovered_plugins.py +0 -0
  60. {dirsql-0.4.16 → dirsql-0.4.18}/dirsql/cli/main.py +0 -0
  61. {dirsql-0.4.16 → dirsql-0.4.18}/dirsql/cli/resolve_config_extensions.py +0 -0
  62. {dirsql-0.4.16 → dirsql-0.4.18}/dirsql/py.typed +0 -0
  63. {dirsql-0.4.16 → dirsql-0.4.18}/dirsql/resolve_config_extensions.py +0 -0
  64. {dirsql-0.4.16 → dirsql-0.4.18}/dirsql/resolve_extension.py +0 -0
  65. {dirsql-0.4.16 → dirsql-0.4.18}/docs/.claude/CLAUDE.md +0 -0
  66. {dirsql-0.4.16 → dirsql-0.4.18}/docs/.vitepress/theme/index.ts +0 -0
  67. {dirsql-0.4.16 → dirsql-0.4.18}/docs/.vitepress/theme/lang.ts +0 -0
  68. {dirsql-0.4.16 → dirsql-0.4.18}/docs/AGENTS.md +0 -0
  69. {dirsql-0.4.16 → dirsql-0.4.18}/docs/explanation.md +0 -0
  70. {dirsql-0.4.16 → dirsql-0.4.18}/docs/getting-started.md +0 -0
  71. {dirsql-0.4.16 → dirsql-0.4.18}/docs/howto/columns-from-paths.md +0 -0
  72. {dirsql-0.4.16 → dirsql-0.4.18}/docs/howto/define-tables.md +0 -0
  73. {dirsql-0.4.16 → dirsql-0.4.18}/docs/howto/embed.md +0 -0
  74. {dirsql-0.4.16 → dirsql-0.4.18}/docs/howto/extract-from-contents.md +0 -0
  75. {dirsql-0.4.16 → dirsql-0.4.18}/docs/howto/load-extension.md +0 -0
  76. {dirsql-0.4.16 → dirsql-0.4.18}/docs/howto/parse-files-into-columns.md +0 -0
  77. {dirsql-0.4.16 → dirsql-0.4.18}/docs/howto/persist.md +0 -0
  78. {dirsql-0.4.16 → dirsql-0.4.18}/docs/howto/query-without-config.md +0 -0
  79. {dirsql-0.4.16 → dirsql-0.4.18}/docs/howto/react-to-changes.md +0 -0
  80. {dirsql-0.4.16 → dirsql-0.4.18}/docs/howto/skip-files.md +0 -0
  81. {dirsql-0.4.16 → dirsql-0.4.18}/docs/index.md +0 -0
  82. {dirsql-0.4.16 → dirsql-0.4.18}/docs/migrations.md +0 -0
  83. {dirsql-0.4.16 → dirsql-0.4.18}/docs/package.json +0 -0
  84. {dirsql-0.4.16 → dirsql-0.4.18}/docs/pnpm-lock.yaml +0 -0
  85. {dirsql-0.4.16 → dirsql-0.4.18}/docs/pnpm-workspace.yaml +0 -0
  86. {dirsql-0.4.16 → dirsql-0.4.18}/docs/reference/columns.md +0 -0
  87. {dirsql-0.4.16 → dirsql-0.4.18}/docs/reference/http-api.md +0 -0
  88. {dirsql-0.4.16 → dirsql-0.4.18}/docs/reference/path-tables.md +0 -0
  89. {dirsql-0.4.16 → dirsql-0.4.18}/docs/reference/sdk.md +0 -0
  90. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/CHANGELOG.md +0 -0
  91. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/MIGRATIONS.md +0 -0
  92. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/README.md +0 -0
  93. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/changelog.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
  94. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/changelog.d/2026-07-13-plugin-discovery.md +0 -0
  95. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/changelog.d/2026-07-13-rename-extract-to-on-file.md +0 -0
  96. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/changelog.d/2026-07-13-repeatable-config.md +0 -0
  97. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/changelog.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
  98. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/changelog.d/2026-07-20-drop-implicit-files-table.md +0 -0
  99. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/changelog.d/2026-07-21-remove-glob-captures.md +0 -0
  100. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/changelog.d/2026-07-24-hookless-table-config-error.md +0 -0
  101. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/changelog.d/2026-07-24-remove-stat-fact-injection.md +0 -0
  102. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/changelog.d/2026-07-29-restore-python-310.md +0 -0
  103. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/changelog.d/2026-08-02-release-profile.md +0 -0
  104. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/changelog.d/2026-08-02-scan-failures.md +0 -0
  105. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/changelog.d/2026-08-03-discovery-ext-resolution.md +0 -0
  106. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/changelog.d/2026-08-03-manylinux-dynamic-binary.md +0 -0
  107. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/changelog.d/2026-08-03-python-no-ignore.md +0 -0
  108. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/changelog.d/2026-08-04-cast-lints.md +0 -0
  109. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/changelog.d/2026-08-04-cli-in-process.md +0 -0
  110. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/changelog.d/README.md +0 -0
  111. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/conftest.py +0 -0
  112. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/.claude/CLAUDE.md +0 -0
  113. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/.vitepress/theme/index.ts +0 -0
  114. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
  115. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/AGENTS.md +0 -0
  116. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/explanation.md +0 -0
  117. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/getting-started.md +0 -0
  118. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/howto/columns-from-paths.md +0 -0
  119. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/howto/define-tables.md +0 -0
  120. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/howto/embed.md +0 -0
  121. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/howto/extract-from-contents.md +0 -0
  122. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/howto/load-extension.md +0 -0
  123. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/howto/parse-files-into-columns.md +0 -0
  124. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/howto/persist.md +0 -0
  125. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/howto/query-without-config.md +0 -0
  126. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/howto/react-to-changes.md +0 -0
  127. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/howto/skip-files.md +0 -0
  128. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/index.md +0 -0
  129. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/migrations.md +0 -0
  130. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/package.json +0 -0
  131. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/pnpm-lock.yaml +0 -0
  132. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/pnpm-workspace.yaml +0 -0
  133. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/reference/columns.md +0 -0
  134. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/reference/http-api.md +0 -0
  135. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/reference/path-tables.md +0 -0
  136. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/docs/reference/sdk.md +0 -0
  137. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/migrations.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
  138. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/migrations.d/2026-07-13-rename-extract-to-on-file.md +0 -0
  139. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/migrations.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
  140. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/migrations.d/2026-07-20-drop-implicit-files-table.md +0 -0
  141. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/migrations.d/2026-07-21-remove-glob-captures.md +0 -0
  142. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/migrations.d/2026-07-24-hookless-table-config-error.md +0 -0
  143. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/migrations.d/2026-07-24-remove-stat-fact-injection.md +0 -0
  144. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/migrations.d/2026-07-29-restore-python-310.md +0 -0
  145. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/migrations.d/2026-08-04-cli-in-process.md +0 -0
  146. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/migrations.d/README.md +0 -0
  147. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/src/lib.rs +0 -0
  148. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/tests/__init__.py +0 -0
  149. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/tests/conftest.py +0 -0
  150. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/tests/e2e/__init__.py +0 -0
  151. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/__init__.py +0 -0
  152. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/dirsql.toml +0 -0
  153. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/tests/integration/__init__.py +0 -0
  154. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/tests/integration/binding/__init__.py +0 -0
  155. {dirsql-0.4.16 → dirsql-0.4.18}/packages/python/tests/integration/hermetic/__init__.py +0 -0
  156. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/CHANGELOG.md +0 -0
  157. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/MIGRATIONS.md +0 -0
  158. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/README.md +0 -0
  159. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/benches/db_bench.rs +0 -0
  160. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/benches/differ_bench.rs +0 -0
  161. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/benches/matcher_bench.rs +0 -0
  162. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/benches/scanner_bench.rs +0 -0
  163. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-07-13-batch-ingest-transaction.md +0 -0
  164. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-07-13-chained-pre-post-query-hooks.md +0 -0
  165. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
  166. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-07-13-fan-out-file-table-matching.md +0 -0
  167. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-07-13-persist-wal.md +0 -0
  168. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-07-13-rename-extract-to-on-file.md +0 -0
  169. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-07-13-repeatable-builder-config.md +0 -0
  170. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-07-13-repeatable-cli-config.md +0 -0
  171. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
  172. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-07-13-subcommand-local-config-flags.md +0 -0
  173. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-07-19-path-table-query-fallback.md +0 -0
  174. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-07-19-path-table-vtab.md +0 -0
  175. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-07-20-drop-implicit-files-table.md +0 -0
  176. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-07-20-path-table-glob-semantics.md +0 -0
  177. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-07-20-schema-inference.md +0 -0
  178. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-07-21-on-file-flag.md +0 -0
  179. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-07-21-remove-glob-captures.md +0 -0
  180. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-07-23-init-escalation-example.md +0 -0
  181. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-07-23-query-default-cli.md +0 -0
  182. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-07-24-hookless-table-config-error.md +0 -0
  183. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-07-24-remove-stat-fact-injection.md +0 -0
  184. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-07-29-on-file-failures-accumulate.md +0 -0
  185. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-08-02-drop-regex.md +0 -0
  186. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-08-02-per-file-scan-failures.md +0 -0
  187. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-08-02-release-profile.md +0 -0
  188. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-08-03-gitignore-by-default.md +0 -0
  189. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-08-03-nested-default-ignores.md +0 -0
  190. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-08-04-cast-lints.md +0 -0
  191. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-08-04-cli-run-shim.md +0 -0
  192. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-08-04-configless-extension.md +0 -0
  193. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/2026-08-10-remove-query-hooks.md +0 -0
  194. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/changelog.d/README.md +0 -0
  195. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/docs/explanation.md +0 -0
  196. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/docs/getting-started.md +0 -0
  197. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/docs/howto/columns-from-paths.md +0 -0
  198. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/docs/howto/define-tables.md +0 -0
  199. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/docs/howto/embed.md +0 -0
  200. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/docs/howto/extract-from-contents.md +0 -0
  201. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/docs/howto/load-extension.md +0 -0
  202. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/docs/howto/parse-files-into-columns.md +0 -0
  203. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/docs/howto/persist.md +0 -0
  204. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/docs/howto/query-without-config.md +0 -0
  205. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/docs/howto/react-to-changes.md +0 -0
  206. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/docs/howto/skip-files.md +0 -0
  207. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/docs/index.md +0 -0
  208. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/docs/migrations.md +0 -0
  209. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/docs/reference/columns.md +0 -0
  210. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/docs/reference/http-api.md +0 -0
  211. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/docs/reference/path-tables.md +0 -0
  212. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/docs/reference/sdk.md +0 -0
  213. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/migrations.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
  214. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/migrations.d/2026-07-13-fan-out-file-table-matching.md +0 -0
  215. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/migrations.d/2026-07-13-persist-wal.md +0 -0
  216. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/migrations.d/2026-07-13-rename-extract-to-on-file.md +0 -0
  217. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/migrations.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
  218. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/migrations.d/2026-07-13-subcommand-local-config-flags.md +0 -0
  219. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/migrations.d/2026-07-20-drop-implicit-files-table.md +0 -0
  220. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/migrations.d/2026-07-20-path-table-glob-semantics.md +0 -0
  221. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/migrations.d/2026-07-21-remove-glob-captures.md +0 -0
  222. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/migrations.d/2026-07-23-init-escalation-example.md +0 -0
  223. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/migrations.d/2026-07-23-query-default-cli.md +0 -0
  224. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/migrations.d/2026-07-24-hookless-table-config-error.md +0 -0
  225. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/migrations.d/2026-07-24-remove-stat-fact-injection.md +0 -0
  226. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/migrations.d/2026-08-02-per-file-scan-failures.md +0 -0
  227. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/migrations.d/2026-08-03-gitignore-by-default.md +0 -0
  228. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/migrations.d/2026-08-03-nested-default-ignores.md +0 -0
  229. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/migrations.d/2026-08-10-remove-query-hooks.md +0 -0
  230. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/migrations.d/README.md +0 -0
  231. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/src/bin/dirsql.rs +0 -0
  232. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/src/cli/init.rs +0 -0
  233. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/src/cli/mod.rs +0 -0
  234. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/src/cli/serialize.rs +0 -0
  235. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/src/cli/server.rs +0 -0
  236. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/src/db.rs +0 -0
  237. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/src/default_config.toml +0 -0
  238. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/src/differ.rs +0 -0
  239. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/src/infer.rs +0 -0
  240. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/src/matcher.rs +0 -0
  241. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/src/path_table.rs +0 -0
  242. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/src/persist.rs +0 -0
  243. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/src/scanner.rs +0 -0
  244. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/src/vtab.rs +0 -0
  245. {dirsql-0.4.16 → dirsql-0.4.18}/packages/rust/src/watcher.rs +0 -0
  246. {dirsql-0.4.16 → dirsql-0.4.18}/pyproject.toml +0 -0
@@ -484,7 +484,6 @@ dependencies = [
484
484
  "tokio",
485
485
  "tokio-stream",
486
486
  "toml",
487
- "wait-timeout",
488
487
  "walkdir",
489
488
  ]
490
489
 
@@ -500,7 +499,7 @@ dependencies = [
500
499
 
501
500
  [[package]]
502
501
  name = "dirsql-py-ext"
503
- version = "0.4.16"
502
+ version = "0.4.18"
504
503
  dependencies = [
505
504
  "dirsql",
506
505
  "pyo3",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dirsql
3
- Version: 0.4.16
3
+ Version: 0.4.18
4
4
  Requires-Dist: tomli>=2 ; python_full_version < '3.11'
5
5
  Requires-Dist: bin-shim>=0.1
6
6
  Summary: Ephemeral SQL index over a local directory
@@ -54,6 +54,7 @@ export default defineConfig({
54
54
  { text: 'Derive columns from file paths', link: '/howto/columns-from-paths' },
55
55
  { text: 'Extract rows from file contents', link: '/howto/extract-from-contents' },
56
56
  { text: 'Parse your files into columns', link: '/howto/parse-files-into-columns' },
57
+ { text: 'Search documents by meaning', link: '/howto/search-by-meaning' },
57
58
  { text: "Skip files you don't want indexed", link: '/howto/skip-files' },
58
59
  { text: 'Load a SQLite extension', link: '/howto/load-extension' },
59
60
  { text: 'Keep the index across restarts', link: '/howto/persist' },
@@ -0,0 +1,114 @@
1
+ # Search documents by meaning
2
+
3
+ Ask a question in plain language and get the closest documents back — even
4
+ when they share no keywords with it. Install
5
+ [`dirsql-plugin-embeddings`](../plugins.md#dirsql-plugin-embeddings) and
6
+ semantic search becomes plain SQL: the plugin's `embed()` function turns
7
+ text into vectors, [`sqlite-vec`](https://github.com/asg017/sqlite-vec)'s
8
+ `vec_distance_cosine()` measures distance, and `ORDER BY … LIMIT` does the
9
+ ranking. No config, no API keys, no services — the model runs locally.
10
+
11
+ Suppose short notes live in `notes/*.md`:
12
+
13
+ ```
14
+ notes/pasta.md # boiling spaghetti, olive oil, garlic
15
+ notes/branches.md # git feature branches and pull requests
16
+ notes/tomatoes.md # planting tomato seedlings after the last frost
17
+ ```
18
+
19
+ ## The one-liner
20
+
21
+ The plugin package is also its own command. Give it a corpus glob and a
22
+ question, and it prints the closest paths, ranked by distance:
23
+
24
+ ```bash
25
+ uvx dirsql-plugin-embeddings 'notes/*.md' "how do I cook pasta?" -k 3
26
+ ```
27
+
28
+ - The **corpus glob is required**, and always first: the plugin never picks a
29
+ default corpus, so you always say exactly which files are in scope. A bare
30
+ glob is fine — the command normalizes it to the `./`-relative form the SQL
31
+ layer requires.
32
+ - The question is the second positional.
33
+ - `-k` / `--limit` (both spellings, default 10) is the number of results —
34
+ it is exactly the SQL `LIMIT` of the generated query; there is no other
35
+ cutoff.
36
+ - `--model <id>` switches the embedding model
37
+ ([model story](../plugins.md#model)).
38
+
39
+ The first-ever run downloads the model (on the order of a hundred megabytes,
40
+ with progress on stderr); after that it loads from the local cache. Results
41
+ print one `path<TAB>distance` line per match, closest first.
42
+
43
+ ## The SQL behind it
44
+
45
+ The one-liner generates and runs ordinary `dirsql` SQL, and you can write it
46
+ yourself when you want more than ranked paths — a different projection, a
47
+ join, a `WHERE` clause, a subset of a JSON file's content:
48
+
49
+ ```bash
50
+ uvx --with dirsql-plugin-embeddings dirsql "
51
+ SELECT path,
52
+ vec_distance_cosine(emb, embed('how do I cook pasta?')) AS distance
53
+ FROM (SELECT path, embed(content) AS emb FROM './notes/*.md')
54
+ ORDER BY distance
55
+ LIMIT 3"
56
+ ```
57
+
58
+ ```json
59
+ [{"path":"notes/pasta.md","distance":0.315},{"path":"notes/tomatoes.md","distance":0.881},{"path":"notes/branches.md","distance":0.92}]
60
+ ```
61
+
62
+ Neither "cook" nor any other keyword needs to appear in `pasta.md` — the
63
+ distance ranking is doing the work.
64
+
65
+ Reading the query inside-out:
66
+
67
+ 1. The subquery scans the [path-table](../reference/path-tables.md)
68
+ `'./notes/*.md'` and embeds each file's
69
+ [`content`](../reference/path-tables.md#columns) — only the files the
70
+ glob matches are ever read or embedded. In hand-written SQL the `./`
71
+ prefix is [required](../reference/path-tables.md#writing-the-path); only
72
+ the one-liner normalizes a bare glob for you.
73
+ 2. `embed('how do I cook pasta?')` embeds the question once (the function is
74
+ deterministic, so SQLite reuses the value across rows).
75
+ 3. `vec_distance_cosine(...)` computes cosine distance between the two
76
+ vectors; `ORDER BY distance LIMIT 3` keeps the three nearest.
77
+
78
+ Structured files compose with SQL's JSON operators — embed one field instead
79
+ of the whole file:
80
+
81
+ ```sql
82
+ SELECT path
83
+ FROM (SELECT path, embed(content ->> 'abstract') AS emb
84
+ FROM './papers/**/metadata.json')
85
+ ORDER BY vec_distance_cosine(emb, embed('local private models'))
86
+ LIMIT 10
87
+ ```
88
+
89
+ ::: tip Top-k is `LIMIT k`
90
+ If you know `sqlite-vec` you may reach for its `MATCH … AND k = 10` idiom.
91
+ That syntax belongs to `sqlite-vec`'s `vec0` virtual table, which `dirsql`
92
+ does not use — `dirsql` tables are per-file row tables. For plain
93
+ expressions, `sqlite-vec`'s own documented pattern is exactly what this
94
+ guide uses: `ORDER BY vec_distance_cosine(...) LIMIT k`.
95
+ :::
96
+
97
+ ## Repeat runs are cheap
98
+
99
+ Computed vectors are cached on disk, keyed on content and model
100
+ ([vector cache](../plugins.md#vector-cache)) — re-running a search over
101
+ unchanged files skips the model entirely and re-embeds only what changed.
102
+ And the plugin costs nothing when idle: a query that never calls `embed()`
103
+ spawns no worker and loads no model
104
+ ([zero cost when unused](../plugins.md#zero-cost-when-unused)).
105
+
106
+ ## How `embed()` gets into SQL
107
+
108
+ The plugin ships a config fragment declaring `embed()` via
109
+ [`[[dirsql.function]]`](../reference/config.md#dirsql-function), which the
110
+ `uvx`/`pip` launcher [discovers automatically](../reference/cli.md#plugins).
111
+ The same mechanism is open to your own configs and plugins — any external
112
+ command that speaks the
113
+ [worker protocol](../reference/config.md#worker-protocol) can back a SQL
114
+ function. To build one, see [Write a plugin](./write-a-plugin.md).
@@ -54,8 +54,10 @@ structurally **identical to a user config**. It may declare
54
54
  [`[[table]]`](../reference/config.md#table) (with
55
55
  [`on-file`](../reference/hooks.md#on-file)),
56
56
  [`[[dirsql.extension]]`](../reference/config.md#dirsql-extension),
57
- [`ignore`](../reference/config.md#dirsql-keys), and
58
- [`hook-timeout`](../reference/hooks.md#timeout).
57
+ [`[[dirsql.function]]`](../reference/config.md#dirsql-function) (how
58
+ [`dirsql-plugin-embeddings`](../plugins.md#dirsql-plugin-embeddings) provides
59
+ its `embed()` SQL function),
60
+ and [`ignore`](../reference/config.md#dirsql-keys).
59
61
 
60
62
  There are **no plugin-specific keys and no plugin-specific restrictions**. The
61
63
  config schema is content-only: the index `root` and `--persist` are
@@ -101,9 +103,6 @@ distance math, and an `on-file` command to embed each file.
101
103
  The fragment, `src/dirsql_embeddings/dirsql.toml`:
102
104
 
103
105
  ```toml
104
- [dirsql]
105
- hook-timeout = 300 # headroom for the first-run model download
106
-
107
106
  [[dirsql.extension]]
108
107
  path = "sqlite_vec"
109
108
  entrypoint = "sqlite3_vec_init"
@@ -202,7 +201,8 @@ Discovery is deliberately narrow. Know exactly who does what:
202
201
  [convention above](#sdk-style-convention-expose-the-config)).
203
202
  - **Name collisions are a hard error.** Because fragments compose like any
204
203
  [multiple configs](../reference/config.md#composing-multiple-configs), two
205
- plugins (or a plugin and your config) defining a table of the same name
204
+ plugins (or a plugin and your config) defining a table or declaring a
205
+ [function](../reference/config.md#dirsql-function) — of the same name
206
206
  fail loudly, naming the conflict. It is never a silent
207
207
  last-writer-wins.
208
208
 
@@ -0,0 +1,100 @@
1
+ # Plugins
2
+
3
+ A **plugin** is an ordinary Python package that ships a `dirsql.toml` config
4
+ fragment and declares itself via a `dirsql` entry point. Installing it in the
5
+ same environment as `dirsql` activates it: the `pip`/`uvx` launcher discovers
6
+ the package and loads its fragment automatically, with zero config edits. The
7
+ full discovery contract (ordering, opt-out, failure modes) is in the
8
+ [CLI reference](./reference/cli.md#plugins); to build your own, see
9
+ [Write a plugin](./howto/write-a-plugin.md).
10
+
11
+ This page lists the first-party plugins.
12
+
13
+ ## `dirsql-plugin-embeddings`
14
+
15
+ Semantic search over files. The plugin's product is content → vectors: it
16
+ declares an `embed()` SQL scalar function (via
17
+ [`[[dirsql.function]]`](./reference/config.md#dirsql-function)) that turns
18
+ TEXT or BLOB values into embedding vectors, and loads
19
+ [`sqlite-vec`](https://github.com/asg017/sqlite-vec) so
20
+ `vec_distance_cosine()` and friends do the distance math. You scope the
21
+ search with an ordinary [path-table](./reference/path-tables.md) glob, rank
22
+ with `ORDER BY`, and cut with `LIMIT` — search is plain SQL.
23
+
24
+ [PyPI](https://pypi.org/project/dirsql-plugin-embeddings/) ·
25
+ [Source](https://github.com/thekevinscott/dirsql/tree/main/plugins/dirsql-plugin-embeddings)
26
+
27
+ ### Install and launch
28
+
29
+ The plugin is a normal PyPI package; installing it alongside `dirsql` is the
30
+ whole install story (installed = active — there is no enable step, and no
31
+ configuration at all):
32
+
33
+ ```sh
34
+ uvx --with dirsql-plugin-embeddings dirsql "
35
+ SELECT path
36
+ FROM (SELECT path, embed(content ->> 'abstract') AS emb
37
+ FROM './arxiv-firehose/data/**/metadata.json')
38
+ ORDER BY vec_distance_cosine(emb, embed('local private models'))
39
+ LIMIT 10"
40
+ ```
41
+
42
+ The launcher finds the package through its `dirsql` entry point and injects
43
+ the shipped `dirsql.toml` fragment as an ordinary `-c` flag, composed after
44
+ your own configs. The fragment declares the `sqlite-vec` extension (resolved
45
+ from the installed `sqlite-vec` package, which the plugin depends on) and the
46
+ `embed()` function entry. Discovery can be turned off per-invocation with
47
+ `--no-plugin` or `DIRSQL_NO_PLUGIN=1`
48
+ ([reference](./reference/cli.md#plugins)).
49
+
50
+ For the common case — one glob, one question, top-k paths — the package is
51
+ also its own command, generating and running exactly that SQL:
52
+
53
+ ```sh
54
+ uvx dirsql-plugin-embeddings '**/*.md' "local private models" -k 10
55
+ ```
56
+
57
+ The corpus glob is a **required** first positional — the plugin never picks a
58
+ default corpus for you — and a bare glob is normalized to the `./`-relative
59
+ form the SQL layer requires. Results print as ranked `path<TAB>distance`
60
+ lines, closest first. See
61
+ [Search documents by meaning](./howto/search-by-meaning.md) for the guide to
62
+ both styles.
63
+
64
+ ### Zero cost when unused
65
+
66
+ `embed()` is [inert until called](./reference/config.md#worker-lifecycle):
67
+ installing the plugin changes nothing for queries that never call it. No
68
+ worker process is spawned, no model is loaded or downloaded, and no cache is
69
+ touched. Only what a query's glob actually selects is ever embedded — the
70
+ worker receives **values, not paths**, and never opens files itself.
71
+
72
+ ### Model
73
+
74
+ Embeddings come from [model2vec](https://github.com/MinishLab/model2vec)
75
+ static models — inference needs numpy and tokenizers only, no torch, so the
76
+ plugin stays light enough for `uvx` ephemeral environments and is fast on
77
+ CPU. The default model is
78
+ [`minishlab/potion-retrieval-32M`](https://huggingface.co/minishlab/potion-retrieval-32M).
79
+
80
+ The very first `embed()` call downloads the model (on the order of a hundred
81
+ megabytes — expect seconds to a few minutes depending on your connection,
82
+ with progress on stderr) into the standard Hugging Face cache
83
+ (`~/.cache/huggingface`), which persists across `uvx` environments; every
84
+ later run loads it from disk.
85
+
86
+ Override the model per call with the optional second argument —
87
+ `embed(text, 'model-id')` — or per run with the one-liner's `--model` flag,
88
+ which templates the same second argument. The id must be a
89
+ **model2vec-loadable** model; sentence-transformers/torch models are out of
90
+ scope.
91
+
92
+ ### Vector cache
93
+
94
+ Computed vectors are cached at `~/.cache/dirsql/embeddings/` (respecting
95
+ `XDG_CACHE_HOME`), keyed on the SHA-256 of the value bytes plus the model
96
+ identifier. Changing either recomputes — switching models never serves stale
97
+ vectors — and re-running a query over unchanged files is cache hits all the
98
+ way. There is no eviction: the directory is **safe to wipe at any time**; the
99
+ only cost is re-embedding. The cache never lives inside a queried tree — the
100
+ worker writes nothing into the directories you query.
@@ -76,11 +76,12 @@ Config flags are subcommand-local: pass them after `server`
76
76
 
77
77
  ### Defaults
78
78
 
79
- - Per-query timeout: **30 seconds**. A query exceeding it returns
80
- `408 Request Timeout`.
81
- - `on-file` command hooks default to a **30-second** timeout each,
82
- overridable with the config key
83
- [`[dirsql].hook-timeout`](./config.md#dirsql-keys).
79
+ - Per-query timeout: **30 seconds**, in server mode only. A query exceeding
80
+ it returns `408 Request Timeout`. One-shot [`dirsql query`](#dirsql-query)
81
+ has no built-in timeout.
82
+ - `on-file` command hooks run **unbounded**; bound one by wrapping its
83
+ command in `timeout(1)` (see
84
+ [Bounding a hook](./hooks.md#bounding-a-hook)).
84
85
 
85
86
  ### Configless mode
86
87
 
@@ -166,13 +167,17 @@ uses**, so behavior is identical to `POST /query` by construction:
166
167
  (`--persist=/path`) so it does not swallow the SQL argument.
167
168
  - **`--no-ignore`** is honored: path-tables in the query scan files a
168
169
  `.gitignore` would hide. See [Skip rules](./path-tables.md#skip-rules).
169
- - **`on-file` hooks** and the
170
- [`[dirsql].hook-timeout`](./config.md#dirsql-keys) apply identically.
171
- - The **30-second query timeout**, the **read-only rule**, and the
172
- `_dirsql_*` **internal-table denial** apply identically. A rejected read
173
- is an error, not empty output. The read-only rule here governs SQL
174
- statements; dirsql separately never modifies the files it indexes — see
170
+ - **`on-file` hooks** apply identically (unbounded; wrap in `timeout(1)` to
171
+ bound — see [Bounding a hook](./hooks.md#bounding-a-hook)).
172
+ - The **read-only rule** and the `_dirsql_*` **internal-table denial** apply
173
+ identically. A rejected read is an error, not empty output. The read-only
174
+ rule here governs SQL statements; dirsql separately never modifies the
175
+ files it indexes — see
175
176
  [Read-only by design](../explanation#read-only-by-design).
177
+ - **No per-query timeout.** Unlike the server's 30-second bound (`408`),
178
+ a one-shot query runs to completion — the process *is* the query, so
179
+ cap it from the shell if you want one: `timeout 60 dirsql query "<sql>"`
180
+ (see `timeout(1)`).
176
181
 
177
182
  #### `--on-file <command>`
178
183
 
@@ -1,11 +1,13 @@
1
1
  # Configuration file (`.dirsql.toml`)
2
2
 
3
3
  `.dirsql.toml` is a TOML file with one optional `[dirsql]` section, zero or
4
- more `[[dirsql.extension]]` entries, and zero or more `[[table]]` entries.
4
+ more `[[dirsql.extension]]` entries, zero or more `[[dirsql.function]]`
5
+ entries, and zero or more `[[table]]` entries.
5
6
  An empty file is valid. A missing `[dirsql]` section behaves as an
6
7
  all-defaults one. Unknown keys are a parse error at every level (top level,
7
- `[dirsql]`, `[[table]]`, `[[dirsql.extension]]`) — a typo or a removed key
8
- fails loudly, naming the offending key, rather than silently no-opping.
8
+ `[dirsql]`, `[[table]]`, `[[dirsql.extension]]`, `[[dirsql.function]]`) — a
9
+ typo or a removed key fails loudly, naming the offending key, rather than
10
+ silently no-opping.
9
11
 
10
12
  The [CLI](./cli.md) loads a config only when you pass it with `-c/--config`;
11
13
  with none given [no named tables](./cli.md#configless-mode) are defined (a
@@ -23,7 +25,12 @@ the config file's location. See [`--config`](./cli.md#flags).
23
25
  | Key | Type | Default | Description |
24
26
  |---|---|---|---|
25
27
  | `ignore` | array of strings | `[]` | Glob patterns matched against root-relative paths. Matched files are skipped entirely — excluded from the initial scan and from watch events. |
26
- | `hook-timeout` | integer (seconds) | `30` | One global per-run timeout for every `on-file` command hook run. Positive whole seconds; zero and negative values are a config error. See [Command hooks](./hooks.md#timeout). |
28
+
29
+ There is no timeout key. `on-file` hook runs are unbounded; to bound one, wrap
30
+ its command in `timeout(1)` (see [Command hooks](./hooks.md#bounding-a-hook)).
31
+ A config that still declares the removed `hook-timeout` key fails to load with
32
+ an error naming that replacement. [`[[dirsql.function]]`](#dirsql-function)
33
+ worker calls have their own per-call `timeout` key (default 30 seconds).
27
34
 
28
35
  The top-level `.dirsql/` directory under the root is always excluded from
29
36
  scanning, whether or not it appears in `ignore` — it is reserved for
@@ -34,7 +41,6 @@ directory.
34
41
  ```toml
35
42
  [dirsql]
36
43
  ignore = ["node_modules/**", ".git/**"]
37
- hook-timeout = 300
38
44
  ```
39
45
 
40
46
  Persistence is not a config key. Keep the SQLite index on disk between runs
@@ -86,6 +92,72 @@ extension-backed **virtual table** cannot be declared as a `[[table]]` —
86
92
  `dirsql` tables are per-file row tables, so a `CREATE VIRTUAL TABLE` DDL is
87
93
  rejected; call the extension's functions in queries instead.
88
94
 
95
+ ## `[[dirsql.function]]`
96
+
97
+ Each entry declares a **worker-backed SQL scalar function**: a function
98
+ queries can call by name, whose values are computed by an external worker
99
+ process you (or a [plugin](../plugins.md)) provide. This is how a plugin adds
100
+ computed values — an embedding, a hash, a classification — to SQL without
101
+ `dirsql` knowing anything about the domain: the config names the function and
102
+ the command, the worker does the work. The first-party
103
+ [`dirsql-plugin-embeddings`](../plugins.md#dirsql-plugin-embeddings) declares
104
+ its `embed()` function exactly this way.
105
+
106
+ | Key | Required | Description |
107
+ |---|---|---|
108
+ | `name` | yes | The SQL name queries call. Must be a plain identifier — an ASCII letter or underscore followed by ASCII letters, digits, or underscores. |
109
+ | `args` | yes | The accepted arities (argument counts), each `0`–`127`. The function is registered once per listed arity, so `args = [1, 2]` makes both `f(x)` and `f(x, y)` callable and any other count a SQL error. An empty list, an out-of-range value, or a repeated value is a config error. |
110
+ | `command` | yes (non-empty) | The worker command. Argv-split with the same no-shell quoting rules as [command hooks](./hooks.md#argv-not-a-shell); runs in the config file's directory. |
111
+ | `deterministic` | no (default `false`) | When `true`, the function is registered with `SQLITE_DETERMINISTIC`, letting SQLite cache and reuse results for identical arguments within a query. Only set it when the worker really is a pure function of its arguments. |
112
+ | `timeout` | no | Per-**call** time bound: a positive integer is whole seconds (`timeout = 600`), a string is an integer suffixed `s` or `ms` (`"600s"`, `"250ms"`). When absent, the function mechanism's own 30-second default applies. |
113
+
114
+ ```toml
115
+ [[dirsql.function]]
116
+ name = "embed"
117
+ args = [1, 2]
118
+ command = "dirsql-plugin-embeddings worker"
119
+ deterministic = true
120
+ timeout = "600s" # generous: absorbs a first-call model download
121
+ ```
122
+
123
+ ### Worker lifecycle
124
+
125
+ Declaring a function is **inert**: at startup the function is registered on
126
+ the connection and nothing else happens. No process is spawned and nothing is
127
+ read until a query actually calls the function — a declared function nobody
128
+ calls costs nothing.
129
+
130
+ On the **first call**, `dirsql` spawns `command` and keeps that one worker
131
+ process alive for the rest of the invocation, sending it every subsequent
132
+ call — one process total, never one per row or per file. The worker is torn
133
+ down when the invocation ends.
134
+
135
+ The `timeout` bounds each **round-trip call**, not the query: a query that
136
+ calls the function on 10 000 rows is 10 000 individually timed calls. A call
137
+ that times out, or a worker that crashes or closes its pipes, fails the query
138
+ with an actionable error naming the function and command; the worker is
139
+ killed and the next call starts a fresh one.
140
+
141
+ Calling a function that no loaded config declares (say, the plugin providing
142
+ it is not installed) is SQLite's ordinary `no such function` error.
143
+
144
+ ### Worker protocol
145
+
146
+ The worker speaks **newline-delimited JSON** over its stdin/stdout — one
147
+ request line in, one response line out, per call:
148
+
149
+ - **Request:** `{"call": [<arg>, ...]}` with the call's SQL arguments
150
+ encoded as: TEXT → JSON string, INTEGER/REAL → JSON number, NULL → `null`,
151
+ BLOB → `{"$bytes": "<base64>"}`.
152
+ - **Response:** `{"ok": <value>}` with the same scalar encodings — a JSON
153
+ array or any other object is bound as TEXT, its JSON text (which is how an
154
+ embedding worker returns a vector: `sqlite-vec`'s distance functions accept
155
+ JSON-text vectors) — or `{"err": "message"}`, which **fails the query**
156
+ with that message. An `{"err": ...}` response leaves the healthy worker
157
+ running; only transport failures (timeout, crash) recycle it.
158
+ - **stderr passes through** to `dirsql`'s stderr, so a worker's progress
159
+ bars and download logs reach the terminal.
160
+
89
161
  ## `[[table]]`
90
162
 
91
163
  Each entry maps a glob pattern to a SQL table. A table's columns are exactly
@@ -143,16 +215,18 @@ dirsql -c ./.dirsql.toml -c ~/team/embeddings.toml -c ./local.toml
143
215
 
144
216
  The configs load and merge in **argv order**:
145
217
 
146
- - **`[[table]]`, `ignore`, and `[[dirsql.extension]]` entries accumulate** across
147
- all configs, in order.
148
- - **Each config's `on-file` hooks run from that config file's own
149
- directory**, under that config's own [`hook-timeout`](#dirsql-keys) — so a
150
- relative command like `on-file = "sh ./extract.sh"` resolves against the
151
- config that declared it, wherever it lives.
218
+ - **`[[table]]`, `ignore`, `[[dirsql.extension]]`, and `[[dirsql.function]]`
219
+ entries accumulate** across all configs, in order.
220
+ - **Each config's `on-file` hooks and `[[dirsql.function]]` workers run from
221
+ that config file's own directory** — so a relative command like
222
+ `on-file = "sh ./extract.sh"` resolves against the config that declared it,
223
+ wherever it lives.
152
224
  - Each config is **validated on its own** (the [parse errors](#parse-errors)
153
- below apply per file). There is no cross-file merge validation, with one
154
- structural exception: **two configs defining a table of the same name is an
155
- error**, naming the table.
225
+ below apply per file). There is no cross-file merge validation, with two
226
+ structural exceptions: **two configs defining a table of the same name is
227
+ an error**, naming the table, and **two configs declaring a function of the
228
+ same name is an error**, naming the function and both sources — never a
229
+ silent last-writer-wins.
156
230
 
157
231
  The index [root](./cli.md#flags) is the invocation directory regardless of where
158
232
  any config lives. With no `-c`, [no named tables](./cli.md#configless-mode) are
@@ -174,20 +248,32 @@ SDKs raise/reject) when:
174
248
  > `[[table]] '**/*.md' has no on-file hook, so every row would be all-NULL. Add an `on-file` hook that emits the columns, or, for stat columns with no code, query the path directly: `FROM './'``
175
249
 
176
250
  - A `[[dirsql.extension]]` entry omits `path`, or `path` is empty.
177
- - `hook-timeout` is zero or negative.
251
+ - A `[[dirsql.function]]` entry omits `name`, `command`, or `args` (or
252
+ `command` is empty/whitespace); its `name` is not a plain identifier; its
253
+ `args` list is empty, repeats an arity, or lists one outside `0`–`127`; or
254
+ its `timeout` is not positive whole seconds / a positive-integer `"...s"` or
255
+ `"...ms"` string.
256
+ - `[dirsql]` declares the removed `hook-timeout` key (the error names the
257
+ `timeout(1)` replacement).
178
258
 
179
259
  ## Full example
180
260
 
181
261
  ```toml
182
262
  [dirsql]
183
263
  ignore = ["node_modules/**", ".git/**", "dist/**"]
184
- hook-timeout = 120
185
264
 
186
265
  [[dirsql.extension]]
187
266
  path = "sqlite_vec" # Python module name; on Node use the
188
267
  # platform package, e.g. sqlite-vec-linux-x64
189
268
  entrypoint = "sqlite3_vec_init"
190
269
 
270
+ [[dirsql.function]]
271
+ name = "embed"
272
+ args = [1, 2]
273
+ command = "dirsql-plugin-embeddings worker"
274
+ deterministic = true
275
+ timeout = "600s"
276
+
191
277
  [[table]]
192
278
  ddl = "CREATE TABLE comments (author TEXT, body TEXT)"
193
279
  glob = "_comments/*/*.jsonl"
@@ -58,18 +58,30 @@ everything above the last line. `jq` users: pass `-c` so the JSON is emitted
58
58
  compactly on one line.
59
59
  :::
60
60
 
61
- ### Timeout
61
+ ### Bounding a hook
62
62
 
63
- Every hook run is bounded by a **30-second** default timeout. A run
64
- exceeding it is killed and treated as a failure. One global config key
65
- raises (or tightens) the bound:
63
+ Hook runs are **unbounded** `dirsql` imposes no timeout of its own. To
64
+ bound a hook, make the bound part of the command by wrapping it in
65
+ `timeout(1)`:
66
66
 
67
67
  ```toml
68
- [dirsql]
69
- hook-timeout = 300 # positive whole seconds
68
+ on-file = "timeout 30 my-extractor {path}"
70
69
  ```
71
70
 
72
- Zero and negative values are a config error.
71
+ When the wrapper kills an overrunning command, the run exits non-zero and
72
+ the ordinary [failure semantics](#failure-semantics) apply — the file is
73
+ skipped, the scan continues.
74
+
75
+ ::: warning Windows
76
+ Windows's built-in `timeout` command is a *sleep*, not a bound — it cannot
77
+ wrap another command. On Windows, bound the work inside the command itself
78
+ (or accept unbounded runs).
79
+ :::
80
+
81
+ [`[[dirsql.function]]`](./config.md#dirsql-function) worker calls are
82
+ different: a call is a round-trip on a persistent worker process, which
83
+ `timeout(1)` cannot express, so the function mechanism carries its own
84
+ per-call `timeout` key with a 30-second default.
73
85
 
74
86
  ### Failure semantics
75
87
 
@@ -77,8 +89,8 @@ A hook run fails when the command:
77
89
 
78
90
  - cannot be spawned (e.g. the program is not found),
79
91
  - exits non-zero (the exit code — or `signal`, if killed by one — and the
80
- stderr tail are reported),
81
- - exceeds the timeout (killed; stderr tail reported),
92
+ stderr tail are reported; a `timeout(1)` wrapper killing an overrun lands
93
+ here),
82
94
  - exits zero but prints no non-empty stdout line,
83
95
  - or prints output that does not parse as a JSON array of row objects.
84
96
 
@@ -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.4.16"
7
+ version = "0.4.18"
8
8
  # abi3 note: the `abi3-py310` pyo3 feature (below) builds ONE stable-ABI
9
9
  # `cp310-abi3` wheel per platform that loads on every CPython >= 3.10
10
10
  # (matching `pyproject.toml`'s `requires-python = ">=3.10"`), instead of
@@ -54,6 +54,7 @@ export default defineConfig({
54
54
  { text: 'Derive columns from file paths', link: '/howto/columns-from-paths' },
55
55
  { text: 'Extract rows from file contents', link: '/howto/extract-from-contents' },
56
56
  { text: 'Parse your files into columns', link: '/howto/parse-files-into-columns' },
57
+ { text: 'Search documents by meaning', link: '/howto/search-by-meaning' },
57
58
  { text: "Skip files you don't want indexed", link: '/howto/skip-files' },
58
59
  { text: 'Load a SQLite extension', link: '/howto/load-extension' },
59
60
  { text: 'Keep the index across restarts', link: '/howto/persist' },