sk4n 0.1.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 (78) hide show
  1. sk4n-0.1.0/.github/workflows/ci.yml +120 -0
  2. sk4n-0.1.0/.github/workflows/release.yml +241 -0
  3. sk4n-0.1.0/.gitignore +232 -0
  4. sk4n-0.1.0/AGENT.md +97 -0
  5. sk4n-0.1.0/CHANGELOG.md +9 -0
  6. sk4n-0.1.0/LICENSE +21 -0
  7. sk4n-0.1.0/PKG-INFO +247 -0
  8. sk4n-0.1.0/README.md +220 -0
  9. sk4n-0.1.0/justfile +36 -0
  10. sk4n-0.1.0/pyproject.toml +83 -0
  11. sk4n-0.1.0/scripts/generate_skill_references.py +398 -0
  12. sk4n-0.1.0/scripts/validate_package.py +135 -0
  13. sk4n-0.1.0/src/sk4n/__init__.py +1 -0
  14. sk4n-0.1.0/src/sk4n/__main__.py +4 -0
  15. sk4n-0.1.0/src/sk4n/academic_calendar.py +116 -0
  16. sk4n-0.1.0/src/sk4n/canvas/README.md +266 -0
  17. sk4n-0.1.0/src/sk4n/canvas/__init__.py +4 -0
  18. sk4n-0.1.0/src/sk4n/canvas/__main__.py +4 -0
  19. sk4n-0.1.0/src/sk4n/canvas/assignments.py +1792 -0
  20. sk4n-0.1.0/src/sk4n/canvas/auth.py +182 -0
  21. sk4n-0.1.0/src/sk4n/canvas/cli.py +1016 -0
  22. sk4n-0.1.0/src/sk4n/canvas/client.py +696 -0
  23. sk4n-0.1.0/src/sk4n/canvas/content.py +191 -0
  24. sk4n-0.1.0/src/sk4n/canvas/fetcher.py +958 -0
  25. sk4n-0.1.0/src/sk4n/canvas/files.py +341 -0
  26. sk4n-0.1.0/src/sk4n/canvas/groups.py +54 -0
  27. sk4n-0.1.0/src/sk4n/canvas/models.py +360 -0
  28. sk4n-0.1.0/src/sk4n/canvas/modules.py +53 -0
  29. sk4n-0.1.0/src/sk4n/canvas/pages.py +112 -0
  30. sk4n-0.1.0/src/sk4n/canvas/people.py +54 -0
  31. sk4n-0.1.0/src/sk4n/canvas/quiz_content.py +632 -0
  32. sk4n-0.1.0/src/sk4n/canvas/syllabus.py +80 -0
  33. sk4n-0.1.0/src/sk4n/canvas/sync.py +592 -0
  34. sk4n-0.1.0/src/sk4n/canvas/topics.py +165 -0
  35. sk4n-0.1.0/src/sk4n/canvas/utils.py +368 -0
  36. sk4n-0.1.0/src/sk4n/cli.py +322 -0
  37. sk4n-0.1.0/src/sk4n/doctor.py +501 -0
  38. sk4n-0.1.0/src/sk4n/errors.py +30 -0
  39. sk4n-0.1.0/src/sk4n/nusmods/README.md +187 -0
  40. sk4n-0.1.0/src/sk4n/nusmods/__init__.py +5 -0
  41. sk4n-0.1.0/src/sk4n/nusmods/__main__.py +4 -0
  42. sk4n-0.1.0/src/sk4n/nusmods/cli.py +1247 -0
  43. sk4n-0.1.0/src/sk4n/nusmods/client.py +295 -0
  44. sk4n-0.1.0/src/sk4n/nusmods/schedule.py +840 -0
  45. sk4n-0.1.0/src/sk4n/paths.py +56 -0
  46. sk4n-0.1.0/src/sk4n/skill_install.py +601 -0
  47. sk4n-0.1.0/src/sk4n/skills/nus-canvas/SKILL.md +46 -0
  48. sk4n-0.1.0/src/sk4n/skills/nus-canvas/agents/openai.yaml +4 -0
  49. sk4n-0.1.0/src/sk4n/skills/nus-canvas/references/commands.md +254 -0
  50. sk4n-0.1.0/src/sk4n/skills/nus-canvas/references/low-level.md +43 -0
  51. sk4n-0.1.0/src/sk4n/skills/nus-talent-connect/SKILL.md +26 -0
  52. sk4n-0.1.0/src/sk4n/skills/nus-talent-connect/agents/openai.yaml +4 -0
  53. sk4n-0.1.0/src/sk4n/skills/nus-talent-connect/references/commands.md +280 -0
  54. sk4n-0.1.0/src/sk4n/skills/nus-talent-connect/references/low-level.md +43 -0
  55. sk4n-0.1.0/src/sk4n/skills/nusmods/SKILL.md +26 -0
  56. sk4n-0.1.0/src/sk4n/skills/nusmods/agents/openai.yaml +4 -0
  57. sk4n-0.1.0/src/sk4n/skills/nusmods/references/commands.md +248 -0
  58. sk4n-0.1.0/src/sk4n/talent_connect/README.md +291 -0
  59. sk4n-0.1.0/src/sk4n/talent_connect/__init__.py +6 -0
  60. sk4n-0.1.0/src/sk4n/talent_connect/__main__.py +4 -0
  61. sk4n-0.1.0/src/sk4n/talent_connect/auth.py +172 -0
  62. sk4n-0.1.0/src/sk4n/talent_connect/authenticated_client.py +659 -0
  63. sk4n-0.1.0/src/sk4n/talent_connect/cli.py +1228 -0
  64. sk4n-0.1.0/src/sk4n/talent_connect/client.py +349 -0
  65. sk4n-0.1.0/src/sk4n/talent_connect/storage.py +648 -0
  66. sk4n-0.1.0/src/sk4n/tools/__init__.py +11 -0
  67. sk4n-0.1.0/src/sk4n/tools/playwright_cli.py +144 -0
  68. sk4n-0.1.0/src/sk4n/tools/request.py +259 -0
  69. sk4n-0.1.0/src/sk4n/tools/shared.py +172 -0
  70. sk4n-0.1.0/tests/test_canvas.py +1054 -0
  71. sk4n-0.1.0/tests/test_management_cli.py +308 -0
  72. sk4n-0.1.0/tests/test_nusmods.py +506 -0
  73. sk4n-0.1.0/tests/test_paths.py +111 -0
  74. sk4n-0.1.0/tests/test_playwright_cli.py +156 -0
  75. sk4n-0.1.0/tests/test_skill_install.py +176 -0
  76. sk4n-0.1.0/tests/test_skill_references.py +37 -0
  77. sk4n-0.1.0/tests/test_talent_connect.py +518 -0
  78. sk4n-0.1.0/uv.lock +1139 -0
@@ -0,0 +1,120 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches:
7
+ - main
8
+ workflow_dispatch:
9
+ workflow_call:
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ concurrency:
15
+ group: ci-${{ github.workflow }}-${{ github.ref }}
16
+ cancel-in-progress: true
17
+
18
+ env:
19
+ UV_NO_PROGRESS: "1"
20
+
21
+ jobs:
22
+ tests:
23
+ name: Tests (${{ matrix.os }}, Python ${{ matrix.python-version }})
24
+ runs-on: ${{ matrix.os }}
25
+ timeout-minutes: 15
26
+ strategy:
27
+ fail-fast: true
28
+ matrix:
29
+ include:
30
+ - os: ubuntu-latest
31
+ python-version: "3.11"
32
+ - os: ubuntu-latest
33
+ python-version: "3.12"
34
+ - os: ubuntu-latest
35
+ python-version: "3.13"
36
+ - os: ubuntu-latest
37
+ python-version: "3.14"
38
+ - os: macos-latest
39
+ python-version: "3.14"
40
+ - os: windows-latest
41
+ python-version: "3.14"
42
+
43
+ steps:
44
+ - name: Check out the repository
45
+ uses: actions/checkout@v7
46
+ with:
47
+ persist-credentials: false
48
+
49
+ - name: Install uv and Python
50
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
51
+ with:
52
+ version: "0.11.17"
53
+ python-version: ${{ matrix.python-version }}
54
+ enable-cache: true
55
+
56
+ - name: Install the locked project and development dependencies
57
+ run: uv sync --locked --all-groups
58
+
59
+ - name: Run unit tests
60
+ run: uv run --locked pytest -q
61
+
62
+ source-checks:
63
+ name: Source checks
64
+ runs-on: ubuntu-latest
65
+ timeout-minutes: 15
66
+
67
+ steps:
68
+ - name: Check out the repository
69
+ uses: actions/checkout@v7
70
+ with:
71
+ persist-credentials: false
72
+
73
+ - name: Install uv and Python
74
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
75
+ with:
76
+ version: "0.11.17"
77
+ python-version: "3.14"
78
+ enable-cache: true
79
+
80
+ - name: Install the locked project and development dependencies
81
+ run: uv sync --locked --all-groups
82
+
83
+ - name: Run Ruff
84
+ run: uv run --locked ruff --config pyproject.toml check .
85
+
86
+ - name: Run Pyright
87
+ run: uv run --locked pyright
88
+
89
+ - name: Check generated CLI references
90
+ run: uv run --locked python scripts/generate_skill_references.py --check
91
+
92
+ package:
93
+ name: Package artifacts
94
+ runs-on: ubuntu-latest
95
+ timeout-minutes: 20
96
+
97
+ steps:
98
+ - name: Check out the repository
99
+ uses: actions/checkout@v7
100
+ with:
101
+ persist-credentials: false
102
+
103
+ - name: Install uv and the minimum supported Python
104
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
105
+ with:
106
+ version: "0.11.17"
107
+ python-version: "3.11"
108
+ enable-cache: true
109
+
110
+ - name: Install the locked project and development dependencies
111
+ run: uv sync --locked --all-groups
112
+
113
+ - name: Build wheel and source distribution
114
+ run: uv run --locked python -m build
115
+
116
+ - name: Check package metadata
117
+ run: uv run --locked twine check --strict dist/*
118
+
119
+ - name: Inspect and clean-install both artifacts with pip
120
+ run: uv run --locked python scripts/validate_package.py dist
@@ -0,0 +1,241 @@
1
+ name: Release
2
+
3
+ on:
4
+ release:
5
+ types:
6
+ - published
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ concurrency:
12
+ group: release-${{ github.event.release.tag_name }}
13
+ cancel-in-progress: false
14
+
15
+ env:
16
+ UV_NO_PROGRESS: "1"
17
+
18
+ jobs:
19
+ ci:
20
+ name: Run CI for the release commit
21
+ permissions:
22
+ contents: read
23
+ uses: $/.github/workflows/ci.yml
24
+
25
+ build:
26
+ name: Build and validate release artifacts
27
+ needs:
28
+ - ci
29
+ runs-on: ubuntu-latest
30
+ timeout-minutes: 25
31
+ outputs:
32
+ version: ${{ steps.package-version.outputs.version }}
33
+
34
+ steps:
35
+ - name: Check out the release tag
36
+ uses: actions/checkout@v7
37
+ with:
38
+ ref: ${{ github.event.release.tag_name }}
39
+ persist-credentials: false
40
+
41
+ - name: Install uv and Python
42
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
43
+ with:
44
+ version: "0.11.17"
45
+ python-version: "3.14"
46
+ enable-cache: true
47
+
48
+ - name: Install the locked project and development dependencies
49
+ run: uv sync --locked --all-groups
50
+
51
+ - name: Verify that the release tag matches the package version
52
+ id: package-version
53
+ env:
54
+ RELEASE_TAG: ${{ github.event.release.tag_name }}
55
+ run: |
56
+ package_version="$(uv run --locked python -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')"
57
+ if [[ "$RELEASE_TAG" != "v$package_version" ]]; then
58
+ echo "Release tag $RELEASE_TAG does not match package version $package_version." >&2
59
+ exit 1
60
+ fi
61
+ echo "version=$package_version" >> "$GITHUB_OUTPUT"
62
+
63
+ - name: Audit the locked environment
64
+ run: uv run --locked pip-audit --local --skip-editable --progress-spinner off
65
+
66
+ - name: Audit the allowed runtime dependency range
67
+ run: >-
68
+ uv run --locked pip-audit
69
+ --strict
70
+ --progress-spinner off
71
+ .
72
+
73
+ - name: Build wheel and source distribution
74
+ run: uv run --locked python -m build
75
+
76
+ - name: Check package metadata
77
+ run: uv run --locked twine check --strict dist/*
78
+
79
+ - name: Inspect and clean-install both artifacts with pip
80
+ run: uv run --locked python scripts/validate_package.py dist
81
+
82
+ - name: Store the exact release artifacts
83
+ uses: actions/upload-artifact@v7
84
+ with:
85
+ name: python-package-distributions
86
+ path: dist/
87
+ if-no-files-found: error
88
+ overwrite: true
89
+ retention-days: 90
90
+
91
+ github-release-assets:
92
+ name: Attach artifacts to the GitHub Release
93
+ needs:
94
+ - build
95
+ - publish-pypi
96
+ runs-on: ubuntu-latest
97
+ timeout-minutes: 10
98
+ permissions:
99
+ contents: write
100
+
101
+ steps:
102
+ - name: Download the release artifacts
103
+ uses: actions/download-artifact@v8
104
+ with:
105
+ name: python-package-distributions
106
+ path: dist/
107
+
108
+ - name: Attach the artifacts
109
+ env:
110
+ GH_TOKEN: ${{ github.token }}
111
+ RELEASE_TAG: ${{ github.event.release.tag_name }}
112
+ run: gh release upload "$RELEASE_TAG" dist/* --clobber --repo "$GITHUB_REPOSITORY"
113
+
114
+ publish-testpypi:
115
+ name: Publish to TestPyPI
116
+ needs:
117
+ - build
118
+ runs-on: ubuntu-latest
119
+ timeout-minutes: 10
120
+ environment:
121
+ name: testpypi
122
+ url: https://test.pypi.org/p/sk4n
123
+ permissions:
124
+ id-token: write
125
+
126
+ steps:
127
+ - name: Download the release artifacts
128
+ uses: actions/download-artifact@v8
129
+ with:
130
+ name: python-package-distributions
131
+ path: dist/
132
+
133
+ - name: Publish the artifacts to TestPyPI
134
+ uses: pypa/gh-action-pypi-publish@v1.14.2
135
+ with:
136
+ repository-url: https://test.pypi.org/legacy/
137
+ skip-existing: true
138
+ print-hash: true
139
+ attestations: true
140
+
141
+ test-testpypi:
142
+ name: Test TestPyPI (${{ matrix.os }})
143
+ needs:
144
+ - build
145
+ - publish-testpypi
146
+ runs-on: ${{ matrix.os }}
147
+ timeout-minutes: 15
148
+ permissions:
149
+ contents: read
150
+ strategy:
151
+ fail-fast: false
152
+ matrix:
153
+ os:
154
+ - ubuntu-latest
155
+ - macos-latest
156
+ - windows-latest
157
+
158
+ steps:
159
+ - name: Set up Python
160
+ uses: actions/setup-python@v7
161
+ with:
162
+ python-version: "3.14"
163
+
164
+ - name: Download the original release artifacts
165
+ uses: actions/download-artifact@v8
166
+ with:
167
+ name: python-package-distributions
168
+ path: release-dist/
169
+
170
+ - name: Download the published wheel from TestPyPI
171
+ shell: bash
172
+ env:
173
+ PACKAGE_VERSION: ${{ needs.build.outputs.version }}
174
+ run: |
175
+ for attempt in 1 2 3 4 5 6; do
176
+ if python -m pip download \
177
+ --disable-pip-version-check \
178
+ --no-cache-dir \
179
+ --no-deps \
180
+ --index-url https://test.pypi.org/simple/ \
181
+ --dest test-dist \
182
+ "sk4n==$PACKAGE_VERSION"; then
183
+ break
184
+ fi
185
+ if [[ "$attempt" == "6" ]]; then
186
+ exit 1
187
+ fi
188
+ sleep 10
189
+ done
190
+
191
+ - name: Verify that TestPyPI served the original wheel
192
+ run: >-
193
+ python -c
194
+ "from pathlib import Path;
195
+ original=list(Path('release-dist').glob('sk4n-*.whl'));
196
+ published=list(Path('test-dist').glob('sk4n-*.whl'));
197
+ assert len(original) == len(published) == 1;
198
+ assert original[0].read_bytes() == published[0].read_bytes()"
199
+
200
+ - name: Install the TestPyPI wheel with dependencies from PyPI
201
+ shell: bash
202
+ run: python -m pip install --disable-pip-version-check test-dist/sk4n-*.whl
203
+
204
+ - name: Exercise the installed package and console scripts
205
+ shell: bash
206
+ env:
207
+ PACKAGE_VERSION: ${{ needs.build.outputs.version }}
208
+ SK4N_HOME: ${{ runner.temp }}/sk4n-release-smoke
209
+ run: |
210
+ python -c 'import os; from importlib.metadata import version; assert version("sk4n") == os.environ["PACKAGE_VERSION"]'
211
+ sk4n --help
212
+ canvas --help
213
+ nusmods --help
214
+ talent-connect --help
215
+
216
+ publish-pypi:
217
+ name: Publish to PyPI
218
+ needs:
219
+ - build
220
+ - test-testpypi
221
+ runs-on: ubuntu-latest
222
+ timeout-minutes: 10
223
+ environment:
224
+ name: pypi
225
+ url: https://pypi.org/p/sk4n
226
+ permissions:
227
+ id-token: write
228
+
229
+ steps:
230
+ - name: Download the original release artifacts
231
+ uses: actions/download-artifact@v8
232
+ with:
233
+ name: python-package-distributions
234
+ path: dist/
235
+
236
+ - name: Publish the artifacts to PyPI
237
+ uses: pypa/gh-action-pypi-publish@v1.14.2
238
+ with:
239
+ skip-existing: true
240
+ print-hash: true
241
+ attestations: true
sk4n-0.1.0/.gitignore ADDED
@@ -0,0 +1,232 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ # Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ # poetry.lock
109
+ # poetry.toml
110
+
111
+ # pdm
112
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
113
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
114
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
115
+ # pdm.lock
116
+ # pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # pixi
121
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
122
+ # pixi.lock
123
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
124
+ # in the .venv directory. It is recommended not to include this directory in version control.
125
+ .pixi
126
+
127
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
128
+ __pypackages__/
129
+
130
+ # Celery stuff
131
+ celerybeat-schedule
132
+ celerybeat.pid
133
+
134
+ # Redis
135
+ *.rdb
136
+ *.aof
137
+ *.pid
138
+
139
+ # RabbitMQ
140
+ mnesia/
141
+ rabbitmq/
142
+ rabbitmq-data/
143
+
144
+ # ActiveMQ
145
+ activemq-data/
146
+
147
+ # SageMath parsed files
148
+ *.sage.py
149
+
150
+ # Environments
151
+ .env
152
+ .envrc
153
+ .venv
154
+ env/
155
+ venv/
156
+ ENV/
157
+ env.bak/
158
+ venv.bak/
159
+
160
+ # Spyder project settings
161
+ .spyderproject
162
+ .spyproject
163
+
164
+ # Rope project settings
165
+ .ropeproject
166
+
167
+ # mkdocs documentation
168
+ /site
169
+
170
+ # mypy
171
+ .mypy_cache/
172
+ .dmypy.json
173
+ dmypy.json
174
+
175
+ # Pyre type checker
176
+ .pyre/
177
+
178
+ # pytype static type analyzer
179
+ .pytype/
180
+
181
+ # Cython debug symbols
182
+ cython_debug/
183
+
184
+ # PyCharm
185
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
186
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
187
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
188
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
189
+ # .idea/
190
+
191
+ # Abstra
192
+ # Abstra is an AI-powered process automation framework.
193
+ # Ignore directories containing user credentials, local state, and settings.
194
+ # Learn more at https://abstra.io/docs
195
+ .abstra/
196
+
197
+ # Visual Studio Code
198
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
199
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
200
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
201
+ # you could uncomment the following to ignore the entire vscode folder
202
+ # .vscode/
203
+ # Temporary file for partial code execution
204
+ tempCodeRunnerFile.py
205
+
206
+ # Ruff stuff:
207
+ .ruff_cache/
208
+
209
+ # PyPI configuration file
210
+ .pypirc
211
+
212
+ # Marimo
213
+ marimo/_static/
214
+ marimo/_lsp/
215
+ __marimo__/
216
+
217
+ # Streamlit
218
+ .streamlit/secrets.toml
219
+
220
+ temp/
221
+ sessions/
222
+ data/
223
+ .DS_Store
224
+ ._*
225
+ desktop.ini
226
+ Thumbs.db
227
+
228
+ .playwright/
229
+ .playwright-cli/
230
+ .claude/
231
+ .agents/
232
+ .agent/
sk4n-0.1.0/AGENT.md ADDED
@@ -0,0 +1,97 @@
1
+ # Maintainer agent guide
2
+
3
+ This file is guidance for agents maintaining this repository. It is not the
4
+ operating prompt for an end-user-facing NUS assistant.
5
+
6
+ nus-canvas, nus-talent-connect and nusmods skills may have been installed to you, those are for end-user-facing NUS assistant.
7
+
8
+ ## Repository map
9
+
10
+ - `src/sk4n/canvas/` provides authenticated, read-only Canvas queries, targeted cache
11
+ refreshes, bulk synchronization, direct authenticated API
12
+ access, and authenticated `@playwright/cli` sessions.
13
+ - `src/sk4n/talent_connect/` provides public and authenticated Kinobi job/company
14
+ access, local SQLite persistence, direct authenticated API access, and
15
+ authenticated `@playwright/cli` sessions.
16
+ - `src/sk4n/nusmods/` uses public NUSMods APIs and does not need an authentication flow.
17
+ - `src/sk4n/tools/` contains shared request, saved-session, and browser-session helpers.
18
+ - `tests/` contains the unit suite. Keep application-specific behavior in its
19
+ package and put genuinely shared behavior in `src/sk4n/tools/`.
20
+
21
+ For check / build / test, refer to the justfile under repo root.
22
+ Prefer running the just commands like `just check` as is.
23
+
24
+ ## Authentication comes first
25
+
26
+ Before any Canvas or TalentConnect task that may contact the real service,
27
+ always check authentication status first:
28
+
29
+ ```bash
30
+ uv run canvas auth status
31
+ uv run talent-connect auth status
32
+ ```
33
+
34
+ If the relevant command reports unauthenticated and the task needs actual
35
+ access, ask the user for approval to start authentication. Once approved, run
36
+ the appropriate command below and ask the user to complete NUS SSO in the
37
+ browser window:
38
+
39
+ ```bash
40
+ uv run canvas auth login
41
+ uv run talent-connect auth login
42
+ ```
43
+
44
+ Do not require authentication for NUSMods: its APIs are public, so use direct
45
+ requests or available browser skills as appropriate.
46
+
47
+ Prefer the repository's direct authenticated `api` command for endpoint
48
+ discovery and validation. It reuses the durable saved login and has lower cost
49
+ and overhead than a built-in browser/app connector. When page-level inspection
50
+ is necessary, use the repository's `playwright-cli` command; it injects that
51
+ same saved authentication into a named `@playwright/cli` session that later CLI
52
+ commands can reuse. Avoid built-in app/browser connectors for Canvas and
53
+ TalentConnect maintenance because their authentication is not persisted for
54
+ subsequent repository commands and they add unnecessary overhead.
55
+
56
+ ```bash
57
+ uv run canvas api /api/v1/users/self/profile
58
+ uv run canvas playwright-cli --session canvas
59
+
60
+ uv run talent-connect api /api/auth/
61
+ uv run talent-connect playwright-cli --session talent-connect
62
+ ```
63
+
64
+ The browser-opening commands leave their sessions running. Continue with, for
65
+ example, `playwright-cli -s=canvas snapshot`, and close a session explicitly
66
+ when the task is finished.
67
+
68
+ ## Updating a remote integration
69
+
70
+ Remote interfaces change. When adding support for a new or updated Canvas or
71
+ TalentConnect page, field, endpoint, filter, or workflow:
72
+
73
+ 1. Check auth status before beginning remote investigation.
74
+ 2. Inspect the smallest useful read-only API call first with the app's `api`
75
+ command. Use the authenticated `playwright-cli` session for network, DOM, or
76
+ interaction details that the direct API cannot reveal.
77
+ 3. Record the observed request/response contract and preserve existing unknown
78
+ fields unless there is a reason to normalize them.
79
+ 4. Implement the narrowest package-local change, sharing only infrastructure
80
+ that truly applies to multiple apps.
81
+ 5. Add or update deterministic unit tests, then run the focused tests and the
82
+ full unit suite.
83
+ 6. Follow unit tests with safe real-service end-to-end checks when remote
84
+ behavior is relevant. Read-only checks such as auth status, profile fetches,
85
+ small list/detail requests, and page inspection are allowed and encouraged
86
+ by default unless the user says otherwise.
87
+
88
+ Real read-only API calls and actual use of the repository's `playwright-cli`
89
+ commands are allowed by default and are required for many remote-integration
90
+ tasks; mocks alone are not proof that a changed remote interface still works.
91
+
92
+ Any operation that would exercise real write access on the user's behalf needs
93
+ the user's explicit approval first. This includes creating, editing, deleting,
94
+ submitting, applying, bookmarking, messaging, enrolling, or otherwise changing
95
+ remote state. Approval for read-only investigation does not imply approval for
96
+ writes. Prefer a read-only status/detail request as the end-to-end smoke test
97
+ whenever it can validate the implementation safely.
@@ -0,0 +1,9 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Initial public release.
6
+ - Provide Canvas, NUSMods, TalentConnect, and shared `sk4n` command-line tools.
7
+ - Bundle agent skills for the supported NUS services. Provide cli commands to skill management
8
+
9
+ [0.1.0]: https://github.com/hykzr/sk4n/releases/tag/v0.1.0