dirsql 0.3.74__tar.gz → 0.3.76__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 (138) hide show
  1. {dirsql-0.3.74 → dirsql-0.3.76}/Cargo.lock +1 -1
  2. {dirsql-0.3.74 → dirsql-0.3.76}/PKG-INFO +1 -1
  3. {dirsql-0.3.74/packages/rust → dirsql-0.3.76}/docs/reference/cli.md +11 -17
  4. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/Cargo.toml +1 -1
  5. {dirsql-0.3.74 → dirsql-0.3.76/packages/python}/docs/reference/cli.md +11 -17
  6. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/e2e-attestation.json +2 -2
  7. {dirsql-0.3.74/packages/python → dirsql-0.3.76/packages/rust}/docs/reference/cli.md +11 -17
  8. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/src/bin/dirsql.rs +17 -14
  9. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/src/cli/execute.rs +3 -8
  10. dirsql-0.3.76/packages/rust/src/cli/init.rs +93 -0
  11. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/src/cli/mod.rs +9 -0
  12. dirsql-0.3.76/packages/rust/src/default_config.toml +3 -0
  13. dirsql-0.3.74/packages/rust/src/cli/init.rs +0 -207
  14. {dirsql-0.3.74 → dirsql-0.3.76}/Cargo.toml +0 -0
  15. {dirsql-0.3.74 → dirsql-0.3.76}/README.md +0 -0
  16. {dirsql-0.3.74 → dirsql-0.3.76}/dirsql/__init__.py +0 -0
  17. {dirsql-0.3.74 → dirsql-0.3.76}/dirsql/_async.py +0 -0
  18. {dirsql-0.3.74 → dirsql-0.3.76}/dirsql/_dirsql.pyi +0 -0
  19. {dirsql-0.3.74 → dirsql-0.3.76}/dirsql/cli/__init__.py +0 -0
  20. {dirsql-0.3.74 → dirsql-0.3.76}/dirsql/cli/binary_path.py +0 -0
  21. {dirsql-0.3.74 → dirsql-0.3.76}/dirsql/cli/is_windows.py +0 -0
  22. {dirsql-0.3.74 → dirsql-0.3.76}/dirsql/cli/main.py +0 -0
  23. {dirsql-0.3.74 → dirsql-0.3.76}/dirsql/cli/resolve_config_extensions.py +0 -0
  24. {dirsql-0.3.74 → dirsql-0.3.76}/dirsql/py.typed +0 -0
  25. {dirsql-0.3.74 → dirsql-0.3.76}/dirsql/resolve_config_extensions.py +0 -0
  26. {dirsql-0.3.74 → dirsql-0.3.76}/dirsql/resolve_extension.py +0 -0
  27. {dirsql-0.3.74 → dirsql-0.3.76}/docs/.claude/CLAUDE.md +0 -0
  28. {dirsql-0.3.74 → dirsql-0.3.76}/docs/.vitepress/config.ts +0 -0
  29. {dirsql-0.3.74 → dirsql-0.3.76}/docs/.vitepress/theme/index.ts +0 -0
  30. {dirsql-0.3.74 → dirsql-0.3.76}/docs/.vitepress/theme/lang.ts +0 -0
  31. {dirsql-0.3.74 → dirsql-0.3.76}/docs/AGENTS.md +0 -0
  32. {dirsql-0.3.74 → dirsql-0.3.76}/docs/explanation.md +0 -0
  33. {dirsql-0.3.74 → dirsql-0.3.76}/docs/getting-started.md +0 -0
  34. {dirsql-0.3.74 → dirsql-0.3.76}/docs/howto/columns-from-paths.md +0 -0
  35. {dirsql-0.3.74 → dirsql-0.3.76}/docs/howto/define-tables.md +0 -0
  36. {dirsql-0.3.74 → dirsql-0.3.76}/docs/howto/embed.md +0 -0
  37. {dirsql-0.3.74 → dirsql-0.3.76}/docs/howto/extract-from-contents.md +0 -0
  38. {dirsql-0.3.74 → dirsql-0.3.76}/docs/howto/load-extension.md +0 -0
  39. {dirsql-0.3.74 → dirsql-0.3.76}/docs/howto/persist.md +0 -0
  40. {dirsql-0.3.74 → dirsql-0.3.76}/docs/howto/react-to-changes.md +0 -0
  41. {dirsql-0.3.74 → dirsql-0.3.76}/docs/howto/search-by-meaning.md +0 -0
  42. {dirsql-0.3.74 → dirsql-0.3.76}/docs/howto/skip-files.md +0 -0
  43. {dirsql-0.3.74 → dirsql-0.3.76}/docs/index.md +0 -0
  44. {dirsql-0.3.74 → dirsql-0.3.76}/docs/migrations.md +0 -0
  45. {dirsql-0.3.74 → dirsql-0.3.76}/docs/package.json +0 -0
  46. {dirsql-0.3.74 → dirsql-0.3.76}/docs/playwright.config.ts +0 -0
  47. {dirsql-0.3.74 → dirsql-0.3.76}/docs/pnpm-lock.yaml +0 -0
  48. {dirsql-0.3.74 → dirsql-0.3.76}/docs/pnpm-workspace.yaml +0 -0
  49. {dirsql-0.3.74 → dirsql-0.3.76}/docs/reference/columns.md +0 -0
  50. {dirsql-0.3.74 → dirsql-0.3.76}/docs/reference/config.md +0 -0
  51. {dirsql-0.3.74 → dirsql-0.3.76}/docs/reference/hooks.md +0 -0
  52. {dirsql-0.3.74 → dirsql-0.3.76}/docs/reference/http-api.md +0 -0
  53. {dirsql-0.3.74 → dirsql-0.3.76}/docs/reference/sdk.md +0 -0
  54. {dirsql-0.3.74 → dirsql-0.3.76}/docs/tests/integration/home.spec.ts +0 -0
  55. {dirsql-0.3.74 → dirsql-0.3.76}/docs/tests/integration/language-flag.spec.ts +0 -0
  56. {dirsql-0.3.74 → dirsql-0.3.76}/docs/tests/integration/sidebar.spec.ts +0 -0
  57. {dirsql-0.3.74 → dirsql-0.3.76}/docs/tests/unit/config.test.ts +0 -0
  58. {dirsql-0.3.74 → dirsql-0.3.76}/docs/tests/unit/lang.test.ts +0 -0
  59. {dirsql-0.3.74 → dirsql-0.3.76}/docs/vitest.config.ts +0 -0
  60. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/README.md +0 -0
  61. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/conftest.py +0 -0
  62. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/.claude/CLAUDE.md +0 -0
  63. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/.vitepress/config.ts +0 -0
  64. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/.vitepress/theme/index.ts +0 -0
  65. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
  66. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/AGENTS.md +0 -0
  67. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/explanation.md +0 -0
  68. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/getting-started.md +0 -0
  69. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/howto/columns-from-paths.md +0 -0
  70. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/howto/define-tables.md +0 -0
  71. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/howto/embed.md +0 -0
  72. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/howto/extract-from-contents.md +0 -0
  73. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/howto/load-extension.md +0 -0
  74. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/howto/persist.md +0 -0
  75. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/howto/react-to-changes.md +0 -0
  76. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/howto/search-by-meaning.md +0 -0
  77. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/howto/skip-files.md +0 -0
  78. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/index.md +0 -0
  79. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/migrations.md +0 -0
  80. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/package.json +0 -0
  81. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/playwright.config.ts +0 -0
  82. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/pnpm-lock.yaml +0 -0
  83. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/pnpm-workspace.yaml +0 -0
  84. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/reference/columns.md +0 -0
  85. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/reference/config.md +0 -0
  86. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/reference/hooks.md +0 -0
  87. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/reference/http-api.md +0 -0
  88. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/reference/sdk.md +0 -0
  89. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/tests/integration/home.spec.ts +0 -0
  90. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/tests/integration/language-flag.spec.ts +0 -0
  91. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/tests/integration/sidebar.spec.ts +0 -0
  92. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/tests/unit/config.test.ts +0 -0
  93. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/tests/unit/lang.test.ts +0 -0
  94. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/docs/vitest.config.ts +0 -0
  95. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/src/lib.rs +0 -0
  96. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/tests/__init__.py +0 -0
  97. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/tests/binding/__init__.py +0 -0
  98. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/tests/conftest.py +0 -0
  99. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/tests/e2e/__init__.py +0 -0
  100. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/tests/integration/__init__.py +0 -0
  101. {dirsql-0.3.74 → dirsql-0.3.76}/packages/python/tests/smoke/__init__.py +0 -0
  102. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/Cargo.toml +0 -0
  103. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/README.md +0 -0
  104. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/benches/db_bench.rs +0 -0
  105. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/benches/differ_bench.rs +0 -0
  106. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/benches/matcher_bench.rs +0 -0
  107. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/benches/scanner_bench.rs +0 -0
  108. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/docs/explanation.md +0 -0
  109. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/docs/getting-started.md +0 -0
  110. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/docs/howto/columns-from-paths.md +0 -0
  111. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/docs/howto/define-tables.md +0 -0
  112. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/docs/howto/embed.md +0 -0
  113. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/docs/howto/extract-from-contents.md +0 -0
  114. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/docs/howto/load-extension.md +0 -0
  115. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/docs/howto/persist.md +0 -0
  116. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/docs/howto/react-to-changes.md +0 -0
  117. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/docs/howto/search-by-meaning.md +0 -0
  118. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/docs/howto/skip-files.md +0 -0
  119. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/docs/index.md +0 -0
  120. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/docs/migrations.md +0 -0
  121. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/docs/reference/columns.md +0 -0
  122. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/docs/reference/config.md +0 -0
  123. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/docs/reference/hooks.md +0 -0
  124. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/docs/reference/http-api.md +0 -0
  125. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/docs/reference/sdk.md +0 -0
  126. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/src/cli/router.rs +0 -0
  127. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/src/cli/serialize.rs +0 -0
  128. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/src/cli/server.rs +0 -0
  129. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/src/command.rs +0 -0
  130. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/src/config.rs +0 -0
  131. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/src/db.rs +0 -0
  132. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/src/differ.rs +0 -0
  133. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/src/lib.rs +0 -0
  134. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/src/matcher.rs +0 -0
  135. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/src/persist.rs +0 -0
  136. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/src/scanner.rs +0 -0
  137. {dirsql-0.3.74 → dirsql-0.3.76}/packages/rust/src/watcher.rs +0 -0
  138. {dirsql-0.3.74 → dirsql-0.3.76}/pyproject.toml +0 -0
@@ -500,7 +500,7 @@ dependencies = [
500
500
 
501
501
  [[package]]
502
502
  name = "dirsql-py-ext"
503
- version = "0.3.74"
503
+ version = "0.3.76"
504
504
  dependencies = [
505
505
  "dirsql",
506
506
  "pyo3",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dirsql
3
- Version: 0.3.74
3
+ Version: 0.3.76
4
4
  Summary: Ephemeral SQL index over a local directory
5
5
  Keywords: sql,filesystem,directory,sqlite,index
6
6
  Author: Kevin Scott
@@ -5,8 +5,8 @@ The `dirsql` binary has three modes:
5
5
  | Invocation | Behavior |
6
6
  |---|---|
7
7
  | `dirsql` (no subcommand) | Start a long-lived HTTP server exposing a SQL view of a directory. See [HTTP API](./http-api.md). |
8
- | `dirsql query "<sql>"` | Build the index, run one query, print the rows as JSON, exit. No server, no watch. |
9
- | `dirsql init` | Generate a starter `.dirsql.toml` by running `claude` over a directory. |
8
+ | `dirsql query "<sql>"` | Query the file system as JSON |
9
+ | `dirsql init` | Generate a `.dirsql.toml` |
10
10
 
11
11
  ## Installation
12
12
 
@@ -100,8 +100,7 @@ with a JSON body describing the failure:
100
100
 
101
101
  ## `dirsql query`
102
102
 
103
- Run one SQL query from the shell — for ad-hoc inspection, scripting, and
104
- docs verification snippets — without booting the server and `curl`ing it:
103
+ Run a SQL query from the shell:
105
104
 
106
105
  ```bash
107
106
  dirsql query "SELECT basename, size FROM files ORDER BY size DESC LIMIT 5"
@@ -140,34 +139,29 @@ stderr, with exit code `1`.
140
139
 
141
140
  ## `dirsql init`
142
141
 
143
- Generates a `.dirsql.toml` by running the `claude` CLI over the target
144
- directory. The generated config contains only filesystem-fact tables
145
- (`[[table]]` entries whose columns come from [glob captures and virtual
146
- columns](./columns.md)) — never content-derived columns.
142
+ Writes a starter `.dirsql.toml`:
147
143
 
148
144
  ```bash
149
145
  dirsql init
150
146
  ```
151
147
 
148
+ You can further tweak this config as needed.
149
+
152
150
  ### Flags
153
151
 
154
152
  | Flag | Default | Description |
155
153
  |---|---|---|
156
- | `--root <path>` | current directory | Directory to scan. |
157
- | `--output <path>` | `<root>/.dirsql.toml` | Where to write the generated config. |
154
+ | `--root <path>` | current directory | Directory the default `--output` path is resolved against. |
155
+ | `--output <path>` | `<root>/.dirsql.toml` | Where to write the config. |
158
156
  | `--force` | off | Overwrite the output file if it already exists. |
159
157
 
160
158
  ### Requirements and failure modes
161
159
 
162
- `init` requires `claude` on `PATH`, signed in; there is no separate API key.
163
160
  All failures exit `1` with a message on stderr:
164
161
 
165
162
  | Condition | Behavior |
166
163
  |---|---|
167
- | Output file exists and `--force` not passed | Fails before invoking `claude` (no LLM call is made). |
168
- | `claude` not found on `PATH` | Fails with a pointer to the Claude Code install docs. |
169
- | `claude` exits non-zero | Fails with `claude`'s stderr; no partial config is written. |
170
- | `claude` produces non-UTF-8 output | Fails; nothing is written. |
164
+ | Output file exists and `--force` not passed | Fails; nothing is written. |
165
+ | Output path unwritable (e.g. missing parent directory) | Fails with the underlying I/O error. |
171
166
 
172
- On success, `claude`'s stdout is written verbatim to the output path and
173
- `init` exits `0`.
167
+ On success, `init` exits `0`.
@@ -4,7 +4,7 @@ name = "dirsql-py-ext"
4
4
  # pypi/maturin handler can rewrite it via `write-version` before
5
5
  # `maturin build`. `pyproject.toml` declares `dynamic = ["version"]`
6
6
  # and maturin reads this field. Mirrors `packages/rust/Cargo.toml`.
7
- version = "0.3.74"
7
+ version = "0.3.76"
8
8
  edition.workspace = true
9
9
  publish = false
10
10
  readme = "README.md"
@@ -5,8 +5,8 @@ The `dirsql` binary has three modes:
5
5
  | Invocation | Behavior |
6
6
  |---|---|
7
7
  | `dirsql` (no subcommand) | Start a long-lived HTTP server exposing a SQL view of a directory. See [HTTP API](./http-api.md). |
8
- | `dirsql query "<sql>"` | Build the index, run one query, print the rows as JSON, exit. No server, no watch. |
9
- | `dirsql init` | Generate a starter `.dirsql.toml` by running `claude` over a directory. |
8
+ | `dirsql query "<sql>"` | Query the file system as JSON |
9
+ | `dirsql init` | Generate a `.dirsql.toml` |
10
10
 
11
11
  ## Installation
12
12
 
@@ -100,8 +100,7 @@ with a JSON body describing the failure:
100
100
 
101
101
  ## `dirsql query`
102
102
 
103
- Run one SQL query from the shell — for ad-hoc inspection, scripting, and
104
- docs verification snippets — without booting the server and `curl`ing it:
103
+ Run a SQL query from the shell:
105
104
 
106
105
  ```bash
107
106
  dirsql query "SELECT basename, size FROM files ORDER BY size DESC LIMIT 5"
@@ -140,34 +139,29 @@ stderr, with exit code `1`.
140
139
 
141
140
  ## `dirsql init`
142
141
 
143
- Generates a `.dirsql.toml` by running the `claude` CLI over the target
144
- directory. The generated config contains only filesystem-fact tables
145
- (`[[table]]` entries whose columns come from [glob captures and virtual
146
- columns](./columns.md)) — never content-derived columns.
142
+ Writes a starter `.dirsql.toml`:
147
143
 
148
144
  ```bash
149
145
  dirsql init
150
146
  ```
151
147
 
148
+ You can further tweak this config as needed.
149
+
152
150
  ### Flags
153
151
 
154
152
  | Flag | Default | Description |
155
153
  |---|---|---|
156
- | `--root <path>` | current directory | Directory to scan. |
157
- | `--output <path>` | `<root>/.dirsql.toml` | Where to write the generated config. |
154
+ | `--root <path>` | current directory | Directory the default `--output` path is resolved against. |
155
+ | `--output <path>` | `<root>/.dirsql.toml` | Where to write the config. |
158
156
  | `--force` | off | Overwrite the output file if it already exists. |
159
157
 
160
158
  ### Requirements and failure modes
161
159
 
162
- `init` requires `claude` on `PATH`, signed in; there is no separate API key.
163
160
  All failures exit `1` with a message on stderr:
164
161
 
165
162
  | Condition | Behavior |
166
163
  |---|---|
167
- | Output file exists and `--force` not passed | Fails before invoking `claude` (no LLM call is made). |
168
- | `claude` not found on `PATH` | Fails with a pointer to the Claude Code install docs. |
169
- | `claude` exits non-zero | Fails with `claude`'s stderr; no partial config is written. |
170
- | `claude` produces non-UTF-8 output | Fails; nothing is written. |
164
+ | Output file exists and `--force` not passed | Fails; nothing is written. |
165
+ | Output path unwritable (e.g. missing parent directory) | Fails with the underlying I/O error. |
171
166
 
172
- On success, `claude`'s stdout is written verbatim to the output path and
173
- `init` exits `0`.
167
+ On success, `init` exits `0`.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "command": "uv run python -m pytest tests/e2e/ -x -q",
3
- "ran_at": 1783471100,
3
+ "ran_at": 1783522894,
4
4
  "exit_code": 0,
5
- "commit": "4722385a3a76526258c52a9ae1759562feba3f7a"
5
+ "commit": "aff7a746e4dfcd46d9f52f11f5cd7f680f5464ae"
6
6
  }
@@ -5,8 +5,8 @@ The `dirsql` binary has three modes:
5
5
  | Invocation | Behavior |
6
6
  |---|---|
7
7
  | `dirsql` (no subcommand) | Start a long-lived HTTP server exposing a SQL view of a directory. See [HTTP API](./http-api.md). |
8
- | `dirsql query "<sql>"` | Build the index, run one query, print the rows as JSON, exit. No server, no watch. |
9
- | `dirsql init` | Generate a starter `.dirsql.toml` by running `claude` over a directory. |
8
+ | `dirsql query "<sql>"` | Query the file system as JSON |
9
+ | `dirsql init` | Generate a `.dirsql.toml` |
10
10
 
11
11
  ## Installation
12
12
 
@@ -100,8 +100,7 @@ with a JSON body describing the failure:
100
100
 
101
101
  ## `dirsql query`
102
102
 
103
- Run one SQL query from the shell — for ad-hoc inspection, scripting, and
104
- docs verification snippets — without booting the server and `curl`ing it:
103
+ Run a SQL query from the shell:
105
104
 
106
105
  ```bash
107
106
  dirsql query "SELECT basename, size FROM files ORDER BY size DESC LIMIT 5"
@@ -140,34 +139,29 @@ stderr, with exit code `1`.
140
139
 
141
140
  ## `dirsql init`
142
141
 
143
- Generates a `.dirsql.toml` by running the `claude` CLI over the target
144
- directory. The generated config contains only filesystem-fact tables
145
- (`[[table]]` entries whose columns come from [glob captures and virtual
146
- columns](./columns.md)) — never content-derived columns.
142
+ Writes a starter `.dirsql.toml`:
147
143
 
148
144
  ```bash
149
145
  dirsql init
150
146
  ```
151
147
 
148
+ You can further tweak this config as needed.
149
+
152
150
  ### Flags
153
151
 
154
152
  | Flag | Default | Description |
155
153
  |---|---|---|
156
- | `--root <path>` | current directory | Directory to scan. |
157
- | `--output <path>` | `<root>/.dirsql.toml` | Where to write the generated config. |
154
+ | `--root <path>` | current directory | Directory the default `--output` path is resolved against. |
155
+ | `--output <path>` | `<root>/.dirsql.toml` | Where to write the config. |
158
156
  | `--force` | off | Overwrite the output file if it already exists. |
159
157
 
160
158
  ### Requirements and failure modes
161
159
 
162
- `init` requires `claude` on `PATH`, signed in; there is no separate API key.
163
160
  All failures exit `1` with a message on stderr:
164
161
 
165
162
  | Condition | Behavior |
166
163
  |---|---|
167
- | Output file exists and `--force` not passed | Fails before invoking `claude` (no LLM call is made). |
168
- | `claude` not found on `PATH` | Fails with a pointer to the Claude Code install docs. |
169
- | `claude` exits non-zero | Fails with `claude`'s stderr; no partial config is written. |
170
- | `claude` produces non-UTF-8 output | Fails; nothing is written. |
164
+ | Output file exists and `--force` not passed | Fails; nothing is written. |
165
+ | Output path unwritable (e.g. missing parent directory) | Fails with the underlying I/O error. |
171
166
 
172
- On success, `claude`'s stdout is written verbatim to the output path and
173
- `init` exits `0`.
167
+ On success, `init` exits `0`.
@@ -2,7 +2,7 @@
2
2
  //! - No subcommand: HTTP server documented in `docs/reference/cli.md`.
3
3
  //! - `query`: one-shot query over the same pipeline the server uses; see
4
4
  //! `docs/reference/cli.md`.
5
- //! - `init`: starter `.dirsql.toml` generation; see `docs/reference/cli.md`.
5
+ //! - `init`: writes a fixed starter `.dirsql.toml`; see `docs/reference/cli.md`.
6
6
  //!
7
7
  //! Only compiled with `--features cli`.
8
8
 
@@ -25,8 +25,9 @@ use dirsql::{DirSQL, Extension, Row, Table};
25
25
  directory. Tables are defined by a `.dirsql.toml` config \
26
26
  file; with no config, a default `files` table over every \
27
27
  file in the directory is served. With the `init` \
28
- subcommand, generates a starter `.dirsql.toml` by running \
29
- `claude` over the target directory."
28
+ subcommand, writes that same default `files` table as a \
29
+ starter `.dirsql.toml` no target-directory inspection, \
30
+ no network, deterministic."
30
31
  )]
31
32
  struct Cli {
32
33
  #[command(subcommand)]
@@ -64,8 +65,8 @@ struct Cli {
64
65
 
65
66
  #[derive(Debug, Subcommand)]
66
67
  enum Command {
67
- /// Generate a starter `.dirsql.toml` by running `claude` over the
68
- /// target directory.
68
+ /// Write the fixed starter `.dirsql.toml` the same default `files`
69
+ /// table zero-config mode serves. No target-directory inspection.
69
70
  Init(InitArgs),
70
71
 
71
72
  /// Run one SQL query against the indexed directory, print the result
@@ -84,7 +85,9 @@ struct QueryArgs {
84
85
 
85
86
  #[derive(Debug, Args)]
86
87
  struct InitArgs {
87
- /// Directory to scan (default: current directory).
88
+ /// Directory the default `--output` path is resolved against (default:
89
+ /// current directory). The written config's content does not depend on
90
+ /// this directory's contents.
88
91
  #[arg(long)]
89
92
  root: Option<PathBuf>,
90
93
 
@@ -162,7 +165,6 @@ fn run_init(args: InitArgs) -> ExitCode {
162
165
  let output = args.output.unwrap_or_else(|| root.join(".dirsql.toml"));
163
166
 
164
167
  let opts = InitOptions {
165
- root,
166
168
  output,
167
169
  force: args.force,
168
170
  };
@@ -344,15 +346,16 @@ fn load_default_state(config_path: &Path) -> AppState {
344
346
  }
345
347
  }
346
348
 
347
- /// The default `files` table used in zero-config mode: glob `**/*` matches
348
- /// every file under the root at any depth (no ignores), and each row is built
349
- /// purely from the auto-injected filesystem-fact columns.
349
+ /// The default `files` table used in zero-config mode, parsed from the same
350
+ /// [`dirsql::cli::DEFAULT_CONFIG_TOML`] asset `dirsql init` writes verbatim,
351
+ /// so the two can never drift apart.
350
352
  fn default_files_table() -> Table {
353
+ let config = dirsql::config::load_config_str(dirsql::cli::DEFAULT_CONFIG_TOML)
354
+ .expect("DEFAULT_CONFIG_TOML must be valid dirsql config TOML");
355
+ let table_config = &config.tables[0];
351
356
  Table::new(
352
- "CREATE TABLE files (\
353
- path TEXT, basename TEXT, dir TEXT, ext TEXT, \
354
- size INTEGER, mtime INTEGER, ctime INTEGER)",
355
- "**/*",
357
+ table_config.ddl.clone(),
358
+ table_config.glob.clone(),
356
359
  |_path| vec![Row::new()],
357
360
  )
358
361
  }
@@ -217,15 +217,10 @@ async fn run_post_query(pq: &PostQuery, rows: Vec<Value>) -> Result<Value, Query
217
217
  mod tests {
218
218
  use super::*;
219
219
 
220
- // The `DirSqlError::Core => BadRequest` arm of `classify_query_error` is
221
- // exercised end-to-end at the integration tier by
220
+ // The `Core => BadRequest` arm is covered end-to-end by
222
221
  // `post_query_malformed_sql_returns_400_not_500` in
223
- // `tests/cli_integration.rs`, which posts malformed SQL to `/query` and
224
- // asserts the 400. Constructing a `Core` value inline would require
225
- // importing the first-party `crate::db::DbError`, which the
226
- // `testing-conventions` `unit lint` isolation rule forbids (a unit test
227
- // may reach only `super::` and pure `std`). The non-Core arm below is
228
- // pure -- it builds a `super::DirSqlError::Lock` -- so it stays inline.
222
+ // `tests/cli_integration.rs` (unit-lint isolation bars constructing a
223
+ // `Core` value inline). The pure non-Core arm below stays here.
229
224
 
230
225
  #[test]
231
226
  fn classify_non_core_error_is_internal() {
@@ -0,0 +1,93 @@
1
+ //! `dirsql init` — write a fixed starter `.dirsql.toml`. See
2
+ //! `docs/reference/cli.md` for the user-facing contract.
3
+ //!
4
+ //! `init` does not inspect the target directory at all: it writes
5
+ //! [`super::DEFAULT_CONFIG_TOML`] verbatim -- the same single `files` table
6
+ //! [zero-config mode](../bin/dirsql.rs) parses that same asset to build --
7
+ //! so a user always has something loadable to hand-edit, and the two
8
+ //! surfaces can never drift apart. No LLM, no network, no filesystem walk.
9
+
10
+ use std::path::PathBuf;
11
+
12
+ use super::DEFAULT_CONFIG_TOML;
13
+
14
+ #[derive(Debug, thiserror::Error)]
15
+ pub enum InitError {
16
+ #[error("{}: already exists; pass --force to overwrite", path.display())]
17
+ AlreadyExists { path: PathBuf },
18
+
19
+ #[error("failed to write {}: {source}", path.display())]
20
+ Write {
21
+ path: PathBuf,
22
+ source: std::io::Error,
23
+ },
24
+ }
25
+
26
+ #[derive(Debug, Clone)]
27
+ pub struct InitOptions {
28
+ /// Path the generated config is written to. Resolved by the caller
29
+ /// (default: `<root>/.dirsql.toml`).
30
+ pub output: PathBuf,
31
+ /// Overwrite `output` if it already exists.
32
+ pub force: bool,
33
+ }
34
+
35
+ pub fn run(opts: InitOptions) -> Result<(), InitError> {
36
+ if !opts.force && opts.output.exists() {
37
+ return Err(InitError::AlreadyExists { path: opts.output });
38
+ }
39
+
40
+ std::fs::write(&opts.output, DEFAULT_CONFIG_TOML.as_bytes()).map_err(|source| {
41
+ InitError::Write {
42
+ path: opts.output.clone(),
43
+ source,
44
+ }
45
+ })?;
46
+
47
+ Ok(())
48
+ }
49
+
50
+ #[cfg(test)]
51
+ mod tests {
52
+ use super::*;
53
+
54
+ #[test]
55
+ fn already_exists_error_mentions_force() {
56
+ let err = InitError::AlreadyExists {
57
+ path: PathBuf::from("/tmp/foo.toml"),
58
+ };
59
+ let msg = format!("{err}");
60
+ assert!(msg.contains("already exists"));
61
+ assert!(msg.contains("--force"));
62
+ }
63
+
64
+ #[test]
65
+ fn write_error_display_names_the_path_and_source() {
66
+ let err = InitError::Write {
67
+ path: PathBuf::from("/tmp/out.toml"),
68
+ source: std::io::Error::other("disk full"),
69
+ };
70
+ let msg = format!("{err}");
71
+ assert!(msg.contains("/tmp/out.toml"), "got: {msg}");
72
+ assert!(msg.contains("disk full"), "got: {msg}");
73
+ }
74
+
75
+ /// The temp dir itself serves as the already-existing output path.
76
+ #[test]
77
+ fn run_bails_when_output_exists_without_force() {
78
+ let dir = tempfile::tempdir().unwrap();
79
+ let opts = InitOptions {
80
+ output: dir.path().to_path_buf(),
81
+ force: false,
82
+ };
83
+ let err = run(opts).unwrap_err();
84
+ assert!(
85
+ matches!(err, InitError::AlreadyExists { .. }),
86
+ "got: {err:?}"
87
+ );
88
+ }
89
+
90
+ // The write success + `--force` paths are covered black-box in
91
+ // `tests/init_integration.rs` (unit-lint isolation keeps effectful
92
+ // `std::fs` out of here).
93
+ }
@@ -39,6 +39,12 @@ pub mod server;
39
39
 
40
40
  pub use server::{serve, serve_with_state};
41
41
 
42
+ /// The one starter `.dirsql.toml` -- a single `files` table over every file
43
+ /// under the root, built from the seven stat columns. This is both what
44
+ /// `dirsql init` writes verbatim ([`init::run`]) and what zero-config mode
45
+ /// parses to build its default table, so the two can never drift apart.
46
+ pub const DEFAULT_CONFIG_TOML: &str = include_str!("../default_config.toml");
47
+
42
48
  /// A server-wide `pre-query` command hook, carrying the command template plus
43
49
  /// the directory it runs in (the config file's parent). When set on a
44
50
  /// [`ServerConfig`], the server passes each `POST /query` request body to the
@@ -336,4 +342,7 @@ mod tests {
336
342
  AppState::Ready(_) => panic!("String must map to the Unavailable arm"),
337
343
  }
338
344
  }
345
+
346
+ // `DEFAULT_CONFIG_TOML` parsing is covered in `tests/config.rs`
347
+ // (unit-lint isolation bars calling `config::load_config_str` here).
339
348
  }
@@ -0,0 +1,3 @@
1
+ [[table]]
2
+ ddl = "CREATE TABLE files (path TEXT, basename TEXT, dir TEXT, ext TEXT, size INTEGER, mtime INTEGER, ctime INTEGER)"
3
+ glob = "**/*"
@@ -1,207 +0,0 @@
1
- //! `dirsql init` — generate a starter `.dirsql.toml` by shelling out to
2
- //! the `claude` CLI. See `docs/reference/cli.md` for the user-facing
3
- //! contract.
4
- //!
5
- //! The agent's only required responsibility is to print a valid
6
- //! filesystem-fact-only `.dirsql.toml` to stdout. We invoke `claude -p`
7
- //! with the working directory set to the target root, capture stdout,
8
- //! and write it verbatim to the configured output path.
9
- //!
10
- //! Failure modes:
11
- //! - Output already exists and `--force` was not passed: bail before
12
- //! spawning the agent (never burn a paid LLM call first).
13
- //! - `claude` is not on PATH: surface a descriptive error pointing at
14
- //! the install docs.
15
- //! - `claude` exits non-zero: surface its stderr; do not write any
16
- //! partial config.
17
-
18
- use std::path::PathBuf;
19
- use std::process::Command;
20
-
21
- #[derive(Debug, thiserror::Error)]
22
- pub enum InitError {
23
- #[error("{}: already exists; pass --force to overwrite", path.display())]
24
- AlreadyExists { path: PathBuf },
25
-
26
- #[error(
27
- "`claude` not found on PATH; install Claude Code (https://docs.claude.com/en/docs/claude-code/quickstart)"
28
- )]
29
- ClaudeNotFound,
30
-
31
- #[error("failed to spawn `claude`: {0}")]
32
- Spawn(std::io::Error),
33
-
34
- #[error("`claude` exited with {status}\n{stderr}")]
35
- ClaudeFailed {
36
- status: std::process::ExitStatus,
37
- stderr: String,
38
- },
39
-
40
- #[error("`claude` produced non-UTF8 output")]
41
- InvalidUtf8(#[from] std::string::FromUtf8Error),
42
-
43
- #[error("failed to write {}: {source}", path.display())]
44
- Write {
45
- path: PathBuf,
46
- source: std::io::Error,
47
- },
48
- }
49
-
50
- #[derive(Debug, Clone)]
51
- pub struct InitOptions {
52
- /// Directory to scan. Resolved by the caller (default: cwd).
53
- pub root: PathBuf,
54
- /// Path the generated config is written to. Resolved by the caller
55
- /// (default: `<root>/.dirsql.toml`).
56
- pub output: PathBuf,
57
- /// Overwrite `output` if it already exists.
58
- pub force: bool,
59
- }
60
-
61
- pub fn run(opts: InitOptions) -> Result<(), InitError> {
62
- if !opts.force && opts.output.exists() {
63
- return Err(InitError::AlreadyExists { path: opts.output });
64
- }
65
-
66
- let prompt = build_prompt();
67
- let output = Command::new("claude")
68
- .arg("-p")
69
- .arg(&prompt)
70
- .current_dir(&opts.root)
71
- .output()
72
- .map_err(|err| match err.kind() {
73
- std::io::ErrorKind::NotFound => InitError::ClaudeNotFound,
74
- _ => InitError::Spawn(err),
75
- })?;
76
-
77
- if !output.status.success() {
78
- return Err(InitError::ClaudeFailed {
79
- status: output.status,
80
- stderr: String::from_utf8_lossy(&output.stderr).to_string(),
81
- });
82
- }
83
-
84
- let toml = String::from_utf8(output.stdout)?;
85
- std::fs::write(&opts.output, toml.as_bytes()).map_err(|source| InitError::Write {
86
- path: opts.output.clone(),
87
- source,
88
- })?;
89
-
90
- Ok(())
91
- }
92
-
93
- fn build_prompt() -> String {
94
- PROMPT.to_string()
95
- }
96
-
97
- const PROMPT: &str = r#"You are running inside a directory. Your job is to produce a `.dirsql.toml` config file that defines SQL tables over that directory's filesystem.
98
-
99
- Inspect the directory structure (files and subdirectories). Then produce a `.dirsql.toml` with one or more `[[table]]` blocks.
100
-
101
- Each `[[table]]` block has:
102
- - `ddl`: a SQLite CREATE TABLE statement.
103
- - `glob`: a glob pattern matching files relative to the directory root.
104
-
105
- Each row is one matched file. Columns come from these sources ONLY:
106
- - Glob path captures: `{name}` segments in the glob become columns named `name`.
107
- - Stat virtuals (reserved column names): `path`, `basename`, `dir`, `ext`, `size`, `mtime`, `ctime`.
108
-
109
- Do NOT include columns sourced from file content (JSON keys, CSV headers, frontmatter, etc.). Content parsing is not configured in `.dirsql.toml`.
110
-
111
- Output ONLY the TOML, with no surrounding prose, no markdown fences, no explanation.
112
-
113
- Example for a flat directory of mixed files:
114
- [[table]]
115
- ddl = "CREATE TABLE files (path TEXT, ext TEXT, size INTEGER)"
116
- glob = "*"
117
-
118
- Example with path captures:
119
- [[table]]
120
- ddl = "CREATE TABLE photos (month TEXT, basename TEXT, mtime INTEGER)"
121
- glob = "{month}/*.jpg"
122
- "#;
123
-
124
- #[cfg(test)]
125
- mod tests {
126
- use super::*;
127
-
128
- #[test]
129
- fn prompt_mentions_filesystem_fact_constraints() {
130
- let p = build_prompt();
131
- assert!(p.contains("[[table]]"));
132
- assert!(p.contains("path"));
133
- assert!(p.contains("Output ONLY the TOML"));
134
- }
135
-
136
- #[test]
137
- fn already_exists_error_mentions_force() {
138
- let err = InitError::AlreadyExists {
139
- path: PathBuf::from("/tmp/foo.toml"),
140
- };
141
- let msg = format!("{err}");
142
- assert!(msg.contains("already exists"));
143
- assert!(msg.contains("--force"));
144
- }
145
-
146
- #[test]
147
- fn claude_not_found_error_mentions_claude() {
148
- let err = InitError::ClaudeNotFound;
149
- let msg = format!("{err}").to_lowercase();
150
- assert!(msg.contains("claude"));
151
- }
152
-
153
- /// The temp dir itself serves as the already-existing output path.
154
- #[test]
155
- fn run_bails_when_output_exists_without_force() {
156
- let dir = tempfile::tempdir().unwrap();
157
- let opts = InitOptions {
158
- root: dir.path().to_path_buf(),
159
- output: dir.path().to_path_buf(),
160
- force: false,
161
- };
162
- let err = run(opts).unwrap_err();
163
- assert!(
164
- matches!(err, InitError::AlreadyExists { .. }),
165
- "got: {err:?}"
166
- );
167
- }
168
-
169
- // The spawn / non-zero-exit / write failure arms of `run` need a real
170
- // `claude` and filesystem, so they live at the e2e tier; only their pure
171
- // `Display` renderings are covered here.
172
-
173
- #[test]
174
- fn spawn_error_display_mentions_spawn() {
175
- let err = InitError::Spawn(std::io::Error::new(
176
- std::io::ErrorKind::PermissionDenied,
177
- "denied",
178
- ));
179
- let msg = format!("{err}");
180
- assert!(msg.contains("failed to spawn"), "got: {msg}");
181
- assert!(msg.contains("denied"), "got: {msg}");
182
- }
183
-
184
- #[test]
185
- fn write_error_display_names_the_path_and_source() {
186
- let err = InitError::Write {
187
- path: PathBuf::from("/tmp/out.toml"),
188
- source: std::io::Error::other("disk full"),
189
- };
190
- let msg = format!("{err}");
191
- assert!(msg.contains("/tmp/out.toml"), "got: {msg}");
192
- assert!(msg.contains("disk full"), "got: {msg}");
193
- }
194
-
195
- #[test]
196
- fn invalid_utf8_error_display_mentions_utf8() {
197
- let utf8_err = String::from_utf8(vec![0xff, 0xfe]).unwrap_err();
198
- let err: InitError = utf8_err.into();
199
- let msg = format!("{err}");
200
- assert!(msg.contains("non-UTF8"), "got: {msg}");
201
- }
202
-
203
- // NOTE: `InitError::ClaudeFailed`'s `Display` is not unit-tested -- building
204
- // it needs an `ExitStatus`, and `std::process::ExitStatus::from_raw` is
205
- // effectful std the unit-isolation lint (`no-out-of-module-call`) forbids.
206
- // Its rendering is covered end-to-end at the e2e tier instead.
207
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes