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.
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.github/dependabot.yml +2 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.github/workflows/autofix.yml +3 -4
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.github/workflows/binaries.yml +12 -4
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.github/workflows/ci.yml +5 -5
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.github/workflows/docs.yml +2 -2
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.github/workflows/lint.yml +2 -2
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.github/workflows/publish-site.yml +2 -2
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.github/workflows/release.yml +2 -2
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/Dockerfile +1 -1
- {hackerrank_cli-2026.10.8/src/hackerrank_cli.egg-info → hackerrank_cli-2026.10.30}/PKG-INFO +18 -2
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/README.rst +15 -0
- hackerrank_cli-2026.10.30/VERSION +1 -0
- hackerrank_cli-2026.10.30/docs/source/changelog/2026.10.30.md +14 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/source/development.rst +5 -5
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/pyproject.toml +19 -16
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli/__init__.py +123 -1
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli/_version.py +3 -3
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30/src/hackerrank_cli.egg-info}/PKG-INFO +18 -2
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli.egg-info/SOURCES.txt +6 -3
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli.egg-info/requires.txt +1 -1
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli.egg-info/scm_file_list.json +5 -2
- hackerrank_cli-2026.10.30/src/hackerrank_cli.egg-info/scm_version.json +8 -0
- hackerrank_cli-2026.10.30/tests/test_help/test_help_questions_.txt +11 -0
- hackerrank_cli-2026.10.30/tests/test_help/test_help_questions_set_system_prompt_.txt +9 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/tests/test_help/test_help_root_.txt +1 -1
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/tests/test_help.py +2 -1
- hackerrank_cli-2026.10.30/tests/test_system_prompt.py +266 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/uv.lock +644 -523
- hackerrank_cli-2026.10.8/VERSION +0 -1
- hackerrank_cli-2026.10.8/src/hackerrank_cli.egg-info/scm_version.json +0 -8
- hackerrank_cli-2026.10.8/tests/test_help/test_help_questions_.txt +0 -10
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.dockerignore +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.gitattributes +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.github/workflows/dependabot-merge.yml +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.github/workflows/nix.yml +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.gitignore +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/.vale.ini +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/LICENSE +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/MANIFEST.in +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/bin/__init__.py +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/bin/hackerrank_wrapper.py +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/__init__.py +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/source/__init__.py +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/source/changelog/2026.10.1.1.md +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/source/changelog/2026.10.7.md +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/source/changelog/2026.10.8.md +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/source/changelog.md +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/source/cli.rst +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/source/conf.py +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/source/index.rst +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/source/macos-releases.rst +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/source/migration.rst +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/docs/source/usage.rst +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/flake.lock +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/flake.nix +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/newsfragments/.gitkeep +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/prek.toml +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/setup.cfg +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/spelling_private_dict.txt +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli/__main__.py +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli/_sources.py +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli/py.typed +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli.egg-info/dependency_links.txt +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli.egg-info/entry_points.txt +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli.egg-info/not-zip-safe +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli.egg-info/top_level.txt +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/styles/snapper/LongProseLine.yml +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/styles/snapper/SemanticLineBreaks.yml +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/tests/__init__.py +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/tests/test_archive.py +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/tests/test_cli.py +0 -0
- /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
- /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
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/tests/test_http.py +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/tests/test_sources.py +0 -0
- {hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/zizmor.yml +0 -0
|
@@ -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
|
|
20
|
+
- uses: astral-sh/setup-uv@v10.3.2
|
|
22
21
|
with:
|
|
23
|
-
version: 0.
|
|
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
|
|
50
|
+
- uses: astral-sh/setup-uv@v10.3.2
|
|
51
51
|
with:
|
|
52
|
-
version: 0.
|
|
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@
|
|
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
|
-
|
|
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
|
|
27
|
+
- uses: astral-sh/setup-uv@v10.3.2
|
|
28
28
|
with:
|
|
29
|
-
version: 0.
|
|
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
|
|
40
|
+
- uses: astral-sh/setup-uv@v10.3.2
|
|
41
41
|
with:
|
|
42
|
-
version: 0.
|
|
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
|
|
22
|
+
- uses: astral-sh/setup-uv@v10.3.2
|
|
23
23
|
with:
|
|
24
|
-
version: 0.
|
|
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
|
|
30
|
+
- uses: astral-sh/setup-uv@v10.3.2
|
|
31
31
|
with:
|
|
32
|
-
version: 0.
|
|
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
|
|
26
|
+
- uses: astral-sh/setup-uv@v10.3.2
|
|
27
27
|
with:
|
|
28
|
-
version: 0.
|
|
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
|
|
19
|
+
- uses: astral-sh/setup-uv@v10.3.2
|
|
20
20
|
with:
|
|
21
|
-
version: 0.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
+
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
|
-
|
|
34
|
-
metadata updates, preserve text, binary contents and executable
|
|
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.
|
|
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.
|
|
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.
|
|
46
|
+
"prek==0.5.7",
|
|
46
47
|
"pydocstringformatter==1.0.0",
|
|
47
|
-
"pylint[spelling]==4.
|
|
48
|
-
"pylint-per-file-ignores==
|
|
49
|
-
"pyproject-fmt==2.
|
|
50
|
-
"pyrefly==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.
|
|
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.
|
|
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.
|
|
70
|
+
"strict-kwargs==2026.10.9",
|
|
70
71
|
"towncrier==26.9",
|
|
71
72
|
"twine==7.0.0",
|
|
72
|
-
"ty==0.0.
|
|
73
|
-
"vale==3.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
22
|
-
__version_tuple__ = version_tuple = (2026, 10,
|
|
21
|
+
__version__ = version = '2026.10.30'
|
|
22
|
+
__version_tuple__ = version_tuple = (2026, 10, 30)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
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.
|
|
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.
|
|
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.
|
{hackerrank_cli-2026.10.8 → hackerrank_cli-2026.10.30}/src/hackerrank_cli.egg-info/SOURCES.txt
RENAMED
|
@@ -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/
|
|
66
|
+
tests/test_system_prompt.py
|
|
66
67
|
tests/test_help/test_help_questions_.txt
|
|
67
|
-
tests/test_help/
|
|
68
|
-
tests/test_help/
|
|
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
|
|
@@ -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,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.
|