hackerrank-cli 2026.10.8__tar.gz → 2026.10.30__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 (76) hide show
  1. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.github/dependabot.yml +2 -0
  2. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.github/workflows/autofix.yml +3 -4
  3. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.github/workflows/binaries.yml +12 -4
  4. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.github/workflows/ci.yml +5 -5
  5. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.github/workflows/docs.yml +2 -2
  6. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.github/workflows/lint.yml +2 -2
  7. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.github/workflows/publish-site.yml +2 -2
  8. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.github/workflows/release.yml +2 -2
  9. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/Dockerfile +1 -1
  10. {hackerrank_cli-2026.10.8/src/hackerrank_cli.egg-info → hackerrank_cli-2026.10.30}/PKG-INFO +18 -2
  11. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/README.rst +15 -0
  12. hackerrank_cli-2026.10.30/VERSION +1 -0
  13. hackerrank_cli-2026.10.30/docs/source/changelog/2026.10.30.md +14 -0
  14. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/source/development.rst +5 -5
  15. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/pyproject.toml +19 -16
  16. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli/__init__.py +123 -1
  17. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli/_version.py +3 -3
  18. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30/src/hackerrank_cli.egg-info}/PKG-INFO +18 -2
  19. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli.egg-info/SOURCES.txt +6 -3
  20. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli.egg-info/requires.txt +1 -1
  21. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli.egg-info/scm_file_list.json +5 -2
  22. hackerrank_cli-2026.10.30/src/hackerrank_cli.egg-info/scm_version.json +8 -0
  23. hackerrank_cli-2026.10.30/tests/test_help/test_help_questions_.txt +11 -0
  24. hackerrank_cli-2026.10.30/tests/test_help/test_help_questions_set_system_prompt_.txt +9 -0
  25. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/tests/test_help/test_help_root_.txt +1 -1
  26. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/tests/test_help.py +2 -1
  27. hackerrank_cli-2026.10.30/tests/test_system_prompt.py +266 -0
  28. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/uv.lock +644 -523
  29. hackerrank_cli-2026.10.8/VERSION +0 -1
  30. hackerrank_cli-2026.10.8/src/hackerrank_cli.egg-info/scm_version.json +0 -8
  31. hackerrank_cli-2026.10.8/tests/test_help/test_help_questions_.txt +0 -10
  32. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.dockerignore +0 -0
  33. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.gitattributes +0 -0
  34. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.github/workflows/dependabot-merge.yml +0 -0
  35. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.github/workflows/nix.yml +0 -0
  36. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.gitignore +0 -0
  37. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.vale.ini +0 -0
  38. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/LICENSE +0 -0
  39. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/MANIFEST.in +0 -0
  40. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/bin/__init__.py +0 -0
  41. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/bin/hackerrank_wrapper.py +0 -0
  42. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/__init__.py +0 -0
  43. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/source/__init__.py +0 -0
  44. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/source/changelog/2026.10.1.1.md +0 -0
  45. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/source/changelog/2026.10.7.md +0 -0
  46. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/source/changelog/2026.10.8.md +0 -0
  47. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/source/changelog.md +0 -0
  48. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/source/cli.rst +0 -0
  49. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/source/conf.py +0 -0
  50. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/source/index.rst +0 -0
  51. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/source/macos-releases.rst +0 -0
  52. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/source/migration.rst +0 -0
  53. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/source/usage.rst +0 -0
  54. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/flake.lock +0 -0
  55. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/flake.nix +0 -0
  56. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/newsfragments/.gitkeep +0 -0
  57. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/prek.toml +0 -0
  58. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/setup.cfg +0 -0
  59. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/spelling_private_dict.txt +0 -0
  60. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli/__main__.py +0 -0
  61. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli/_sources.py +0 -0
  62. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli/py.typed +0 -0
  63. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli.egg-info/dependency_links.txt +0 -0
  64. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli.egg-info/entry_points.txt +0 -0
  65. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli.egg-info/not-zip-safe +0 -0
  66. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli.egg-info/top_level.txt +0 -0
  67. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/styles/snapper/LongProseLine.yml +0 -0
  68. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/styles/snapper/SemanticLineBreaks.yml +0 -0
  69. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/tests/__init__.py +0 -0
  70. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/tests/test_archive.py +0 -0
  71. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/tests/test_cli.py +0 -0
  72. /hackerrank_cli-2026.10.8/tests/test_help/test_help_archive_.txt → /hackerrank_cli-2026.10.30/tests/test_help/test_help_questions_archive_.txt +0 -0
  73. /hackerrank_cli-2026.10.8/tests/test_help/test_help_upload_.txt → /hackerrank_cli-2026.10.30/tests/test_help/test_help_questions_upload_.txt +0 -0
  74. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/tests/test_http.py +0 -0
  75. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/tests/test_sources.py +0 -0
  76. {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/zizmor.yml +0 -0
@@ -3,6 +3,8 @@ version: 2
3
3
 
4
4
  updates:
5
5
  - package-ecosystem: uv
6
+ allow:
7
+ - dependency-type: all
6
8
  directory: /
7
9
  schedule:
8
10
  interval: daily
@@ -2,8 +2,7 @@
2
2
  name: autofix.ci
3
3
 
4
4
  on:
5
- pull_request:
6
- branches: [main]
5
+ pull_request: {}
7
6
  push:
8
7
  branches: [main]
9
8
 
@@ -18,9 +17,9 @@ jobs:
18
17
  with:
19
18
  fetch-depth: 0
20
19
  persist-credentials: false
21
- - uses: astral-sh/setup-uv@v10.2.0
20
+ - uses: astral-sh/setup-uv@v10.3.2
22
21
  with:
23
- version: 0.12.19
22
+ version: 0.13.0
24
23
  python-version: '3.13'
25
24
  - name: Run repository fixers and checks
26
25
  run: uv run --locked prek run --all-files --stage pre-commit --no-fail-fast
@@ -47,9 +47,9 @@ jobs:
47
47
  with:
48
48
  persist-credentials: false
49
49
  fetch-depth: 0
50
- - uses: astral-sh/setup-uv@v10.2.0
50
+ - uses: astral-sh/setup-uv@v10.3.2
51
51
  with:
52
- version: 0.12.19
52
+ version: 0.13.0
53
53
  python-version: '3.13'
54
54
  - name: Require release credentials
55
55
  if: runner.os == 'macOS' && inputs.notarize
@@ -65,8 +65,9 @@ jobs:
65
65
  : "${APPLE_ID:?Missing APPLE_ID}" "${APPLE_TEAM_ID:?Missing APPLE_TEAM_ID}" "${APPLE_APP_PASSWORD:?Missing APPLE_APP_PASSWORD}"
66
66
  # The action removes its temporary keychain in its post step.
67
67
  - name: Import the Developer ID certificate
68
+ id: signing_certificate
68
69
  if: runner.os == 'macOS' && inputs.notarize
69
- uses: Apple-Actions/import-codesign-certs@5142e029c445c10ffc7149d172e540235a065466 # v7.0.0
70
+ uses: Apple-Actions/import-codesign-certs@77ba2a2a8f5cfc8c405ebf3159f767c323e238f9 # v7.1.0
70
71
  with:
71
72
  p12-file-base64: ${{ secrets.DEVELOPER_ID_APP_CERT_P12_BASE64 }}
72
73
  p12-password: ${{ secrets.DEVELOPER_ID_APP_CERT_PASSWORD }}
@@ -75,12 +76,19 @@ jobs:
75
76
  env:
76
77
  NAME: ${{ matrix.name }}
77
78
  NOTARIZE: ${{ inputs.notarize }}
79
+ IDENTITIES: ${{ steps.signing_certificate.outputs.identities }}
78
80
  run: |
79
81
  signing=()
80
82
  if [ "$RUNNER_OS" = macOS ] && [ "$NOTARIZE" = true ]; then
81
83
  # Sign embedded libraries before packing the one-file archive.
82
84
  # PyInstaller enables the hardened runtime and secure timestamp.
83
- signing=(--codesign-identity "Developer ID Application")
85
+ signing_identity="$(jq -er '
86
+ [.[] | select(.name | startswith("Developer ID Application:"))]
87
+ | if length == 1 then .[0].hash
88
+ else error("Expected exactly one Developer ID Application identity; found \(length)")
89
+ end
90
+ ' <<< "$IDENTITIES")"
91
+ signing=(--codesign-identity "$signing_identity")
84
92
  fi
85
93
  uv run --locked --group binary pyinstaller --clean --onefile --copy-metadata hackerrank-cli --name "$NAME" "${signing[@]}" bin/hackerrank_wrapper.py
86
94
  - name: Verify Developer ID signature
@@ -16,7 +16,7 @@ jobs:
16
16
  tests:
17
17
  strategy:
18
18
  matrix:
19
- python: ['3.13', '3.14']
19
+ python: ['3.13', '3.14', '3.15']
20
20
  os: [ubuntu-latest, macos-latest, windows-latest]
21
21
  runs-on: ${{ matrix.os }}
22
22
  steps:
@@ -24,9 +24,9 @@ jobs:
24
24
  with:
25
25
  persist-credentials: false
26
26
  fetch-depth: 0
27
- - uses: astral-sh/setup-uv@v10.2.0
27
+ - uses: astral-sh/setup-uv@v10.3.2
28
28
  with:
29
- version: 0.12.19
29
+ version: 0.13.0
30
30
  python-version: ${{ matrix.python }}
31
31
  enable-cache: true
32
32
  - run: uv run --locked pytest
@@ -37,9 +37,9 @@ jobs:
37
37
  with:
38
38
  persist-credentials: false
39
39
  fetch-depth: 0
40
- - uses: astral-sh/setup-uv@v10.2.0
40
+ - uses: astral-sh/setup-uv@v10.3.2
41
41
  with:
42
- version: 0.12.19
42
+ version: 0.13.0
43
43
  python-version: '3.13'
44
44
  - run: uv build
45
45
  - run: uv run --locked twine check dist/*
@@ -19,9 +19,9 @@ jobs:
19
19
  with:
20
20
  persist-credentials: false
21
21
  fetch-depth: 0
22
- - uses: astral-sh/setup-uv@v10.2.0
22
+ - uses: astral-sh/setup-uv@v10.3.2
23
23
  with:
24
- version: 0.12.19
24
+ version: 0.13.0
25
25
  python-version: '3.13'
26
26
  - run: uv run --locked prek run --all-files --stage manual --group docs
27
27
  - run: uv run --locked towncrier build --draft --version unreleased
@@ -27,9 +27,9 @@ jobs:
27
27
  with:
28
28
  persist-credentials: false
29
29
  fetch-depth: 0
30
- - uses: astral-sh/setup-uv@v10.2.0
30
+ - uses: astral-sh/setup-uv@v10.3.2
31
31
  with:
32
- version: 0.12.19
32
+ version: 0.13.0
33
33
  python-version: '3.13'
34
34
  enable-cache: true
35
35
  - run: uv run --locked prek run --all-files --no-fail-fast
@@ -23,9 +23,9 @@ jobs:
23
23
  with:
24
24
  persist-credentials: false
25
25
  fetch-depth: 0
26
- - uses: astral-sh/setup-uv@v10.2.0
26
+ - uses: astral-sh/setup-uv@v10.3.2
27
27
  with:
28
- version: 0.12.19
28
+ version: 0.13.0
29
29
  python-version: '3.13'
30
30
  - run: uv run --locked sphinx-build -W --keep-going -b html docs/source docs/build/html
31
31
  - uses: actions/configure-pages@v6
@@ -16,9 +16,9 @@ jobs:
16
16
  with:
17
17
  persist-credentials: false
18
18
  fetch-depth: 0
19
- - uses: astral-sh/setup-uv@v10.2.0
19
+ - uses: astral-sh/setup-uv@v10.3.2
20
20
  with:
21
- version: 0.12.19
21
+ version: 0.13.0
22
22
  python-version: '3.13'
23
23
  - run: uv run --locked pytest
24
24
  - run: uv run --locked prek run --all-files
@@ -1,6 +1,6 @@
1
1
  FROM python:3.13-slim AS build
2
2
  LABEL org.opencontainers.image.source="https://github.com/adamtheturtle/hackerrank-cli"
3
- COPY --from=ghcr.io/astral-sh/uv:0.12.19 /uv /bin/uv
3
+ COPY --from=ghcr.io/astral-sh/uv:0.13.0 /uv /bin/uv
4
4
  WORKDIR /build
5
5
  COPY . .
6
6
  ARG HACKERRANK_CLI_VERSION=0.0.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hackerrank-cli
3
- Version: 2026.10.8
3
+ Version: 2026.10.30
4
4
  Summary: Upload starter files to existing HackerRank project questions.
5
5
  Author-email: Adam Dangoor <adamdangoor@gmail.com>
6
6
  License-Expression: MIT
@@ -14,11 +14,12 @@ Classifier: Operating System :: OS Independent
14
14
  Classifier: Programming Language :: Python :: 3 :: Only
15
15
  Classifier: Programming Language :: Python :: 3.13
16
16
  Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Programming Language :: Python :: 3.15
17
18
  Requires-Python: >=3.13
18
19
  Description-Content-Type: text/x-rst
19
20
  License-File: LICENSE
20
21
  Requires-Dist: click<8.6,>=8.3
21
- Requires-Dist: hackerrank>=2026.9.16
22
+ Requires-Dist: hackerrank>=2026.10.30
22
23
  Requires-Dist: httpx>=0.28
23
24
  Requires-Dist: httpx2>=2.12
24
25
  Requires-Dist: pathspec>=1
@@ -168,3 +169,18 @@ There are no prebuilt artifacts yet.
168
169
 
169
170
  See ``docs/source/migration.rst`` for migrating an existing shell uploader and
170
171
  keeping repository-specific snippet preparation outside this CLI.
172
+
173
+ Project AI system prompts
174
+ -------------------------
175
+
176
+ Set an existing project question's AI assistant prompt from a UTF-8 file:
177
+
178
+ .. code-block:: console
179
+
180
+ $ hackerrank questions set-system-prompt 123456 --prompt-file prompt.txt
181
+
182
+ Use ``--dry-run`` to validate without credentials or network access.
183
+ Prompts may contain up to 15,000 characters.
184
+ Text is sent exactly as read, including whitespace and line endings.
185
+ An empty file explicitly clears the prompt.
186
+ The command reads the question first to preserve its other metadata.
@@ -142,3 +142,18 @@ There are no prebuilt artifacts yet.
142
142
 
143
143
  See ``docs/source/migration.rst`` for migrating an existing shell uploader and
144
144
  keeping repository-specific snippet preparation outside this CLI.
145
+
146
+ Project AI system prompts
147
+ -------------------------
148
+
149
+ Set an existing project question's AI assistant prompt from a UTF-8 file:
150
+
151
+ .. code-block:: console
152
+
153
+ $ hackerrank questions set-system-prompt 123456 --prompt-file prompt.txt
154
+
155
+ Use ``--dry-run`` to validate without credentials or network access.
156
+ Prompts may contain up to 15,000 characters.
157
+ Text is sent exactly as read, including whitespace and line endings.
158
+ An empty file explicitly clears the prompt.
159
+ The command reads the question first to preserve its other metadata.
@@ -0,0 +1 @@
1
+ 2026.10.30
@@ -0,0 +1,14 @@
1
+ # 2026.10.30 (2026-10-30)
2
+
3
+ ## Features
4
+
5
+ - Add `hackerrank questions set-system-prompt QUESTION_ID --prompt-file FILE`
6
+ for project AI prompts.
7
+ Validate UTF-8 text and the 15,000-character limit before network access,
8
+ preserve exact whitespace, and clear prompts with an empty file.
9
+ - Add support for Python 3.15.
10
+
11
+ ## Bug fixes
12
+
13
+ - Run validation workflows for pull requests targeting feature branches so
14
+ stacked changes receive the normal checks.
@@ -6,13 +6,13 @@ This standalone repository follows ``coderpad-cli`` main at
6
6
  It adapts the src layout, Click entry points, setuptools-scm versions, uv
7
7
  lockfile, strict checks, 100% branch coverage, Sphinx, Towncrier, and
8
8
  distribution workflows.
9
- The SDK baseline is the released ``hackerrank==2026.9.16`` in ``uv.lock``.
9
+ The SDK baseline is the released ``hackerrank==2026.10.30`` in ``uv.lock``.
10
10
  Python 3.13 or later is required by the SDK.
11
11
 
12
12
  The released SDK exposes ``questions.upload_project_zip``.
13
- Its newer ``upload_project_directory`` helper is not yet released.
14
13
  This CLI prepares filtered ZIPs with the standard library and uses the public
15
14
  released SDK for multipart serialization and all API communication.
15
+ Project AI prompt updates use the SDK's question read and update methods.
16
16
  It does not import private SDK helpers or install an unreleased Git dependency.
17
17
 
18
18
  .. code-block:: shell
@@ -30,9 +30,9 @@ Tests use the real SDK with its public ``Transport`` interface and default
30
30
  HTTPX transport.
31
31
  No SDK methods are replaced and no live questions are mutated.
32
32
  RESPX intercepts the HTTP boundary and fails closed for unregistered requests.
33
- Tests inspect the actual multipart body and ZIP bytes, assert the absence of
34
- metadata updates, preserve text, binary contents and executable bits, and
35
- exercise SDK retries with immutable archive bytes.
33
+ Upload tests inspect the actual multipart body and ZIP bytes, assert the
34
+ absence of metadata updates, preserve text, binary contents and executable
35
+ bits, and exercise SDK retries with immutable archive bytes.
36
36
 
37
37
  The SDK's checked-in Swagger specification does not describe the
38
38
  ``upload_project_zip`` endpoint.
@@ -18,9 +18,10 @@ classifiers = [
18
18
  "Programming Language :: Python :: 3 :: Only",
19
19
  "Programming Language :: Python :: 3.13",
20
20
  "Programming Language :: Python :: 3.14",
21
+ "Programming Language :: Python :: 3.15",
21
22
  ]
22
23
  dynamic = [ "version" ]
23
- dependencies = [ "click>=8.3,<8.6", "hackerrank>=2026.9.16", "httpx>=0.28", "httpx2>=2.12", "pathspec>=1" ]
24
+ dependencies = [ "click>=8.3,<8.6", "hackerrank>=2026.10.30", "httpx>=0.28", "httpx2>=2.12", "pathspec>=1" ]
24
25
  urls.Documentation = "https://adamtheturtle.github.io/hackerrank-cli/"
25
26
  urls.Issues = "https://github.com/adamtheturtle/hackerrank-cli/issues"
26
27
  urls.Source = "https://github.com/adamtheturtle/hackerrank-cli"
@@ -38,16 +39,16 @@ dev = [
38
39
  "furo==2025.12.19",
39
40
  "interrogate==1.7.0",
40
41
  "mypy==2.4.0",
41
- "mypy-pytest-parametrize==2026.10.5",
42
- "mypy-strict-kwargs==2026.8.25.1",
42
+ "mypy-pytest-parametrize==2026.10.5.1",
43
+ "mypy-strict-kwargs==2026.10.19",
43
44
  "myst-parser==5.1.0",
44
45
  "no-defaults==2026.9.1",
45
- "prek==0.5.4",
46
+ "prek==0.5.7",
46
47
  "pydocstringformatter==1.0.0",
47
- "pylint[spelling]==4.0.9",
48
- "pylint-per-file-ignores==3.2.1",
49
- "pyproject-fmt==2.29.4",
50
- "pyrefly==1.3.1",
48
+ "pylint[spelling]==4.1.3",
49
+ "pylint-per-file-ignores==4.0.0",
50
+ "pyproject-fmt==2.30.3",
51
+ "pyrefly==1.4.0",
51
52
  "pyright==1.1.414",
52
53
  "pyroma==5.0.1",
53
54
  "pytest==9.1.1",
@@ -56,24 +57,24 @@ dev = [
56
57
  "pytest-regressions==2.11.0",
57
58
  "pytest-xdist==3.8.0",
58
59
  "respx==0.23.1",
59
- "ruff==0.16.9",
60
+ "ruff==0.17.2",
60
61
  "setuptools>=77",
61
62
  "setuptools-scm>=8.1",
62
63
  "shellcheck-py==0.11.0.1",
63
64
  "shfmt-py==4.2.0",
64
- "snapper-fmt==0.11.7",
65
+ "snapper-fmt==0.11.10",
65
66
  "sphinx>=8.1.3",
66
67
  "sphinx-click==6.2",
67
68
  "sphinx-lint==1.0.2",
68
69
  "sphinxcontrib-spelling==8.0.2",
69
- "strict-kwargs==2026.8.28.post2",
70
+ "strict-kwargs==2026.10.9",
70
71
  "towncrier==26.9",
71
72
  "twine==7.0.0",
72
- "ty==0.0.84",
73
- "vale==3.22.0.0",
73
+ "ty==0.0.92",
74
+ "vale==3.25.1.0",
74
75
  "vulture==2.16",
75
76
  "yamlfix==1.19.1",
76
- "zizmor==1.30.1",
77
+ "zizmor==1.31.1",
77
78
  ]
78
79
  binary = [ "pyinstaller>=6.12" ]
79
80
 
@@ -87,7 +88,8 @@ version_file = "src/hackerrank_cli/_version.py"
87
88
  version_scheme = "post-release"
88
89
 
89
90
  [tool.uv]
90
- required-version = "==0.12.19"
91
+ # Dependabot currently ships uv 0.12.18; CI pins uv separately.
92
+ required-version = ">=0.12.18"
91
93
 
92
94
  [tool.ruff]
93
95
  line-length = 79
@@ -191,7 +193,7 @@ ignore_names = [
191
193
 
192
194
  [tool.pyproject-fmt]
193
195
  keep_full_version = true
194
- max_supported_python = "3.14"
196
+ max_supported_python = "3.15"
195
197
 
196
198
  [tool.mypy]
197
199
  files = [ "." ]
@@ -201,6 +203,7 @@ follow_untyped_imports = true
201
203
  python_version = "3.13"
202
204
  strict = true
203
205
  plugins = [ "mypy_pytest_parametrize", "mypy_strict_kwargs" ]
206
+ show_traceback = true
204
207
  num_workers = "auto"
205
208
 
206
209
  [tool.pyrefly]
@@ -33,7 +33,7 @@ def create_cli(*, transport: Transport | None = None) -> click.Group:
33
33
 
34
34
  @cli.group()
35
35
  def questions() -> None:
36
- """Manage project-question starter files."""
36
+ """Manage project-question starter files and AI prompts."""
37
37
 
38
38
  @click.argument("question_id")
39
39
  @click.option(
@@ -131,11 +131,133 @@ def create_cli(*, transport: Transport | None = None) -> click.Group:
131
131
  with click.open_file(filename=output, mode="xb") as destination:
132
132
  _ = destination.write(contents)
133
133
 
134
+ @click.argument("question_id")
135
+ @click.option(
136
+ "--prompt-file",
137
+ type=click.Path(
138
+ path_type=Path, exists=True, dir_okay=False, readable=True
139
+ ),
140
+ required=True,
141
+ help=(
142
+ "UTF-8 system prompt, up to 15,000 characters. "
143
+ "An empty file clears it."
144
+ ),
145
+ )
146
+ @click.option(
147
+ "--dry-run",
148
+ is_flag=True,
149
+ help="Validate the prompt without credentials or network access.",
150
+ )
151
+ def set_system_prompt(
152
+ question_id: str, prompt_file: Path, *, dry_run: bool
153
+ ) -> None:
154
+ """Set the AI system prompt on an existing project question."""
155
+ _set_system_prompt(
156
+ question_id=question_id,
157
+ prompt_file=prompt_file,
158
+ dry_run=dry_run,
159
+ transport=transport,
160
+ )
161
+
162
+ _ = questions.command()(set_system_prompt)
134
163
  _ = questions.command()(archive)
135
164
  _ = questions.command()(upload)
136
165
  return cli
137
166
 
138
167
 
168
+ _MAX_PROMPT_LENGTH = 15_000
169
+
170
+
171
+ def _set_system_prompt(
172
+ question_id: str,
173
+ prompt_file: Path,
174
+ *,
175
+ dry_run: bool,
176
+ transport: Transport | None,
177
+ ) -> None:
178
+ """Validate exact prompt bytes and preserve question metadata."""
179
+ if (
180
+ not question_id.isascii()
181
+ or not question_id.isdecimal()
182
+ or int(question_id) < 1
183
+ ):
184
+ msg = "QUESTION_ID must be a positive decimal integer."
185
+ raise click.BadParameter(message=msg, param_hint="QUESTION_ID")
186
+ try:
187
+ prompt = prompt_file.read_bytes().decode(encoding="utf-8")
188
+ except (OSError, UnicodeError):
189
+ msg = "Could not read --prompt-file as UTF-8."
190
+ raise click.ClickException(message=msg) from None
191
+ if len(prompt) > _MAX_PROMPT_LENGTH:
192
+ msg = "System prompt must be at most 15,000 characters."
193
+ raise click.BadParameter(message=msg, param_hint="--prompt-file")
194
+ if dry_run:
195
+ click.echo(
196
+ message=(
197
+ "Would set system prompt for HackerRank project "
198
+ f"question {question_id}"
199
+ )
200
+ )
201
+ return
202
+ api_key = _api_key()
203
+ try:
204
+ with HackerRank(api_key=api_key, transport=transport) as client:
205
+ _write_system_prompt(
206
+ client=client, question_id=question_id, prompt=prompt
207
+ )
208
+ except HackerRankError as error:
209
+ msg = (
210
+ "HackerRank rejected the prompt update "
211
+ f"(HTTP {error.status_code})."
212
+ )
213
+ raise click.ClickException(message=msg) from None
214
+ except (httpx.TransportError, httpx2.TransportError):
215
+ msg = "Could not reach HackerRank. Check your network connection."
216
+ raise click.ClickException(message=msg) from None
217
+ except (ValueError, TypeError):
218
+ msg = (
219
+ "HackerRank returned an invalid response. "
220
+ "Check the question before trying again."
221
+ )
222
+ raise click.ClickException(message=msg) from None
223
+ click.echo(
224
+ message=(
225
+ f"Set system prompt for HackerRank project question {question_id}"
226
+ )
227
+ )
228
+
229
+
230
+ def _write_system_prompt(
231
+ *, client: HackerRank, question_id: str, prompt: str
232
+ ) -> None:
233
+ """Read existing metadata before replacing a project's system
234
+ prompt.
235
+ """
236
+ current = client.questions.get(question_id=question_id)
237
+ if current.type != "fullstack":
238
+ msg = "System prompts are supported for project (fullstack) questions."
239
+ raise click.ClickException(message=msg)
240
+ _ = client.questions.update(
241
+ question_id=question_id,
242
+ name=current.name,
243
+ type=current.type,
244
+ internal_notes=""
245
+ if current.internal_notes is None
246
+ else current.internal_notes,
247
+ languages=[] if current.languages is None else current.languages,
248
+ problem_statement=""
249
+ if current.problem_statement is None
250
+ else current.problem_statement,
251
+ recommended_duration=0
252
+ if current.recommended_duration is None
253
+ else current.recommended_duration,
254
+ tags=[] if current.tags is None else current.tags,
255
+ options=[] if current.options is None else current.options,
256
+ answer=[] if current.answer is None else current.answer,
257
+ custom_system_prompt=prompt,
258
+ )
259
+
260
+
139
261
  def _validate_source(
140
262
  directory: Path | None,
141
263
  source_file: Path | None,
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '2026.10.8'
22
- __version_tuple__ = version_tuple = (2026, 10, 8)
21
+ __version__ = version = '2026.10.30'
22
+ __version_tuple__ = version_tuple = (2026, 10, 30)
23
23
 
24
- __commit_id__ = commit_id = 'gfb4c14cb0'
24
+ __commit_id__ = commit_id = 'gf7fd0dcbf'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hackerrank-cli
3
- Version: 2026.10.8
3
+ Version: 2026.10.30
4
4
  Summary: Upload starter files to existing HackerRank project questions.
5
5
  Author-email: Adam Dangoor <adamdangoor@gmail.com>
6
6
  License-Expression: MIT
@@ -14,11 +14,12 @@ Classifier: Operating System :: OS Independent
14
14
  Classifier: Programming Language :: Python :: 3 :: Only
15
15
  Classifier: Programming Language :: Python :: 3.13
16
16
  Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Programming Language :: Python :: 3.15
17
18
  Requires-Python: >=3.13
18
19
  Description-Content-Type: text/x-rst
19
20
  License-File: LICENSE
20
21
  Requires-Dist: click<8.6,>=8.3
21
- Requires-Dist: hackerrank>=2026.9.16
22
+ Requires-Dist: hackerrank>=2026.10.30
22
23
  Requires-Dist: httpx>=0.28
23
24
  Requires-Dist: httpx2>=2.12
24
25
  Requires-Dist: pathspec>=1
@@ -168,3 +169,18 @@ There are no prebuilt artifacts yet.
168
169
 
169
170
  See ``docs/source/migration.rst`` for migrating an existing shell uploader and
170
171
  keeping repository-specific snippet preparation outside this CLI.
172
+
173
+ Project AI system prompts
174
+ -------------------------
175
+
176
+ Set an existing project question's AI assistant prompt from a UTF-8 file:
177
+
178
+ .. code-block:: console
179
+
180
+ $ hackerrank questions set-system-prompt 123456 --prompt-file prompt.txt
181
+
182
+ Use ``--dry-run`` to validate without credentials or network access.
183
+ Prompts may contain up to 15,000 characters.
184
+ Text is sent exactly as read, including whitespace and line endings.
185
+ An empty file explicitly clears the prompt.
186
+ The command reads the question first to preserve its other metadata.
@@ -37,6 +37,7 @@ docs/source/macos-releases.rst
37
37
  docs/source/migration.rst
38
38
  docs/source/usage.rst
39
39
  docs/source/changelog/2026.10.1.1.md
40
+ docs/source/changelog/2026.10.30.md
40
41
  docs/source/changelog/2026.10.7.md
41
42
  docs/source/changelog/2026.10.8.md
42
43
  newsfragments/.gitkeep
@@ -62,7 +63,9 @@ tests/test_cli.py
62
63
  tests/test_help.py
63
64
  tests/test_http.py
64
65
  tests/test_sources.py
65
- tests/test_help/test_help_archive_.txt
66
+ tests/test_system_prompt.py
66
67
  tests/test_help/test_help_questions_.txt
67
- tests/test_help/test_help_root_.txt
68
- tests/test_help/test_help_upload_.txt
68
+ tests/test_help/test_help_questions_archive_.txt
69
+ tests/test_help/test_help_questions_set_system_prompt_.txt
70
+ tests/test_help/test_help_questions_upload_.txt
71
+ tests/test_help/test_help_root_.txt
@@ -1,5 +1,5 @@
1
1
  click<8.6,>=8.3
2
- hackerrank>=2026.9.16
2
+ hackerrank>=2026.10.30
3
3
  httpx>=0.28
4
4
  httpx2>=2.12
5
5
  pathspec>=1
@@ -25,6 +25,7 @@
25
25
  "docs/source/__init__.py",
26
26
  "docs/source/changelog.md",
27
27
  "docs/source/changelog/2026.10.1.1.md",
28
+ "docs/source/changelog/2026.10.30.md",
28
29
  "docs/source/changelog/2026.10.7.md",
29
30
  "docs/source/changelog/2026.10.8.md",
30
31
  "docs/source/cli.rst",
@@ -50,12 +51,14 @@
50
51
  "tests/test_archive.py",
51
52
  "tests/test_cli.py",
52
53
  "tests/test_help.py",
53
- "tests/test_help/test_help_archive_.txt",
54
54
  "tests/test_help/test_help_questions_.txt",
55
+ "tests/test_help/test_help_questions_archive_.txt",
56
+ "tests/test_help/test_help_questions_set_system_prompt_.txt",
57
+ "tests/test_help/test_help_questions_upload_.txt",
55
58
  "tests/test_help/test_help_root_.txt",
56
- "tests/test_help/test_help_upload_.txt",
57
59
  "tests/test_http.py",
58
60
  "tests/test_sources.py",
61
+ "tests/test_system_prompt.py",
59
62
  "uv.lock",
60
63
  "zizmor.yml"
61
64
  ]
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "2026.10.30",
3
+ "distance": 0,
4
+ "node": "gf7fd0dcbf9ec0aa31ff42a05193bd6765477f8da",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-10-30"
8
+ }
@@ -0,0 +1,11 @@
1
+ Usage: hackerrank questions [OPTIONS] COMMAND [ARGS]...
2
+
3
+ Manage project-question starter files and AI prompts.
4
+
5
+ Options:
6
+ --help Show this message and exit.
7
+
8
+ Commands:
9
+ archive Export a project ZIP without credentials or network...
10
+ set-system-prompt Set the AI system prompt on an existing project question.
11
+ upload Replace project starter files while preserving...
@@ -0,0 +1,9 @@
1
+ Usage: hackerrank questions set-system-prompt [OPTIONS] QUESTION_ID
2
+
3
+ Set the AI system prompt on an existing project question.
4
+
5
+ Options:
6
+ --prompt-file FILE UTF-8 system prompt, up to 15,000 characters. An empty
7
+ file clears it. [required]
8
+ --dry-run Validate the prompt without credentials or network access.
9
+ --help Show this message and exit.
@@ -7,4 +7,4 @@ Options:
7
7
  --help Show this message and exit.
8
8
 
9
9
  Commands:
10
- questions Manage project-question starter files.
10
+ questions Manage project-question starter files and AI prompts.