strictcli 0.12.0__tar.gz → 0.14.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 (137) hide show
  1. strictcli-0.14.0/.github/workflows/publish.yml +27 -0
  2. strictcli-0.14.0/.npmignore +34 -0
  3. strictcli-0.14.0/.rlsbl/bases/.github/workflows/publish.yml +27 -0
  4. strictcli-0.14.0/.rlsbl/bases/.npmignore +34 -0
  5. strictcli-0.14.0/.rlsbl/changes/.validated +1 -0
  6. strictcli-0.14.0/.rlsbl/changes/0.13.0.jsonl +3 -0
  7. strictcli-0.14.0/.rlsbl/changes/0.13.0.md +5 -0
  8. strictcli-0.14.0/.rlsbl/changes/0.14.0.jsonl +3 -0
  9. strictcli-0.14.0/.rlsbl/changes/0.14.0.md +5 -0
  10. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/hashes.json +3 -2
  11. strictcli-0.14.0/.rlsbl/releases/v0.13.0.toml +4 -0
  12. strictcli-0.14.0/.rlsbl/version +1 -0
  13. {strictcli-0.12.0 → strictcli-0.14.0}/CHANGELOG.md +21 -0
  14. {strictcli-0.12.0 → strictcli-0.14.0}/PKG-INFO +1 -1
  15. {strictcli-0.12.0 → strictcli-0.14.0}/package-lock.json +2 -2
  16. {strictcli-0.12.0 → strictcli-0.14.0}/package.json +1 -1
  17. {strictcli-0.12.0 → strictcli-0.14.0}/pyproject.toml +1 -1
  18. {strictcli-0.12.0 → strictcli-0.14.0}/strictcli/__init__.py +68 -19
  19. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_check_discovery.py +51 -0
  20. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_check_schema.py +7 -0
  21. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_dump_schema.py +66 -0
  22. {strictcli-0.12.0 → strictcli-0.14.0}/uv.lock +1 -1
  23. strictcli-0.12.0/.github/workflows/publish.yml +0 -32
  24. strictcli-0.12.0/.rlsbl/bases/.github/workflows/publish.yml +0 -32
  25. strictcli-0.12.0/.rlsbl/changes/.validated +0 -1
  26. strictcli-0.12.0/.rlsbl/releases/unreleased.toml +0 -0
  27. strictcli-0.12.0/.rlsbl/version +0 -1
  28. {strictcli-0.12.0 → strictcli-0.14.0}/.claude/settings.json +0 -0
  29. {strictcli-0.12.0 → strictcli-0.14.0}/.github/workflows/ci.yml +0 -0
  30. {strictcli-0.12.0 → strictcli-0.14.0}/.gitignore +0 -0
  31. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/bases/.claude/settings.json +0 -0
  32. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/bases/.github/workflows/ci.yml +0 -0
  33. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/bases/.gitignore +0 -0
  34. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/bases/.rlsbl/hooks/post-release.sh +0 -0
  35. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/bases/.rlsbl/hooks/pre-checks.sh +0 -0
  36. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/bases/.rlsbl/hooks/pre-release.sh +0 -0
  37. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/bases/.rlsbl/lint/go.toml +0 -0
  38. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/bases/.rlsbl/lint/npm.toml +0 -0
  39. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/bases/.rlsbl/lint/python.toml +0 -0
  40. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/bases/CHANGELOG.md +0 -0
  41. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/bases/CLAUDE.md +0 -0
  42. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/bases/LICENSE +0 -0
  43. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.10.0.jsonl +0 -0
  44. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.10.0.md +0 -0
  45. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.11.0.jsonl +0 -0
  46. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.11.0.md +0 -0
  47. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.12.0.jsonl +0 -0
  48. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.12.0.md +0 -0
  49. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.4.0.jsonl +0 -0
  50. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.4.0.md +0 -0
  51. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.4.1.jsonl +0 -0
  52. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.4.1.md +0 -0
  53. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.5.0.jsonl +0 -0
  54. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.5.0.md +0 -0
  55. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.6.0.jsonl +0 -0
  56. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.6.0.md +0 -0
  57. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.6.1.jsonl +0 -0
  58. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.6.1.md +0 -0
  59. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.7.0.jsonl +0 -0
  60. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.7.0.md +0 -0
  61. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.7.1.jsonl +0 -0
  62. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.7.1.md +0 -0
  63. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.8.0.jsonl +0 -0
  64. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.8.0.md +0 -0
  65. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.8.1.jsonl +0 -0
  66. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.8.1.md +0 -0
  67. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.8.2.jsonl +0 -0
  68. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.8.2.md +0 -0
  69. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.8.3.jsonl +0 -0
  70. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.8.3.md +0 -0
  71. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.8.4.jsonl +0 -0
  72. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.8.4.md +0 -0
  73. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.8.5.jsonl +0 -0
  74. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.8.5.md +0 -0
  75. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.8.6.jsonl +0 -0
  76. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.8.6.md +0 -0
  77. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.8.7.jsonl +0 -0
  78. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.8.7.md +0 -0
  79. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.9.0.jsonl +0 -0
  80. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.9.0.md +0 -0
  81. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.9.1.jsonl +0 -0
  82. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/0.9.1.md +0 -0
  83. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/changes/unreleased.jsonl +0 -0
  84. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/config.json +0 -0
  85. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/hooks/post-release.sh +0 -0
  86. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/hooks/pre-checks.sh +0 -0
  87. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/hooks/pre-release.sh +0 -0
  88. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/lint/go.toml +0 -0
  89. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/lint/npm.toml +0 -0
  90. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/lint/python.toml +0 -0
  91. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/releases/v0.10.0.toml +0 -0
  92. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/releases/v0.11.0.toml +0 -0
  93. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/releases/v0.12.0.toml +0 -0
  94. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/releases/v0.8.5.toml +0 -0
  95. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/releases/v0.8.6.toml +0 -0
  96. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/releases/v0.8.7.toml +0 -0
  97. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/releases/v0.9.0.toml +0 -0
  98. {strictcli-0.12.0 → strictcli-0.14.0}/.rlsbl/releases/v0.9.1.toml +0 -0
  99. {strictcli-0.12.0 → strictcli-0.14.0}/.strictcli/schema.json +0 -0
  100. {strictcli-0.12.0 → strictcli-0.14.0}/CLAUDE.md +0 -0
  101. {strictcli-0.12.0 → strictcli-0.14.0}/LICENSE +0 -0
  102. {strictcli-0.12.0 → strictcli-0.14.0}/README.md +0 -0
  103. {strictcli-0.12.0 → strictcli-0.14.0}/index.js +0 -0
  104. {strictcli-0.12.0 → strictcli-0.14.0}/postinstall.js +0 -0
  105. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_arg_default.py +0 -0
  106. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_at_prefix.py +0 -0
  107. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_auto_version.py +0 -0
  108. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_check_command.py +0 -0
  109. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_check_runner.py +0 -0
  110. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_check_types.py +0 -0
  111. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_choices.py +0 -0
  112. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_command_help_suggestion.py +0 -0
  113. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_config.py +0 -0
  114. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_config_file_path.py +0 -0
  115. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_deep_nesting.py +0 -0
  116. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_dependencies.py +0 -0
  117. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_deprecated.py +0 -0
  118. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_e2e.py +0 -0
  119. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_env.py +0 -0
  120. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_exit_codes.py +0 -0
  121. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_float_type.py +0 -0
  122. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_global_flags.py +0 -0
  123. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_help.py +0 -0
  124. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_int_type.py +0 -0
  125. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_mutex.py +0 -0
  126. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_nesting.py +0 -0
  127. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_parser.py +0 -0
  128. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_passthrough.py +0 -0
  129. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_registration.py +0 -0
  130. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_repeatable.py +0 -0
  131. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_tagdsl.py +0 -0
  132. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_tags.py +0 -0
  133. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_toml_loading.py +0 -0
  134. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_validate.py +0 -0
  135. {strictcli-0.12.0 → strictcli-0.14.0}/tests/test_variadic.py +0 -0
  136. {strictcli-0.12.0 → strictcli-0.14.0}/todo/.defer/deferred.md +0 -0
  137. {strictcli-0.12.0 → strictcli-0.14.0}/todo/.done/original-idea.md +0 -0
@@ -0,0 +1,27 @@
1
+ name: Publish
2
+ on:
3
+ release:
4
+ types: [published]
5
+ workflow_dispatch:
6
+ permissions:
7
+ contents: read
8
+ id-token: write
9
+ jobs:
10
+ pypi:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v6
14
+ - uses: astral-sh/setup-uv@v7
15
+ - run: uv build
16
+ - uses: pypa/gh-action-pypi-publish@release/v1
17
+ npm:
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@v6
21
+ - uses: actions/setup-node@v6
22
+ with:
23
+ node-version: 24
24
+ registry-url: https://registry.npmjs.org
25
+ - run: npm publish --provenance --access public
26
+ env:
27
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -0,0 +1,34 @@
1
+ # Development and CI
2
+ tests/
3
+ test/
4
+ __tests__/
5
+ .github/
6
+ scripts/
7
+ docs/
8
+ todo/
9
+ e2e/
10
+
11
+ # rlsbl metadata
12
+ .rlsbl/
13
+ .rlsbl-monorepo/
14
+
15
+ # Python artifacts (for multi-target projects)
16
+ __pycache__/
17
+ *.pyc
18
+ *.py
19
+ *.toml
20
+ .venv/
21
+
22
+ # Build/cache
23
+ coverage/
24
+ .nyc_output/
25
+
26
+ # Editor/OS
27
+ .DS_Store
28
+ *.log
29
+ .env
30
+ .env.local
31
+
32
+ # Selfdoc
33
+ .selfdoc/
34
+ selfdoc.json
@@ -0,0 +1,27 @@
1
+ name: Publish
2
+ on:
3
+ release:
4
+ types: [published]
5
+ workflow_dispatch:
6
+ permissions:
7
+ contents: read
8
+ id-token: write
9
+ jobs:
10
+ pypi:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v6
14
+ - uses: astral-sh/setup-uv@v7
15
+ - run: uv build
16
+ - uses: pypa/gh-action-pypi-publish@release/v1
17
+ npm:
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@v6
21
+ - uses: actions/setup-node@v6
22
+ with:
23
+ node-version: 24
24
+ registry-url: https://registry.npmjs.org
25
+ - run: npm publish --provenance --access public
26
+ env:
27
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -0,0 +1,34 @@
1
+ # Development and CI
2
+ tests/
3
+ test/
4
+ __tests__/
5
+ .github/
6
+ scripts/
7
+ docs/
8
+ todo/
9
+ e2e/
10
+
11
+ # rlsbl metadata
12
+ .rlsbl/
13
+ .rlsbl-monorepo/
14
+
15
+ # Python artifacts (for multi-target projects)
16
+ __pycache__/
17
+ *.pyc
18
+ *.py
19
+ *.toml
20
+ .venv/
21
+
22
+ # Build/cache
23
+ coverage/
24
+ .nyc_output/
25
+
26
+ # Editor/OS
27
+ .DS_Store
28
+ *.log
29
+ .env
30
+ .env.local
31
+
32
+ # Selfdoc
33
+ .selfdoc/
34
+ selfdoc.json
@@ -0,0 +1 @@
1
+ caba81b23948f3b79c908ccd06ed76596881c075
@@ -0,0 +1,3 @@
1
+ {"commits":["12ad7c022938b07cf1f44b20bdbe9de0ae6a84be"],"user_facing":true,"description":"**New feature.** Schema dump (--dump-schema) now includes a project_id field read from pyproject.toml, providing provenance for schema validation.","type":"feature"}
2
+ {"commits":["a50cf7899c938974b12e8e74201bdf58c0205a55"],"user_facing":false}
3
+ {"commits":["214721745a82a47189a13b2443fb9ddc59826347"],"user_facing":false}
@@ -0,0 +1,5 @@
1
+ ## 0.13.0
2
+
3
+ ### Features
4
+
5
+ - **New feature.** Schema dump (--dump-schema) now includes a project_id field read from pyproject.toml, providing provenance for schema validation.
@@ -0,0 +1,3 @@
1
+ {"commits":["f2f2de83edc0b7fe24f46744201e8be1407f28a3"],"user_facing":false}
2
+ {"commits":["a2e1d64fb5f695169fafa63e5d1b69fe5c4e043d"],"user_facing":true,"description":"**New feature.** `checks_embed` parameter accepts raw TOML bytes, enabling inline checks configuration without a file on disk.","type":"feature"}
3
+ {"commits":["09799755dc2ef93a088f739a46001102bcb2aa5e"],"user_facing":false}
@@ -0,0 +1,5 @@
1
+ ## 0.14.0
2
+
3
+ ### Features
4
+
5
+ - **New feature.** `checks_embed` parameter accepts raw TOML bytes, enabling inline checks configuration without a file on disk.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  ".github/workflows/ci.yml": "b4e075171b131d25f9cce55a3e3a30732e95a2a794af00f354ee3200626d6a04",
3
- ".github/workflows/publish.yml": "8befe0164321eb68e9465c634aa15d6ccb30d46fa669f7d30bf3ed064ad258ba",
3
+ ".github/workflows/publish.yml": "e77dcd0b5d1c1abae5424e5f3b73862f44f06b5d46209cbe71bc04d6baa6b5be",
4
4
  "CHANGELOG.md": "7a6ec46141007c18090a0ff325e2dd68cd51588c4e0b5af7b580365e5eaaca85",
5
5
  ".gitignore": "c89d76c6c43e7f0897b3472a15b7c0103e0be5b3a04c2b44e585fa48bdcda2bb",
6
6
  "LICENSE": "e958892abc1dd5d991fca6d528e695d335f25773d373d2e0be5f4a9507d8f8d5",
@@ -11,5 +11,6 @@
11
11
  ".rlsbl/hooks/pre-checks.sh": "60c57d4563263124530e6f33aca77550ea2161ad3b33b3ba6ad1c03fea9eb6e2",
12
12
  ".rlsbl/lint/python.toml": "9cfbcef2e010d5fad243437bf2fd8df54a1fae70ee0762f9d11da70fe207501c",
13
13
  ".rlsbl/lint/go.toml": "807d5cf760baad6d571a5bfd43cb163efe356f7462ba9f1918d72de45f62339f",
14
- ".rlsbl/lint/npm.toml": "baa36014e65923d8cc5c8b83c77fa651da79351627570476402da66840027645"
14
+ ".rlsbl/lint/npm.toml": "baa36014e65923d8cc5c8b83c77fa651da79351627570476402da66840027645",
15
+ ".npmignore": "7c0eea9785c0318aeb39bf8b670ce8384f4a5b812fde69ce23d7d018057325a3"
15
16
  }
@@ -0,0 +1,4 @@
1
+ # Version bump type: patch, minor, or major
2
+ bump = "minor"
3
+ include = ["pypi", "npm"]
4
+ exclude = []
@@ -0,0 +1 @@
1
+ 0.60.1
@@ -2,6 +2,27 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## 0.14.0
6
+
7
+ checks_embed for inline TOML data
8
+
9
+ <details>
10
+ <summary>Context</summary>
11
+
12
+ Adds checks_embed parameter as an alternative to checks_path, allowing TOML bytes to be passed directly without requiring a file on disk.
13
+
14
+ </details>
15
+
16
+ ### Features
17
+
18
+ - **New feature.** `checks_embed` parameter accepts raw TOML bytes, enabling inline checks configuration without a file on disk.
19
+
20
+ ## 0.13.0
21
+
22
+ ### Features
23
+
24
+ - **New feature.** Schema dump (--dump-schema) now includes a project_id field read from pyproject.toml, providing provenance for schema validation.
25
+
5
26
  ## 0.12.0
6
27
 
7
28
  ### Breaking
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: strictcli
3
- Version: 0.12.0
3
+ Version: 0.14.0
4
4
  Summary: A strict, zero-dependency CLI framework for Python
5
5
  Project-URL: Homepage, https://github.com/smm-h/strictcli
6
6
  Project-URL: Repository, https://github.com/smm-h/strictcli
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "strictcli",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "strictcli",
9
- "version": "0.12.0",
9
+ "version": "0.14.0",
10
10
  "hasInstallScript": true,
11
11
  "license": "MIT"
12
12
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strictcli",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "A strict, zero-dependency CLI framework for Python (npm wrapper)",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "strictcli"
7
- version = "0.12.0"
7
+ version = "0.14.0"
8
8
  description = "A strict, zero-dependency CLI framework for Python"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- __version__ = "0.12.0"
5
+ __version__ = "0.14.0"
6
6
 
7
7
  __all__ = [
8
8
  "App", "Flag", "Arg", "Tag", "MutexGroup", "CoRequired", "Requires",
@@ -130,37 +130,32 @@ _CHECK_REQUIRED_FIELDS = {"tags", "severity", "fast", "pure", "needs_network", "
130
130
  _CHECK_VALID_SEVERITIES = {"error", "warn"}
131
131
 
132
132
 
133
- def _load_checks_toml(path: str | Path) -> tuple[str, dict[str, _CheckDef]]:
134
- """Parse and validate a checks.toml file, returning (app_name, check_defs).
133
+ def _parse_checks_toml(data: bytes) -> tuple[str, dict[str, _CheckDef]]:
134
+ """Parse and validate checks TOML data, returning (app_name, check_defs).
135
135
 
136
- Raises ValueError on any schema violation, file error, or invalid TOML.
136
+ Raises ValueError on any schema violation or invalid TOML.
137
137
  """
138
- path = Path(path)
139
- try:
140
- raw = path.read_bytes()
141
- except OSError as exc:
142
- raise ValueError(f"checks.toml: {exc}") from exc
143
138
  try:
144
- data = tomllib.loads(raw.decode())
139
+ parsed = tomllib.loads(data.decode())
145
140
  except (tomllib.TOMLDecodeError, UnicodeDecodeError) as exc:
146
141
  raise ValueError(f"checks.toml: {exc}") from exc
147
142
 
148
143
  # Only "app" and [checks] are allowed at the top level
149
- for key in data:
144
+ for key in parsed:
150
145
  if key not in ("app", "checks"):
151
146
  raise ValueError(f'checks.toml: unknown top-level key "{key}"')
152
147
 
153
148
  # Validate required "app" field
154
- if "app" not in data:
149
+ if "app" not in parsed:
155
150
  raise ValueError('checks.toml: missing required top-level key "app"')
156
- if not isinstance(data["app"], str) or not data["app"]:
151
+ if not isinstance(parsed["app"], str) or not parsed["app"]:
157
152
  raise ValueError('checks.toml: "app" must be a non-empty string')
158
- app_name = data["app"]
153
+ app_name = parsed["app"]
159
154
 
160
- if "checks" not in data:
155
+ if "checks" not in parsed:
161
156
  return (app_name, {})
162
157
 
163
- checks_section = data["checks"]
158
+ checks_section = parsed["checks"]
164
159
  if not isinstance(checks_section, dict):
165
160
  raise ValueError("checks.toml: [checks] must be a table")
166
161
 
@@ -253,6 +248,19 @@ def _load_checks_toml(path: str | Path) -> tuple[str, dict[str, _CheckDef]]:
253
248
  return (app_name, result)
254
249
 
255
250
 
251
+ def _load_checks_toml(path: str | Path) -> tuple[str, dict[str, _CheckDef]]:
252
+ """Read and parse a checks.toml file, returning (app_name, check_defs).
253
+
254
+ Raises ValueError on any file error, schema violation, or invalid TOML.
255
+ """
256
+ path = Path(path)
257
+ try:
258
+ raw = path.read_bytes()
259
+ except OSError as exc:
260
+ raise ValueError(f"checks.toml: {exc}") from exc
261
+ return _parse_checks_toml(raw)
262
+
263
+
256
264
  class _HelpRequested(Exception):
257
265
  """Raised when --help or -h is encountered."""
258
266
 
@@ -675,6 +683,7 @@ class App:
675
683
  config_path: str | None = None
676
684
  config_format: str = "json"
677
685
  checks_path: str | Path | None = None
686
+ checks_embed: bytes | None = None
678
687
  flags: list[Flag] = field(default_factory=list)
679
688
  _commands: dict[str, Command] = field(default_factory=dict)
680
689
  _groups: dict[str, Group] = field(default_factory=dict)
@@ -712,6 +721,8 @@ class App:
712
721
  self._register_config_group()
713
722
  # Discover checks TOML
714
723
  self._check_context_factory: Callable | None = None
724
+ if self.checks_path is not None and self.checks_embed is not None:
725
+ raise ValueError("cannot use both checks_path and checks_embed")
715
726
  if self.checks_path is not None:
716
727
  checks_toml_path = Path(self.checks_path).resolve()
717
728
  if not checks_toml_path.is_file():
@@ -723,6 +734,14 @@ class App:
723
734
  )
724
735
  self._checks_enabled = True
725
736
  self._register_check_command()
737
+ elif self.checks_embed is not None:
738
+ app_name, self._check_defs = _parse_checks_toml(self.checks_embed)
739
+ if app_name != self.name:
740
+ raise ValueError(
741
+ f'checks.toml: app "{app_name}" does not match app name "{self.name}"'
742
+ )
743
+ self._checks_enabled = True
744
+ self._register_check_command()
726
745
  else:
727
746
  self._check_defs: dict[str, _CheckDef] = {}
728
747
  self._checks_enabled = False
@@ -1460,7 +1479,11 @@ class App:
1460
1479
  print(_format_version(self))
1461
1480
  sys.exit(0)
1462
1481
  except _DumpSchemaRequested:
1463
- path = _write_schema(self)
1482
+ try:
1483
+ path = _write_schema(self)
1484
+ except RuntimeError as e:
1485
+ print(f"error: {e}", file=sys.stderr)
1486
+ sys.exit(1)
1464
1487
  print(path)
1465
1488
  sys.exit(0)
1466
1489
  except _ParseError as e:
@@ -1503,8 +1526,13 @@ class App:
1503
1526
  except _VersionRequested:
1504
1527
  stdout_buf.write(_format_version(self) + "\n")
1505
1528
  except _DumpSchemaRequested:
1506
- path = _write_schema(self)
1507
- stdout_buf.write(path + "\n")
1529
+ try:
1530
+ path = _write_schema(self)
1531
+ except RuntimeError as e:
1532
+ stderr_buf.write(f"error: {e}\n")
1533
+ exit_code = 1
1534
+ else:
1535
+ stdout_buf.write(path + "\n")
1508
1536
  except _ParseError as e:
1509
1537
  stderr_buf.write(f"error: {e}\n")
1510
1538
  prefix = e.command_prefix or self.name
@@ -3087,14 +3115,35 @@ def _build_schema_defaults() -> dict:
3087
3115
  }
3088
3116
 
3089
3117
 
3118
+ def _read_project_id() -> str:
3119
+ """Read project name from pyproject.toml in the current working directory."""
3120
+ pyproject_path = Path(os.getcwd()) / "pyproject.toml"
3121
+ if not pyproject_path.exists():
3122
+ raise RuntimeError(
3123
+ "Cannot determine project_id: pyproject.toml not found "
3124
+ "or missing [project].name"
3125
+ )
3126
+ with open(pyproject_path, "rb") as f:
3127
+ data = tomllib.load(f)
3128
+ project_name = data.get("project", {}).get("name")
3129
+ if not project_name:
3130
+ raise RuntimeError(
3131
+ "Cannot determine project_id: pyproject.toml not found "
3132
+ "or missing [project].name"
3133
+ )
3134
+ return project_name
3135
+
3136
+
3090
3137
  def _dump_schema(app: App) -> dict:
3091
3138
  """Produce a JSON-serializable dict representing the app's command tree.
3092
3139
 
3093
3140
  Fields whose values match the schema defaults are omitted.
3094
3141
  The top-level ``defaults`` key documents what each missing field means.
3095
3142
  """
3143
+ project_id = _read_project_id()
3096
3144
  schema: dict = {
3097
3145
  "defaults": _build_schema_defaults(),
3146
+ "project_id": project_id,
3098
3147
  "name": app.name,
3099
3148
  "version": app.version,
3100
3149
  "help": app.help,
@@ -293,3 +293,54 @@ depends_on = []
293
293
  name="testapp", version="1.0.0", help="test app",
294
294
  checks_path=str(toml_file),
295
295
  )
296
+
297
+
298
+ class TestChecksEmbed:
299
+ def test_checks_embed_enables_checks(self, tmp_path, monkeypatch):
300
+ """checks_embed with valid TOML bytes enables checks."""
301
+ monkeypatch.chdir(tmp_path)
302
+
303
+ app = strictcli.App(
304
+ name="testapp", version="1.0.0", help="test app",
305
+ checks_embed=VALID_TOML.encode(),
306
+ )
307
+ assert app._checks_enabled is True
308
+ assert "lint-code" in app._check_defs
309
+ assert "check-deps" in app._check_defs
310
+
311
+ def test_checks_embed_and_checks_path_raises(self, tmp_path, monkeypatch):
312
+ """Using both checks_path and checks_embed raises ValueError."""
313
+ monkeypatch.chdir(tmp_path)
314
+ toml_file = tmp_path / "checks.toml"
315
+ toml_file.write_text(VALID_TOML)
316
+
317
+ with pytest.raises(ValueError, match="cannot use both checks_path and checks_embed"):
318
+ strictcli.App(
319
+ name="testapp", version="1.0.0", help="test app",
320
+ checks_path=str(toml_file),
321
+ checks_embed=VALID_TOML.encode(),
322
+ )
323
+
324
+ def test_checks_embed_invalid_toml(self, tmp_path, monkeypatch):
325
+ """checks_embed with garbage bytes raises ValueError."""
326
+ monkeypatch.chdir(tmp_path)
327
+
328
+ with pytest.raises(ValueError, match="checks.toml:"):
329
+ strictcli.App(
330
+ name="testapp", version="1.0.0", help="test app",
331
+ checks_embed=b"\x80\x81 not valid toml {{{{",
332
+ )
333
+
334
+ def test_checks_embed_wrong_app_name(self, tmp_path, monkeypatch):
335
+ """checks_embed with mismatched app name raises ValueError."""
336
+ monkeypatch.chdir(tmp_path)
337
+ wrong_toml = 'app = "wrong"\n'
338
+
339
+ with pytest.raises(
340
+ ValueError,
341
+ match=r'checks.toml: app "wrong" does not match app name "testapp"',
342
+ ):
343
+ strictcli.App(
344
+ name="testapp", version="1.0.0", help="test app",
345
+ checks_embed=wrong_toml.encode(),
346
+ )
@@ -2,9 +2,16 @@
2
2
 
3
3
  import json
4
4
 
5
+ import pytest
5
6
  import strictcli
6
7
 
7
8
 
9
+ @pytest.fixture(autouse=True)
10
+ def _pyproject_in_tmp(tmp_path):
11
+ """Ensure every test that uses tmp_path has a pyproject.toml for project_id."""
12
+ (tmp_path / "pyproject.toml").write_text('[project]\nname = "testproject"\n')
13
+
14
+
8
15
  CHECKS_TOML = """\
9
16
  app = "testapp"
10
17
 
@@ -5,8 +5,17 @@ from __future__ import annotations
5
5
  import json
6
6
  import os
7
7
 
8
+ import pytest
8
9
  import strictcli
9
10
 
11
+ _PYPROJECT_TOML = '[project]\nname = "testproject"\n'
12
+
13
+
14
+ @pytest.fixture(autouse=True)
15
+ def _pyproject_in_tmp(tmp_path):
16
+ """Ensure every test that uses tmp_path has a pyproject.toml for project_id."""
17
+ (tmp_path / "pyproject.toml").write_text(_PYPROJECT_TOML)
18
+
10
19
 
11
20
  def _make_app(**kwargs):
12
21
  """Create a minimal app for testing."""
@@ -662,3 +671,60 @@ class TestSchemaNonDefaultValues:
662
671
  app.test(["--dump-schema"])
663
672
  data = json.loads((tmp_path / ".strictcli" / "schema.json").read_text())
664
673
  assert data["config"] is True
674
+
675
+
676
+ class TestSchemaProjectId:
677
+ """Schema contains project_id from pyproject.toml."""
678
+
679
+ def test_project_id_present(self, tmp_path, monkeypatch):
680
+ monkeypatch.chdir(tmp_path)
681
+ app = _make_app()
682
+
683
+ @app.command("noop", help="Does nothing")
684
+ def noop():
685
+ pass
686
+
687
+ app.test(["--dump-schema"])
688
+ data = json.loads((tmp_path / ".strictcli" / "schema.json").read_text())
689
+ assert data["project_id"] == "testproject"
690
+
691
+ def test_project_id_custom_name(self, tmp_path, monkeypatch):
692
+ (tmp_path / "pyproject.toml").write_text(
693
+ '[project]\nname = "my-custom-tool"\n'
694
+ )
695
+ monkeypatch.chdir(tmp_path)
696
+ app = _make_app()
697
+
698
+ @app.command("noop", help="Does nothing")
699
+ def noop():
700
+ pass
701
+
702
+ app.test(["--dump-schema"])
703
+ data = json.loads((tmp_path / ".strictcli" / "schema.json").read_text())
704
+ assert data["project_id"] == "my-custom-tool"
705
+
706
+ def test_project_id_error_no_pyproject(self, tmp_path, monkeypatch):
707
+ os.remove(tmp_path / "pyproject.toml")
708
+ monkeypatch.chdir(tmp_path)
709
+ app = _make_app()
710
+
711
+ @app.command("noop", help="Does nothing")
712
+ def noop():
713
+ pass
714
+
715
+ result = app.test(["--dump-schema"])
716
+ assert result.exit_code != 0
717
+ assert "project_id" in result.stderr
718
+
719
+ def test_project_id_error_no_project_name(self, tmp_path, monkeypatch):
720
+ (tmp_path / "pyproject.toml").write_text("[tool.something]\nkey = 1\n")
721
+ monkeypatch.chdir(tmp_path)
722
+ app = _make_app()
723
+
724
+ @app.command("noop", help="Does nothing")
725
+ def noop():
726
+ pass
727
+
728
+ result = app.test(["--dump-schema"])
729
+ assert result.exit_code != 0
730
+ assert "project_id" in result.stderr
@@ -232,7 +232,7 @@ wheels = [
232
232
 
233
233
  [[package]]
234
234
  name = "strictcli"
235
- version = "0.12.0"
235
+ version = "0.14.0"
236
236
  source = { editable = "." }
237
237
 
238
238
  [package.dev-dependencies]
@@ -1,32 +0,0 @@
1
- name: Publish
2
-
3
- on:
4
- release:
5
- types: [published]
6
- workflow_dispatch:
7
-
8
-
9
- permissions:
10
- contents: read
11
- id-token: write
12
-
13
- jobs:
14
- pypi:
15
- runs-on: ubuntu-latest
16
- steps:
17
- - uses: actions/checkout@v6
18
- - uses: astral-sh/setup-uv@v7
19
- - run: uv build
20
- - uses: pypa/gh-action-pypi-publish@release/v1
21
-
22
- npm:
23
- runs-on: ubuntu-latest
24
- steps:
25
- - uses: actions/checkout@v6
26
- - uses: actions/setup-node@v6
27
- with:
28
- node-version: 24
29
- registry-url: https://registry.npmjs.org
30
- - run: npm publish --provenance --access public
31
- env:
32
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -1,32 +0,0 @@
1
- name: Publish
2
-
3
- on:
4
- release:
5
- types: [published]
6
- workflow_dispatch:
7
-
8
-
9
- permissions:
10
- contents: read
11
- id-token: write
12
-
13
- jobs:
14
- pypi:
15
- runs-on: ubuntu-latest
16
- steps:
17
- - uses: actions/checkout@v6
18
- - uses: astral-sh/setup-uv@v7
19
- - run: uv build
20
- - uses: pypa/gh-action-pypi-publish@release/v1
21
-
22
- npm:
23
- runs-on: ubuntu-latest
24
- steps:
25
- - uses: actions/checkout@v6
26
- - uses: actions/setup-node@v6
27
- with:
28
- node-version: 24
29
- registry-url: https://registry.npmjs.org
30
- - run: npm publish --provenance --access public
31
- env:
32
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -1 +0,0 @@
1
- c161283293520cc94f39d9cfec589f6f4cf5a352
File without changes
@@ -1 +0,0 @@
1
- 0.45.0
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes