datamasque-cli 1.0.0__tar.gz → 1.2.0__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 (81) hide show
  1. datamasque_cli-1.2.0/.claude-plugin/marketplace.json +21 -0
  2. datamasque_cli-1.2.0/CHANGELOG.md +69 -0
  3. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/CONTRIBUTING.md +3 -6
  4. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/Makefile +4 -7
  5. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/PKG-INFO +92 -1
  6. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/README.md +91 -0
  7. datamasque_cli-1.2.0/claude-skills/README.md +60 -0
  8. datamasque_cli-1.2.0/claude-skills/datamasque-cli/.claude-plugin/plugin.json +9 -0
  9. datamasque_cli-1.2.0/claude-skills/datamasque-cli/skills/datamasque-cli/SKILL.md +105 -0
  10. datamasque_cli-1.2.0/claude-skills/ruleset-builder/.claude-plugin/plugin.json +9 -0
  11. datamasque_cli-1.2.0/claude-skills/ruleset-splitter/.claude-plugin/plugin.json +9 -0
  12. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/pyproject.toml +3 -1
  13. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/scripts/active_profile_env.py +2 -1
  14. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/src/datamasque_cli/client.py +66 -25
  15. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/src/datamasque_cli/commands/auth.py +14 -6
  16. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/src/datamasque_cli/commands/connections.py +61 -30
  17. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/src/datamasque_cli/commands/discovery.py +4 -4
  18. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/src/datamasque_cli/commands/files.py +3 -3
  19. datamasque_cli-1.2.0/src/datamasque_cli/commands/ifm.py +354 -0
  20. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/src/datamasque_cli/commands/ruleset_libraries.py +6 -6
  21. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/src/datamasque_cli/commands/rulesets.py +18 -6
  22. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/src/datamasque_cli/commands/runs.py +40 -21
  23. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/src/datamasque_cli/commands/seeds.py +3 -3
  24. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/src/datamasque_cli/commands/system.py +3 -3
  25. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/src/datamasque_cli/commands/users.py +4 -4
  26. datamasque_cli-1.2.0/src/datamasque_cli/main.py +134 -0
  27. datamasque_cli-1.2.0/src/datamasque_cli/output.py +250 -0
  28. datamasque_cli-1.2.0/tests/commands/test_catalog.py +44 -0
  29. datamasque_cli-1.2.0/tests/commands/test_ifm.py +577 -0
  30. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/conftest.py +13 -0
  31. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/test_client_auth.py +1 -1
  32. datamasque_cli-1.2.0/tests/test_client_ifm.py +65 -0
  33. datamasque_cli-1.2.0/tests/test_output.py +200 -0
  34. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/uv.lock +4 -4
  35. datamasque_cli-1.0.0/CHANGELOG.md +0 -5
  36. datamasque_cli-1.0.0/claude-skills/README.md +0 -46
  37. datamasque_cli-1.0.0/claude-skills/datamasque-cli/SKILL.md +0 -187
  38. datamasque_cli-1.0.0/src/datamasque_cli/main.py +0 -58
  39. datamasque_cli-1.0.0/src/datamasque_cli/output.py +0 -133
  40. datamasque_cli-1.0.0/tests/test_output.py +0 -74
  41. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/.github/workflows/ci.yml +0 -0
  42. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/.github/workflows/release-testpypi.yml +0 -0
  43. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/.github/workflows/release.yml +0 -0
  44. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/.gitignore +0 -0
  45. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/LICENSE +0 -0
  46. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/NOTICE +0 -0
  47. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/assets/demo.gif +0 -0
  48. {datamasque_cli-1.0.0/claude-skills → datamasque_cli-1.2.0/claude-skills/ruleset-builder/skills}/ruleset-builder/SKILL.md +0 -0
  49. {datamasque_cli-1.0.0/claude-skills → datamasque_cli-1.2.0/claude-skills/ruleset-builder/skills}/ruleset-builder/references/hash-columns-guide.md +0 -0
  50. {datamasque_cli-1.0.0/claude-skills → datamasque_cli-1.2.0/claude-skills/ruleset-builder/skills}/ruleset-builder/references/mask-definitions-guide.md +0 -0
  51. {datamasque_cli-1.0.0/claude-skills → datamasque_cli-1.2.0/claude-skills/ruleset-builder/skills}/ruleset-builder/references/ruleset-libraries-guide.md +0 -0
  52. {datamasque_cli-1.0.0/claude-skills → datamasque_cli-1.2.0/claude-skills/ruleset-builder/skills}/ruleset-builder/references/ruleset-yaml-reference.md +0 -0
  53. {datamasque_cli-1.0.0/claude-skills → datamasque_cli-1.2.0/claude-skills/ruleset-splitter/skills}/ruleset-splitter/SKILL.md +0 -0
  54. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/scripts/bump_version.py +0 -0
  55. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/src/datamasque_cli/__init__.py +0 -0
  56. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/src/datamasque_cli/commands/__init__.py +0 -0
  57. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/src/datamasque_cli/config.py +0 -0
  58. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/src/datamasque_cli/py.typed +0 -0
  59. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/__init__.py +0 -0
  60. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/commands/__init__.py +0 -0
  61. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/commands/test_auth.py +0 -0
  62. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/commands/test_connections.py +0 -0
  63. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/commands/test_discovery.py +0 -0
  64. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/commands/test_files.py +0 -0
  65. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/commands/test_ruleset_libraries.py +0 -0
  66. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/commands/test_rulesets.py +0 -0
  67. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/commands/test_runs.py +0 -0
  68. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/commands/test_seeds.py +0 -0
  69. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/commands/test_system.py +0 -0
  70. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/commands/test_users.py +0 -0
  71. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/integration/README.md +0 -0
  72. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/integration/__init__.py +0 -0
  73. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/integration/conftest.py +0 -0
  74. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/integration/test_connections.py +0 -0
  75. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/integration/test_delete_safety.py +0 -0
  76. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/integration/test_rulesets.py +0 -0
  77. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/integration/test_runs.py +0 -0
  78. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/test_client_env.py +0 -0
  79. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/test_client_profile.py +0 -0
  80. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/test_config.py +0 -0
  81. {datamasque_cli-1.0.0 → datamasque_cli-1.2.0}/tests/test_version.py +0 -0
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "datamasque-tools",
3
+ "owner": { "name": "DataMasque" },
4
+ "plugins": [
5
+ {
6
+ "name": "datamasque-cli",
7
+ "source": "./claude-skills/datamasque-cli",
8
+ "description": "DataMasque command-line interface integration. Drive DataMasque from Claude Code: list connections, kick off runs, stream logs, download reports."
9
+ },
10
+ {
11
+ "name": "ruleset-builder",
12
+ "source": "./claude-skills/ruleset-builder",
13
+ "description": "Convert auto-generated DataMasque rulesets into production-ready form. Validate and iterate."
14
+ },
15
+ {
16
+ "name": "ruleset-splitter",
17
+ "source": "./claude-skills/ruleset-splitter",
18
+ "description": "Consolidate multi-file DataMasque rulesets for editing, then re-split them back out."
19
+ }
20
+ ]
21
+ }
@@ -0,0 +1,69 @@
1
+ # Changelog
2
+
3
+ ## v1.2.0
4
+
5
+ ### Added
6
+ - `dm ifm` command group
7
+ for managing in-flight masking ruleset plans
8
+ and running mask operations against the IFM service:
9
+ - `dm ifm list` —
10
+ list all IFM ruleset plans.
11
+ - `dm ifm get <name>` —
12
+ show plan metadata,
13
+ or the ruleset YAML with `--yaml`.
14
+ - `dm ifm create --name <name> --file <yaml>` —
15
+ create a plan from a YAML ruleset,
16
+ with optional `--enabled/--disabled` and `--log-level`.
17
+ - `dm ifm update <name>` —
18
+ update a plan;
19
+ pass any of `--file`, `--enabled/--disabled`, `--log-level`
20
+ and only those fields are sent.
21
+ - `dm ifm delete <name>` —
22
+ delete a plan
23
+ (interactive confirm,
24
+ or `--yes` to skip).
25
+ - `dm ifm mask <name> --data <file|->` —
26
+ mask a JSON list of records against a plan,
27
+ with `--disable-instance-secret`,
28
+ `--run-secret`,
29
+ `--log-level`,
30
+ `--request-id`,
31
+ and `--json/--no-json` (NDJSON) output.
32
+ - `dm ifm verify-token` —
33
+ verify the current IFM token and list its scopes.
34
+
35
+ Authentication reuses your existing `dm` profile credentials
36
+ via the SDK's `DataMasqueIfmClient`,
37
+ which transparently exchanges admin-server credentials for an IFM JWT.
38
+
39
+ ## v1.1.0
40
+
41
+ ### Added
42
+ - `dm catalog` command — emits the full subcommand tree as JSON for agent
43
+ introspection. `--compact` for `{path, help}` only (~1.4kB), default for
44
+ full options/arguments.
45
+ - Auto-detection of agent context: output flips to JSON automatically when
46
+ stdout is not a TTY, when `DM_OUTPUT=json` is set, or when the
47
+ vendor-neutral `AI_AGENT` env var is present. `DM_OUTPUT=table` forces
48
+ human output.
49
+ - Structured error envelope on stderr in agent mode:
50
+ `{"error": {"code": "...", "message": "...", "hint": "..."}}` — stdout
51
+ stays empty on failure so downstream pipes don't trip.
52
+
53
+ ### Changed
54
+ - Exit codes are now differentiated by error category. Previously every
55
+ error returned 1; now: `not_found`=3, `invalid_input`=4, `ambiguous`=5,
56
+ `auth_required`=6, `auth_failed`=7, `conflict`=8, `transport_error`=9.
57
+ `error` (unclassified) remains 1; 2 is reserved for typer/click usage
58
+ errors. Stable across minor versions.
59
+ - Long values (UUIDs especially) now fold across lines in table output
60
+ rather than being silently truncated with `…` in narrow terminals.
61
+
62
+ ### Internal
63
+ - `ErrorCode` and `ConnectionType` are now `StrEnum`s; the abort code arg
64
+ is type-checked at edit time and the connection-type "Valid: ..." hint
65
+ is generated from the enum.
66
+
67
+ ## v1.0.0
68
+
69
+ Initial release.
@@ -185,13 +185,10 @@ Each target runs `make check`,
185
185
  bumps the version in `pyproject.toml`,
186
186
  refreshes `uv.lock`,
187
187
  commits, tags, and pushes.
188
- CI handles the publish.
188
+ CI handles the publish to PyPI.
189
189
 
190
- To publish manually without bumping:
191
-
192
- ```console
193
- make publish
194
- ```
190
+ To smoke-test a release against TestPyPI without tagging,
191
+ trigger the `Release (TestPyPI)` workflow manually from the GitHub Actions tab.
195
192
 
196
193
  ## Toolchain
197
194
 
@@ -1,4 +1,4 @@
1
- .PHONY: install lint format mypy test test-integration test-integration-local check build publish release-patch release-minor release-major
1
+ .PHONY: install lint format mypy test test-integration test-integration-local check build release-patch release-minor release-major
2
2
 
3
3
  install:
4
4
  uv sync
@@ -33,9 +33,6 @@ format-check:
33
33
  build:
34
34
  uv build
35
35
 
36
- publish: check build
37
- uv publish --index datamasque-private-pypi -u "" -p "" dist/*
38
-
39
36
  # Bump version, commit, tag, push — CI publishes automatically.
40
37
  # Usage: make release-patch (0.1.0 → 0.1.1)
41
38
  # make release-minor (0.1.0 → 0.2.0)
@@ -47,7 +44,7 @@ release-patch: check
47
44
  git commit -m "Release v$(VERSION)"
48
45
  git tag "v$(VERSION)"
49
46
  git push && git push --tags
50
- @echo "Released v$(VERSION) — CI will publish to pypi.dtq.tools"
47
+ @echo "Released v$(VERSION) — CI will publish to PyPI (https://pypi.org/p/datamasque-cli)"
51
48
 
52
49
  release-minor: check
53
50
  $(eval VERSION := $(shell python3 scripts/bump_version.py minor))
@@ -56,7 +53,7 @@ release-minor: check
56
53
  git commit -m "Release v$(VERSION)"
57
54
  git tag "v$(VERSION)"
58
55
  git push && git push --tags
59
- @echo "Released v$(VERSION) — CI will publish to pypi.dtq.tools"
56
+ @echo "Released v$(VERSION) — CI will publish to PyPI (https://pypi.org/p/datamasque-cli)"
60
57
 
61
58
  release-major: check
62
59
  $(eval VERSION := $(shell python3 scripts/bump_version.py major))
@@ -65,4 +62,4 @@ release-major: check
65
62
  git commit -m "Release v$(VERSION)"
66
63
  git tag "v$(VERSION)"
67
64
  git push && git push --tags
68
- @echo "Released v$(VERSION) — CI will publish to pypi.dtq.tools"
65
+ @echo "Released v$(VERSION) — CI will publish to PyPI (https://pypi.org/p/datamasque-cli)"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datamasque-cli
3
- Version: 1.0.0
3
+ Version: 1.2.0
4
4
  Summary: Official command-line interface for the DataMasque data-masking platform.
5
5
  Project-URL: Homepage, https://datamasque.com/
6
6
  Project-URL: Repository, https://github.com/datamasque/datamasque-cli
@@ -39,6 +39,7 @@ so teams can use production-shaped data in non-production environments without e
39
39
  DataMasque CLI `dm` covers:
40
40
 
41
41
  - connections, rulesets, ruleset libraries, and masking runs
42
+ - in-flight masking (IFM) ruleset plans and on-demand mask requests
42
43
  - schema discovery and sensitive-data discovery
43
44
  - users, files, and DataMasque instance administration
44
45
 
@@ -89,6 +90,26 @@ dm run logs 42 --follow
89
90
  `dm run start` and `dm run wait` block until the run finishes.
90
91
  Pass `--background` to `start` to skip the wait.
91
92
 
93
+ ## Claude Code skills
94
+
95
+ The [`claude-skills/`](claude-skills/) directory ships three
96
+ [Claude Code](https://claude.com/claude-code) plugins that drive `dm` on your behalf:
97
+
98
+ - **`datamasque-cli`** — operate a DataMasque instance (start runs, list connections, fetch discovery reports).
99
+ - **`ruleset-builder`** — turn auto-generated rulesets into production-ready ones.
100
+ - **`ruleset-splitter`** — join many-file rulesets into one file for editing, then re-split.
101
+
102
+ Install via the Claude Code plugin marketplace:
103
+
104
+ ```
105
+ /plugin marketplace add datamasque/datamasque-cli
106
+ /plugin install datamasque-cli@datamasque-tools
107
+ /plugin install ruleset-builder@datamasque-tools
108
+ /plugin install ruleset-splitter@datamasque-tools
109
+ ```
110
+
111
+ See [`claude-skills/README.md`](claude-skills/README.md) for more.
112
+
92
113
  ## Shell completion
93
114
 
94
115
  `dm` provides built-in completion for bash, zsh, and fish:
@@ -176,6 +197,26 @@ dm libraries validate <name> # Re-validate against current
176
197
  dm libraries usage <name> # Show rulesets using it
177
198
  ```
178
199
 
200
+ ### In-flight masking
201
+
202
+ The IFM service runs alongside the admin server,
203
+ reached at `<DataMasque URL>/ifm` via the standard nginx topology.
204
+
205
+ ```console
206
+ dm ifm list # List ruleset plans
207
+ dm ifm get <name> # Show plan metadata
208
+ dm ifm get <name> --yaml # Print the ruleset YAML
209
+ dm ifm create --name myplan --file rules.yaml # Create (server suffixes a random string to the name)
210
+ dm ifm create --name myplan --file rules.yaml --disabled --log-level DEBUG
211
+ dm ifm update <name> --file rules.yaml # Replace the ruleset YAML
212
+ dm ifm update <name> --enabled # Toggle without re-sending the YAML
213
+ dm ifm update <name> --log-level INFO
214
+ dm ifm delete <name> --yes # Delete a plan
215
+ dm ifm mask <name> --data input.json # Mask a JSON list of records
216
+ dm ifm mask <name> --data - # Read records from stdin
217
+ dm ifm verify-token # Show scopes granted to the current IFM token
218
+ ```
219
+
179
220
  ### Masking runs
180
221
 
181
222
  ```console
@@ -249,6 +290,56 @@ STATUS=$(dm run status 42 --json | jq -r '.status')
249
290
  dm rulesets get myruleset --json | jq -r '.yaml' > ruleset.yaml
250
291
  ```
251
292
 
293
+ JSON is also emitted automatically when:
294
+
295
+ - `stdout` is not a TTY (piped or captured),
296
+ - `DM_OUTPUT=json` is set in the environment, or
297
+ - a vendor-neutral `AI_AGENT` env var is set (e.g. by Claude Code).
298
+
299
+ Set `DM_OUTPUT=table` to force human-readable output regardless of context.
300
+
301
+ ## Agent / scripting interface
302
+
303
+ For programmatic use (CI, AI coding agents, shell scripts), the CLI exposes
304
+ a discovery command and a stable error contract.
305
+
306
+ ### Command catalog
307
+
308
+ `dm catalog` dumps every visible subcommand as JSON so an agent can introspect
309
+ the surface without paging through `--help` screens:
310
+
311
+ ```console
312
+ dm catalog --compact # ~1.4kB — {path, help} per command
313
+ dm catalog # full — also includes options and arguments
314
+ ```
315
+
316
+ ### Structured errors
317
+
318
+ In agent mode, errors are emitted as a JSON envelope on stderr (stdout stays
319
+ empty on failure):
320
+
321
+ ```json
322
+ {"error": {"code": "not_found", "message": "Connection 'foo' not found.", "hint": "Run dm connections list."}}
323
+ ```
324
+
325
+ ### Exit codes
326
+
327
+ | Code | Meaning | When |
328
+ | ---: | ----------------- | ---------------------------------------------- |
329
+ | 0 | success | command completed |
330
+ | 1 | error | unclassified failure |
331
+ | 2 | usage error | unknown flag or missing argument (typer/click) |
332
+ | 3 | not_found | resource lookup failed |
333
+ | 4 | invalid_input | argument values rejected |
334
+ | 5 | ambiguous | name matched multiple resources |
335
+ | 6 | auth_required | no credentials configured |
336
+ | 7 | auth_failed | credentials rejected by server |
337
+ | 8 | conflict | operation rejected by server state |
338
+ | 9 | transport_error | network or TLS failure |
339
+
340
+ Exit codes are stable across minor versions. The `error.code` string in the
341
+ JSON envelope mirrors these names.
342
+
252
343
  ## Documentation
253
344
 
254
345
  Documentation for the DataMasque product,
@@ -9,6 +9,7 @@ so teams can use production-shaped data in non-production environments without e
9
9
  DataMasque CLI `dm` covers:
10
10
 
11
11
  - connections, rulesets, ruleset libraries, and masking runs
12
+ - in-flight masking (IFM) ruleset plans and on-demand mask requests
12
13
  - schema discovery and sensitive-data discovery
13
14
  - users, files, and DataMasque instance administration
14
15
 
@@ -59,6 +60,26 @@ dm run logs 42 --follow
59
60
  `dm run start` and `dm run wait` block until the run finishes.
60
61
  Pass `--background` to `start` to skip the wait.
61
62
 
63
+ ## Claude Code skills
64
+
65
+ The [`claude-skills/`](claude-skills/) directory ships three
66
+ [Claude Code](https://claude.com/claude-code) plugins that drive `dm` on your behalf:
67
+
68
+ - **`datamasque-cli`** — operate a DataMasque instance (start runs, list connections, fetch discovery reports).
69
+ - **`ruleset-builder`** — turn auto-generated rulesets into production-ready ones.
70
+ - **`ruleset-splitter`** — join many-file rulesets into one file for editing, then re-split.
71
+
72
+ Install via the Claude Code plugin marketplace:
73
+
74
+ ```
75
+ /plugin marketplace add datamasque/datamasque-cli
76
+ /plugin install datamasque-cli@datamasque-tools
77
+ /plugin install ruleset-builder@datamasque-tools
78
+ /plugin install ruleset-splitter@datamasque-tools
79
+ ```
80
+
81
+ See [`claude-skills/README.md`](claude-skills/README.md) for more.
82
+
62
83
  ## Shell completion
63
84
 
64
85
  `dm` provides built-in completion for bash, zsh, and fish:
@@ -146,6 +167,26 @@ dm libraries validate <name> # Re-validate against current
146
167
  dm libraries usage <name> # Show rulesets using it
147
168
  ```
148
169
 
170
+ ### In-flight masking
171
+
172
+ The IFM service runs alongside the admin server,
173
+ reached at `<DataMasque URL>/ifm` via the standard nginx topology.
174
+
175
+ ```console
176
+ dm ifm list # List ruleset plans
177
+ dm ifm get <name> # Show plan metadata
178
+ dm ifm get <name> --yaml # Print the ruleset YAML
179
+ dm ifm create --name myplan --file rules.yaml # Create (server suffixes a random string to the name)
180
+ dm ifm create --name myplan --file rules.yaml --disabled --log-level DEBUG
181
+ dm ifm update <name> --file rules.yaml # Replace the ruleset YAML
182
+ dm ifm update <name> --enabled # Toggle without re-sending the YAML
183
+ dm ifm update <name> --log-level INFO
184
+ dm ifm delete <name> --yes # Delete a plan
185
+ dm ifm mask <name> --data input.json # Mask a JSON list of records
186
+ dm ifm mask <name> --data - # Read records from stdin
187
+ dm ifm verify-token # Show scopes granted to the current IFM token
188
+ ```
189
+
149
190
  ### Masking runs
150
191
 
151
192
  ```console
@@ -219,6 +260,56 @@ STATUS=$(dm run status 42 --json | jq -r '.status')
219
260
  dm rulesets get myruleset --json | jq -r '.yaml' > ruleset.yaml
220
261
  ```
221
262
 
263
+ JSON is also emitted automatically when:
264
+
265
+ - `stdout` is not a TTY (piped or captured),
266
+ - `DM_OUTPUT=json` is set in the environment, or
267
+ - a vendor-neutral `AI_AGENT` env var is set (e.g. by Claude Code).
268
+
269
+ Set `DM_OUTPUT=table` to force human-readable output regardless of context.
270
+
271
+ ## Agent / scripting interface
272
+
273
+ For programmatic use (CI, AI coding agents, shell scripts), the CLI exposes
274
+ a discovery command and a stable error contract.
275
+
276
+ ### Command catalog
277
+
278
+ `dm catalog` dumps every visible subcommand as JSON so an agent can introspect
279
+ the surface without paging through `--help` screens:
280
+
281
+ ```console
282
+ dm catalog --compact # ~1.4kB — {path, help} per command
283
+ dm catalog # full — also includes options and arguments
284
+ ```
285
+
286
+ ### Structured errors
287
+
288
+ In agent mode, errors are emitted as a JSON envelope on stderr (stdout stays
289
+ empty on failure):
290
+
291
+ ```json
292
+ {"error": {"code": "not_found", "message": "Connection 'foo' not found.", "hint": "Run dm connections list."}}
293
+ ```
294
+
295
+ ### Exit codes
296
+
297
+ | Code | Meaning | When |
298
+ | ---: | ----------------- | ---------------------------------------------- |
299
+ | 0 | success | command completed |
300
+ | 1 | error | unclassified failure |
301
+ | 2 | usage error | unknown flag or missing argument (typer/click) |
302
+ | 3 | not_found | resource lookup failed |
303
+ | 4 | invalid_input | argument values rejected |
304
+ | 5 | ambiguous | name matched multiple resources |
305
+ | 6 | auth_required | no credentials configured |
306
+ | 7 | auth_failed | credentials rejected by server |
307
+ | 8 | conflict | operation rejected by server state |
308
+ | 9 | transport_error | network or TLS failure |
309
+
310
+ Exit codes are stable across minor versions. The `error.code` string in the
311
+ JSON envelope mirrors these names.
312
+
222
313
  ## Documentation
223
314
 
224
315
  Documentation for the DataMasque product,
@@ -0,0 +1,60 @@
1
+ # Claude Code skills
2
+
3
+ - **`datamasque-cli/`** —
4
+ teaches Claude how to operate a DataMasque instance with `dm`:
5
+ - authenticate,
6
+ - list connections,
7
+ - start runs,
8
+ - fetch discovery reports.
9
+ - **`ruleset-builder/`** —
10
+ turns auto-generated rulesets into production-ready ones:
11
+ - extracts a `ruleset_library`,
12
+ - adds `hash_columns`,
13
+ - applies `skip_defaults`,
14
+ - validates.
15
+ - **`ruleset-splitter/`** —
16
+ joins DataMasque's many-file generated rulesets into one file for editing, then re-splits back to the original filenames.
17
+
18
+ ## Install
19
+
20
+ In [Claude Code](https://claude.com/claude-code), add this repo as a plugin marketplace and install the plugins you want:
21
+
22
+ ```
23
+ /plugin marketplace add datamasque/datamasque-cli
24
+ /plugin install datamasque-cli@datamasque-tools
25
+ /plugin install ruleset-builder@datamasque-tools
26
+ /plugin install ruleset-splitter@datamasque-tools
27
+ ```
28
+
29
+ ## Uninstall
30
+
31
+ ```
32
+ /plugin uninstall datamasque-cli@datamasque-tools
33
+ /plugin uninstall ruleset-builder@datamasque-tools
34
+ /plugin uninstall ruleset-splitter@datamasque-tools
35
+ ```
36
+
37
+ ## Working on these skills locally
38
+
39
+ If you're editing these skills in-repo and want Claude Code to pick up changes without reinstalling, symlink each directory into `~/.claude/skills/`:
40
+
41
+ ```bash
42
+ ln -sfn ~/repos/datamasque-cli/claude-skills/datamasque-cli/skills/datamasque-cli \
43
+ ~/.claude/skills/datamasque-cli
44
+
45
+ ln -sfn ~/repos/datamasque-cli/claude-skills/ruleset-builder/skills/ruleset-builder \
46
+ ~/.claude/skills/ruleset-builder
47
+
48
+ ln -sfn ~/repos/datamasque-cli/claude-skills/ruleset-splitter/skills/ruleset-splitter \
49
+ ~/.claude/skills/ruleset-splitter
50
+ ```
51
+
52
+ Reload inside Claude Code (`/reload-plugins` or start a new session). Edits to any `SKILL.md` go live on the next reload — no reinstall.
53
+
54
+ Remove the symlinks when done:
55
+
56
+ ```bash
57
+ rm ~/.claude/skills/datamasque-cli
58
+ rm ~/.claude/skills/ruleset-builder
59
+ rm ~/.claude/skills/ruleset-splitter
60
+ ```
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "datamasque-cli",
3
+ "version": "1.1.0",
4
+ "description": "DataMasque command-line interface integration. Drive DataMasque from Claude Code: list connections, kick off runs, stream logs, download reports.",
5
+ "author": { "name": "DataMasque Ltd" },
6
+ "repository": "https://github.com/datamasque/datamasque-cli",
7
+ "license": "Apache-2.0",
8
+ "keywords": ["datamasque", "data-masking", "cli"]
9
+ }
@@ -0,0 +1,105 @@
1
+ ---
2
+ name: datamasque-cli
3
+ description: Use when the user wants to interact with a DataMasque instance — start masking runs, check run status, list connections or rulesets, manage seeds, manage ruleset libraries, check system health, or any task involving the DataMasque API. Triggers on "mask the data", "start a run", "check the run", "list connections", "list rulesets", "upload a seed", "check DataMasque health", "dm status", "ruleset library", or any request to operate DataMasque programmatically.
4
+ argument-hint: e.g. "start a run with docx_masking on var_input_docx"
5
+ user-invocable: true
6
+ ---
7
+
8
+ # DataMasque CLI
9
+
10
+ Operate a DataMasque instance via the `dm` command-line tool.
11
+
12
+ Run `dm catalog --compact` for a JSON list of every subcommand. The sections
13
+ below cover idioms the catalog can't show you.
14
+
15
+ ## Output and errors
16
+
17
+ In agent mode — auto-detected when stdout is not a TTY, `AI_AGENT` is set, or
18
+ `DM_OUTPUT=json` — output is JSON on stdout, errors are JSON on stderr:
19
+
20
+ ```json
21
+ {"error": {"code": "not_found", "message": "...", "hint": "..."}}
22
+ ```
23
+
24
+ `error.code` is the stable identifier; branch on it rather than the message.
25
+ The set is `not_found`, `invalid_input`, `ambiguous`, `auth_required`,
26
+ `auth_failed`, `conflict`, `transport_error`, `error`. Exit code is non-zero
27
+ on any error; exit 2 specifically means a CLI usage error (unknown flag,
28
+ missing argument) from typer.
29
+
30
+ `DM_OUTPUT=table` forces human-readable output.
31
+
32
+ ## Authentication
33
+
34
+ Set `DATAMASQUE_URL`, `DATAMASQUE_USERNAME`, `DATAMASQUE_PASSWORD` to auth
35
+ without saving anything (right choice for CI / one-offs). For interactive
36
+ use, `dm auth login --profile <name>` prompts and persists to
37
+ `~/.config/datamasque-cli/config.toml`. `--insecure` (on login) or
38
+ `DATAMASQUE_VERIFY_SSL=false` (per call) skip TLS verification.
39
+
40
+ ## Quick start: a masking run
41
+
42
+ ```bash
43
+ dm connections list # find a source
44
+ dm rulesets list # find a ruleset
45
+ dm run start -c <source> -r <ruleset> [-d <dest>] # blocks until done
46
+ ```
47
+
48
+ Add `--background` to return immediately with the run id, then poll with
49
+ `dm run wait <id>`, `dm run status <id>`, or `dm run logs <id> --follow`.
50
+ Pass repeated `--options key=value` for server-side knobs
51
+ (e.g. `--options batch_size=1000 --options dry_run=true`).
52
+
53
+ ## Idioms and gotchas
54
+
55
+ - **Ruleset namespaces.** `database` and `file` rulesets share a name
56
+ namespace, so `customers` can exist in both. `dm run start` reads the
57
+ source connection's type and picks the matching ruleset automatically.
58
+ For `get` / `create` / `delete`, pass `--type file|database` only when
59
+ two rows share the name and you need to disambiguate.
60
+
61
+ - **File masking needs a destination.** Database masking is in-place;
62
+ file masking writes through to a destination connection and fails
63
+ with `invalid_input` without `--destination`.
64
+
65
+ - **`dm run start` blocks by default.** No flag needed for "wait then
66
+ return"; use `--background` only when you genuinely want fire-and-forget.
67
+
68
+ - **`dm run report` is file-masking-only.** The CSV is one row per file
69
+ the worker considered, with `path`, `file_size`, `file_type`, and
70
+ `skip_reason` (e.g. "File archived with Glacier", "File type unsupported
71
+ by data discovery", "Matched a skip filter"). Database runs don't
72
+ produce a report — `not_found` is expected for them, and for any run
73
+ that hasn't reached a terminal state yet.
74
+
75
+ - **`dm libraries delete` refuses to delete libraries imported by a
76
+ ruleset.** Run `dm libraries usage <name>` first to see what depends on
77
+ it; pass `--force` only after you've made an informed decision.
78
+
79
+ - **`dm connections update` preserves the UUID.** Use it to rotate
80
+ passwords or change hosts without invalidating the rulesets and runs
81
+ that already reference the connection.
82
+
83
+ - **`dm rulesets create` is also "update"** — it reads the existing
84
+ `mask_type` from the server, so you only need `--type` for brand-new
85
+ rulesets or to disambiguate a same-name update.
86
+
87
+ - **Discovery is a kind of run.** `dm discover schema <connection>` kicks
88
+ off a discovery run and returns a run id. Poll with `dm run status <id>`,
89
+ then fetch results with `dm discover schema-results <id>` /
90
+ `sdd-report` / `db-report` / `file-report`.
91
+
92
+ - **`dm rulesets validate --file <file> --type <type>`** runs server-side
93
+ validation without committing the ruleset. Use this before `create`
94
+ when you want a clean failure mode for bad YAML.
95
+
96
+ - **"Build a ruleset" usually means the `ruleset-builder` skill, not
97
+ `dm rulesets generate`.** `generate` is server-side scaffolding from a
98
+ JSON generation request. The `ruleset-builder` skill (separate skill in
99
+ this repo) handles the production-quality workflow — hash columns,
100
+ library extraction, refinement — which is what users typically want.
101
+
102
+ - **Names or UUIDs, either works.** `dm connections get <x>`,
103
+ `dm run start -c <x>`, `dm discover schema <x>`, etc. all try the name
104
+ first and fall back to a UUID match. Prefer names for readability;
105
+ reach for UUIDs only when names collide (rare).
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "ruleset-builder",
3
+ "version": "1.0.0",
4
+ "description": "Convert auto-generated DataMasque rulesets into production-ready form. Validate and iterate.",
5
+ "author": { "name": "DataMasque Ltd" },
6
+ "repository": "https://github.com/datamasque/datamasque-cli",
7
+ "license": "Apache-2.0",
8
+ "keywords": ["datamasque", "ruleset", "yaml"]
9
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "ruleset-splitter",
3
+ "version": "1.0.0",
4
+ "description": "Consolidate multi-file DataMasque rulesets for editing, then re-split them back out.",
5
+ "author": { "name": "DataMasque Ltd" },
6
+ "repository": "https://github.com/datamasque/datamasque-cli",
7
+ "license": "Apache-2.0",
8
+ "keywords": ["datamasque", "ruleset", "yaml"]
9
+ }
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "datamasque-cli"
3
- version = "1.0.0"
3
+ version = "1.2.0"
4
4
  description = "Official command-line interface for the DataMasque data-masking platform."
5
5
  authors = [
6
6
  { name = "DataMasque Ltd" },
@@ -99,6 +99,8 @@ ignore = [
99
99
  "S",
100
100
  "SLF001",
101
101
  ]
102
+ # Standalone helper scripts that legitimately print to stdout for shell consumption.
103
+ "scripts/**/*" = ["T20"]
102
104
 
103
105
  [tool.ruff.lint.isort]
104
106
  known-first-party = ["datamasque_cli"]
@@ -19,7 +19,8 @@ except ImportError:
19
19
  path = os.path.expanduser("~/.config/datamasque-cli/config.toml")
20
20
 
21
21
  try:
22
- data = tomllib.loads(open(path).read())
22
+ with open(path, "rb") as f:
23
+ data = tomllib.load(f)
23
24
  except FileNotFoundError:
24
25
  sys.exit(f"No profile config at {path}. Run 'dm auth login' first.")
25
26