canisend 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. canisend-0.2.0/.codex-plugin/plugin.json +33 -0
  2. canisend-0.2.0/.env.example +6 -0
  3. canisend-0.2.0/.github/workflows/ci.yml +45 -0
  4. canisend-0.2.0/.github/workflows/release.yml +198 -0
  5. canisend-0.2.0/.gitignore +16 -0
  6. canisend-0.2.0/CHANGELOG.md +15 -0
  7. canisend-0.2.0/LICENSE +21 -0
  8. canisend-0.2.0/PKG-INFO +514 -0
  9. canisend-0.2.0/README.md +481 -0
  10. canisend-0.2.0/RELEASE.md +120 -0
  11. canisend-0.2.0/agent-skills/canisend/SKILL.md +69 -0
  12. canisend-0.2.0/agent-skills/canisend/agents/openai.yaml +4 -0
  13. canisend-0.2.0/agent-skills/canisend/references/agent-orchestration.md +114 -0
  14. canisend-0.2.0/agent-skills/canisend/references/file-contracts.md +103 -0
  15. canisend-0.2.0/agent-skills/canisend/references/job-lifecycle.md +63 -0
  16. canisend-0.2.0/agent-skills/canisend/references/platforms.md +66 -0
  17. canisend-0.2.0/agent-skills/canisend/references/privacy.md +93 -0
  18. canisend-0.2.0/agent-skills/canisend/references/provider-config.md +74 -0
  19. canisend-0.2.0/agent-skills/canisend/references/quality-gates.md +71 -0
  20. canisend-0.2.0/agent-skills/canisend/references/typst-profile.md +69 -0
  21. canisend-0.2.0/agent-skills/canisend/references/workflow.md +131 -0
  22. canisend-0.2.0/assets/canisend-logo.svg +27 -0
  23. canisend-0.2.0/canisend_v1_proposal.md +1143 -0
  24. canisend-0.2.0/docs/superpowers/plans/2026-06-16-canisend-hardening.md +605 -0
  25. canisend-0.2.0/docs/superpowers/plans/2026-06-19-framework-hardening-plan.md +156 -0
  26. canisend-0.2.0/docs/superpowers/plans/2026-06-22-orchestrator-typst-intake-implementation.md +1538 -0
  27. canisend-0.2.0/docs/superpowers/specs/2026-06-17-application-package-skills-and-job-feed-design.md +224 -0
  28. canisend-0.2.0/docs/superpowers/specs/2026-06-22-local-orchestrator-and-typst-direct-editing-design.md +398 -0
  29. canisend-0.2.0/docs/superpowers/specs/2026-06-22-strict-hr-review-and-job-intake-design.md +217 -0
  30. canisend-0.2.0/examples/end_to_end/README.md +80 -0
  31. canisend-0.2.0/examples/end_to_end/fake_llm_provider.py +128 -0
  32. canisend-0.2.0/examples/end_to_end/full_job_advert.md +19 -0
  33. canisend-0.2.0/examples/end_to_end/jobs_ac_uk_sample.xml +18 -0
  34. canisend-0.2.0/examples/end_to_end/profile/generated/.gitkeep +1 -0
  35. canisend-0.2.0/examples/end_to_end/profile/profile.yaml +13 -0
  36. canisend-0.2.0/examples/end_to_end/profile/typst/cover_letter_base.typ +19 -0
  37. canisend-0.2.0/examples/end_to_end/profile/typst/cv.typ +18 -0
  38. canisend-0.2.0/examples/end_to_end/profile/typst/research_statement.typ +13 -0
  39. canisend-0.2.0/examples/end_to_end/profile/typst/teaching_statement.typ +13 -0
  40. canisend-0.2.0/jobs/.gitkeep +1 -0
  41. canisend-0.2.0/platform-bridges/AGENTS.md +32 -0
  42. canisend-0.2.0/platform-bridges/CLAUDE.md +25 -0
  43. canisend-0.2.0/profile/.gitkeep +1 -0
  44. canisend-0.2.0/prompts/cover_letter_writer.md +45 -0
  45. canisend-0.2.0/prompts/criteria_checker.md +44 -0
  46. canisend-0.2.0/prompts/cv_tailor.md +44 -0
  47. canisend-0.2.0/prompts/job_parser.md +38 -0
  48. canisend-0.2.0/prompts/package_builder.md +33 -0
  49. canisend-0.2.0/prompts/profile_evidence_augmenter.md +46 -0
  50. canisend-0.2.0/prompts/profile_matcher.md +37 -0
  51. canisend-0.2.0/pyproject.toml +78 -0
  52. canisend-0.2.0/schemas/criteria_check.schema.json +23 -0
  53. canisend-0.2.0/schemas/fit_report.schema.json +13 -0
  54. canisend-0.2.0/schemas/parsed_job.schema.json +51 -0
  55. canisend-0.2.0/scripts/release.sh +316 -0
  56. canisend-0.2.0/skills/canisend/SKILL.md +69 -0
  57. canisend-0.2.0/skills/canisend/agents/openai.yaml +4 -0
  58. canisend-0.2.0/skills/canisend/references/agent-orchestration.md +114 -0
  59. canisend-0.2.0/skills/canisend/references/file-contracts.md +103 -0
  60. canisend-0.2.0/skills/canisend/references/job-lifecycle.md +63 -0
  61. canisend-0.2.0/skills/canisend/references/platforms.md +66 -0
  62. canisend-0.2.0/skills/canisend/references/privacy.md +93 -0
  63. canisend-0.2.0/skills/canisend/references/provider-config.md +74 -0
  64. canisend-0.2.0/skills/canisend/references/quality-gates.md +71 -0
  65. canisend-0.2.0/skills/canisend/references/typst-profile.md +69 -0
  66. canisend-0.2.0/skills/canisend/references/workflow.md +131 -0
  67. canisend-0.2.0/skills/canisend-cover-letter/SKILL.md +33 -0
  68. canisend-0.2.0/skills/canisend-cover-letter/agents/openai.yaml +4 -0
  69. canisend-0.2.0/skills/canisend-criteria-check/SKILL.md +33 -0
  70. canisend-0.2.0/skills/canisend-criteria-check/agents/openai.yaml +4 -0
  71. canisend-0.2.0/skills/canisend-cv-tailoring/SKILL.md +33 -0
  72. canisend-0.2.0/skills/canisend-cv-tailoring/agents/openai.yaml +4 -0
  73. canisend-0.2.0/skills/canisend-material-review/SKILL.md +35 -0
  74. canisend-0.2.0/skills/canisend-material-review/agents/openai.yaml +4 -0
  75. canisend-0.2.0/skills/canisend-research-statement/SKILL.md +33 -0
  76. canisend-0.2.0/skills/canisend-research-statement/agents/openai.yaml +4 -0
  77. canisend-0.2.0/src/canisend/__init__.py +3 -0
  78. canisend-0.2.0/src/canisend/cli.py +677 -0
  79. canisend-0.2.0/src/canisend/config_schema.py +56 -0
  80. canisend-0.2.0/src/canisend/evidence.py +543 -0
  81. canisend-0.2.0/src/canisend/examples.py +112 -0
  82. canisend-0.2.0/src/canisend/git_tracking.py +53 -0
  83. canisend-0.2.0/src/canisend/job_import.py +228 -0
  84. canisend-0.2.0/src/canisend/jobs.py +226 -0
  85. canisend-0.2.0/src/canisend/llm.py +107 -0
  86. canisend-0.2.0/src/canisend/match.py +410 -0
  87. canisend-0.2.0/src/canisend/material_review.py +216 -0
  88. canisend-0.2.0/src/canisend/materials.py +193 -0
  89. canisend-0.2.0/src/canisend/orchestrator.py +686 -0
  90. canisend-0.2.0/src/canisend/package_check.py +87 -0
  91. canisend-0.2.0/src/canisend/parse.py +200 -0
  92. canisend-0.2.0/src/canisend/pipeline.py +224 -0
  93. canisend-0.2.0/src/canisend/profile.py +189 -0
  94. canisend-0.2.0/src/canisend/ready_check.py +124 -0
  95. canisend-0.2.0/src/canisend/resource_files.py +78 -0
  96. canisend-0.2.0/src/canisend/resources/__init__.py +1 -0
  97. canisend-0.2.0/src/canisend/rss.py +89 -0
  98. canisend-0.2.0/src/canisend/skill_distribution.py +31 -0
  99. canisend-0.2.0/src/canisend/typst.py +29 -0
  100. canisend-0.2.0/src/canisend/typst_mapping.py +300 -0
  101. canisend-0.2.0/src/canisend/versioning.py +123 -0
  102. canisend-0.2.0/src/canisend/workspace.py +273 -0
  103. canisend-0.2.0/templates/typst/application_package.typ +35 -0
  104. canisend-0.2.0/templates/typst/cover_letter.typ +40 -0
  105. canisend-0.2.0/templates/typst/cv_notes.typ +18 -0
  106. canisend-0.2.0/tests/test_cli.py +183 -0
  107. canisend-0.2.0/tests/test_evidence.py +388 -0
  108. canisend-0.2.0/tests/test_examples.py +138 -0
  109. canisend-0.2.0/tests/test_git_tracking.py +75 -0
  110. canisend-0.2.0/tests/test_job_import.py +341 -0
  111. canisend-0.2.0/tests/test_jobs.py +301 -0
  112. canisend-0.2.0/tests/test_llm.py +103 -0
  113. canisend-0.2.0/tests/test_match.py +306 -0
  114. canisend-0.2.0/tests/test_material_review.py +127 -0
  115. canisend-0.2.0/tests/test_materials_llm.py +187 -0
  116. canisend-0.2.0/tests/test_orchestrator.py +401 -0
  117. canisend-0.2.0/tests/test_parse.py +51 -0
  118. canisend-0.2.0/tests/test_parser_llm.py +102 -0
  119. canisend-0.2.0/tests/test_pipeline.py +458 -0
  120. canisend-0.2.0/tests/test_profile.py +62 -0
  121. canisend-0.2.0/tests/test_ready_check.py +116 -0
  122. canisend-0.2.0/tests/test_release_productization.py +197 -0
  123. canisend-0.2.0/tests/test_release_script.py +265 -0
  124. canisend-0.2.0/tests/test_repository_contract.py +304 -0
  125. canisend-0.2.0/tests/test_resource_files.py +47 -0
  126. canisend-0.2.0/tests/test_rss.py +74 -0
  127. canisend-0.2.0/tests/test_skill_distribution.py +143 -0
  128. canisend-0.2.0/tests/test_typst.py +58 -0
  129. canisend-0.2.0/tests/test_typst_mapping.py +189 -0
  130. canisend-0.2.0/tests/test_versioning.py +58 -0
  131. canisend-0.2.0/tests/test_workspace_productization.py +353 -0
  132. canisend-0.2.0/uv.lock +383 -0
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "canisend",
3
+ "version": "0.2.0",
4
+ "description": "Evidence-backed job application preparation skills for Codex.",
5
+ "author": {
6
+ "name": "Peng Jiaxin"
7
+ },
8
+ "homepage": "https://github.com/jxpeng98/CanISend",
9
+ "repository": "https://github.com/jxpeng98/CanISend",
10
+ "license": "MIT",
11
+ "keywords": [
12
+ "academic jobs",
13
+ "applications",
14
+ "skills",
15
+ "typst"
16
+ ],
17
+ "skills": "./skills/",
18
+ "interface": {
19
+ "displayName": "CanISend",
20
+ "shortDescription": "Evidence-backed application skills.",
21
+ "longDescription": "CanISend provides local-first skills for preparing evidence-backed academic and professional job application materials without submitting applications or fabricating claims.",
22
+ "developerName": "Peng Jiaxin",
23
+ "category": "Productivity",
24
+ "capabilities": [
25
+ "Write"
26
+ ],
27
+ "defaultPrompt": [
28
+ "Use CanISend to review my application materials.",
29
+ "Strengthen this research statement with evidence.",
30
+ "Check my cover letter against the job criteria."
31
+ ]
32
+ }
33
+ }
@@ -0,0 +1,6 @@
1
+ ACADEMIC_PREP_LLM_PROVIDER=openai-compatible
2
+ OPENAI_API_KEY=
3
+ OPENAI_BASE_URL=
4
+ OPENAI_MODEL=
5
+ ACADEMIC_PREP_LLM_COMMAND=
6
+ ACADEMIC_PREP_LLM_TIMEOUT_SECONDS=300
@@ -0,0 +1,45 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ - "feature/**"
8
+ pull_request:
9
+
10
+ jobs:
11
+ test-build:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - name: Install uv
17
+ uses: astral-sh/setup-uv@v5
18
+
19
+ - name: Set up Python
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: "3.12"
23
+
24
+ - name: Install project
25
+ run: uv sync --all-extras --dev
26
+
27
+ - name: Run tests
28
+ run: uv run python -m pytest -v
29
+
30
+ - name: Build distributions
31
+ run: uv build
32
+
33
+ - name: Check package metadata
34
+ run: uvx twine check dist/*
35
+
36
+ - name: Check packaged resources
37
+ run: uv run python -m canisend.package_check dist/*.whl
38
+
39
+ - name: Smoke test built wheel
40
+ run: |
41
+ python -m venv /tmp/canisend-smoke
42
+ /tmp/canisend-smoke/bin/pip install dist/*.whl
43
+ /tmp/canisend-smoke/bin/canisend --help
44
+ /tmp/canisend-smoke/bin/canisend init-workspace --workspace /tmp/canisend-workspace
45
+ /tmp/canisend-smoke/bin/canisend doctor --workspace /tmp/canisend-workspace
@@ -0,0 +1,198 @@
1
+ name: release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+ - "test/v*"
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ outputs:
13
+ version: ${{ steps.release-metadata.outputs.version }}
14
+ release_channel: ${{ steps.release-metadata.outputs.release_channel }}
15
+ prerelease: ${{ steps.release-metadata.outputs.prerelease }}
16
+ publish_pypi: ${{ steps.release-metadata.outputs.publish_pypi }}
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Resolve release metadata
21
+ id: release-metadata
22
+ shell: bash
23
+ run: |
24
+ tag="$GITHUB_REF_NAME"
25
+ publish_pypi=false
26
+ prerelease=false
27
+
28
+ if [[ "$tag" == test/v* ]]; then
29
+ version="${tag#test/v}"
30
+ release_channel=test
31
+ elif [[ "$tag" == v* ]]; then
32
+ version="${tag#v}"
33
+ publish_pypi=true
34
+ if [[ "$version" =~ (a|b|rc)[0-9]+$ ]]; then
35
+ release_channel=beta
36
+ prerelease=true
37
+ else
38
+ release_channel=stable
39
+ fi
40
+ else
41
+ echo "Unsupported release tag: $tag" >&2
42
+ exit 1
43
+ fi
44
+
45
+ pyproject_version="$(sed -nE 's/^version = "([^"]+)"/\1/p' pyproject.toml | head -n 1)"
46
+ package_version="$(sed -nE 's/^__version__ = "([^"]+)"/\1/p' src/canisend/__init__.py | head -n 1)"
47
+
48
+ if [[ "$version" != "$pyproject_version" ]]; then
49
+ echo "Tag version $version does not match pyproject.toml $pyproject_version" >&2
50
+ exit 1
51
+ fi
52
+
53
+ if [[ "$pyproject_version" != "$package_version" ]]; then
54
+ echo "pyproject.toml $pyproject_version does not match __init__.py $package_version" >&2
55
+ exit 1
56
+ fi
57
+
58
+ echo "version=$version" >> "$GITHUB_OUTPUT"
59
+ echo "release_channel=$release_channel" >> "$GITHUB_OUTPUT"
60
+ echo "prerelease=$prerelease" >> "$GITHUB_OUTPUT"
61
+ echo "publish_pypi=$publish_pypi" >> "$GITHUB_OUTPUT"
62
+
63
+ - name: Install uv
64
+ uses: astral-sh/setup-uv@v5
65
+
66
+ - name: Set up Python
67
+ uses: actions/setup-python@v5
68
+ with:
69
+ python-version: "3.12"
70
+
71
+ - name: Install project
72
+ run: uv sync --all-extras --dev
73
+
74
+ - name: Run tests
75
+ run: uv run python -m pytest -v
76
+
77
+ - name: Build distributions
78
+ run: uv build
79
+
80
+ - name: Check package metadata
81
+ run: uvx twine check dist/*
82
+
83
+ - name: Check packaged resources
84
+ run: uv run python -m canisend.package_check dist/*.whl
85
+
86
+ - name: Smoke test built wheel
87
+ run: |
88
+ python -m venv /tmp/canisend-smoke
89
+ /tmp/canisend-smoke/bin/pip install dist/*.whl
90
+ /tmp/canisend-smoke/bin/canisend --help
91
+ /tmp/canisend-smoke/bin/canisend init-workspace --workspace /tmp/canisend-workspace
92
+ /tmp/canisend-smoke/bin/canisend doctor --workspace /tmp/canisend-workspace
93
+
94
+ - name: Upload distributions
95
+ uses: actions/upload-artifact@v4
96
+ with:
97
+ name: python-distributions
98
+ path: |
99
+ dist/*.tar.gz
100
+ dist/*.whl
101
+
102
+ publish-testpypi:
103
+ needs: build
104
+ runs-on: ubuntu-latest
105
+ environment:
106
+ name: testpypi
107
+ url: https://test.pypi.org/p/canisend
108
+ permissions:
109
+ id-token: write
110
+ steps:
111
+ - name: Download distributions
112
+ uses: actions/download-artifact@v4
113
+ with:
114
+ name: python-distributions
115
+ path: dist
116
+
117
+ - name: Publish package distributions to TestPyPI
118
+ uses: pypa/gh-action-pypi-publish@release/v1
119
+ with:
120
+ repository-url: https://test.pypi.org/legacy/
121
+
122
+ smoke-test-testpypi:
123
+ needs: [build, publish-testpypi]
124
+ runs-on: ubuntu-latest
125
+ steps:
126
+ - name: Set up Python
127
+ uses: actions/setup-python@v5
128
+ with:
129
+ python-version: "3.12"
130
+
131
+ - name: Smoke test TestPyPI package
132
+ shell: bash
133
+ run: |
134
+ version="${{ needs.build.outputs.version }}"
135
+ python -m venv /tmp/canisend-testpypi
136
+ for attempt in 1 2 3 4 5; do
137
+ if /tmp/canisend-testpypi/bin/pip install \
138
+ --index-url https://test.pypi.org/simple/ \
139
+ --extra-index-url https://pypi.org/simple/ \
140
+ "canisend==$version"; then
141
+ break
142
+ fi
143
+ if [[ "$attempt" == "5" ]]; then
144
+ exit 1
145
+ fi
146
+ sleep 20
147
+ done
148
+ /tmp/canisend-testpypi/bin/canisend --help
149
+ /tmp/canisend-testpypi/bin/canisend init-workspace --workspace /tmp/canisend-testpypi-workspace
150
+ /tmp/canisend-testpypi/bin/canisend doctor --workspace /tmp/canisend-testpypi-workspace
151
+
152
+ publish-pypi:
153
+ if: needs.build.outputs.publish_pypi == 'true'
154
+ needs: [build, publish-testpypi, smoke-test-testpypi]
155
+ runs-on: ubuntu-latest
156
+ environment:
157
+ name: pypi
158
+ url: https://pypi.org/p/canisend
159
+ permissions:
160
+ id-token: write
161
+ steps:
162
+ - name: Download distributions
163
+ uses: actions/download-artifact@v4
164
+ with:
165
+ name: python-distributions
166
+ path: dist
167
+
168
+ - name: Publish package distributions to PyPI
169
+ uses: pypa/gh-action-pypi-publish@release/v1
170
+
171
+ create-github-release:
172
+ if: needs.build.outputs.publish_pypi == 'true'
173
+ needs: [build, publish-pypi]
174
+ runs-on: ubuntu-latest
175
+ permissions:
176
+ contents: write
177
+ steps:
178
+ - uses: actions/checkout@v4
179
+
180
+ - name: Create GitHub Release
181
+ env:
182
+ GH_TOKEN: ${{ github.token }}
183
+ VERSION: ${{ needs.build.outputs.version }}
184
+ RELEASE_CHANNEL: ${{ needs.build.outputs.release_channel }}
185
+ PRERELEASE: ${{ needs.build.outputs.prerelease }}
186
+ shell: bash
187
+ run: |
188
+ label="${RELEASE_CHANNEL^}"
189
+ args=(
190
+ gh release create "$GITHUB_REF_NAME"
191
+ --verify-tag
192
+ --title "CanISend $VERSION"
193
+ --notes "$label release $VERSION. See CHANGELOG.md and RELEASE.md for details."
194
+ )
195
+ if [[ "$PRERELEASE" == "true" ]]; then
196
+ args+=(--prerelease)
197
+ fi
198
+ "${args[@]}"
@@ -0,0 +1,16 @@
1
+ .DS_Store
2
+ .env
3
+ .venv/
4
+ __pycache__/
5
+ *.py[cod]
6
+ .pytest_cache/
7
+ .ruff_cache/
8
+ .claude/
9
+ dist/
10
+ build/
11
+ jobs/*
12
+ !jobs/.gitkeep
13
+ job_leads/
14
+ profile/*
15
+ !profile/.gitkeep
16
+ .vscode/
@@ -0,0 +1,15 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 - Alpha
4
+
5
+ Initial alpha release of the local-first academic application preparation CLI.
6
+
7
+ - Added installable `canisend` CLI and private workspace workflow.
8
+ - Added jobs.ac.uk RSS lead import with local keyword filtering.
9
+ - Added Typst-first private profile scaffolding and normalized evidence extraction.
10
+ - Added OpenAI-compatible and local command LLM provider support.
11
+ - Added job advert parsing, evidence matching, cover letter draft, CV tailoring notes, criteria checklist, final package, and material review checklist outputs.
12
+ - Added modernpro Typst source generation with optional local PDF rendering.
13
+ - Added cross-platform agent skill resources for Codex, Claude Code, and IDE agents.
14
+
15
+ This release prepares application materials only. It does not submit applications, fill web portals, create accounts, scrape full job pages, or answer sensitive declarations.
canisend-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Peng Jiaxin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.