stage-cli 1.0.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.
- stage_cli-1.0.0/.gitattributes +51 -0
- stage_cli-1.0.0/.gitignore +92 -0
- stage_cli-1.0.0/.python-version +1 -0
- stage_cli-1.0.0/CHANGELOG.md +45 -0
- stage_cli-1.0.0/LICENSE +21 -0
- stage_cli-1.0.0/PKG-INFO +379 -0
- stage_cli-1.0.0/README.md +340 -0
- stage_cli-1.0.0/SECURITY.md +48 -0
- stage_cli-1.0.0/assets/ci-flow.svg +49 -0
- stage_cli-1.0.0/assets/cli.png +0 -0
- stage_cli-1.0.0/assets/tui.png +0 -0
- stage_cli-1.0.0/pyproject.toml +152 -0
- stage_cli-1.0.0/src/stage/__init__.py +1 -0
- stage_cli-1.0.0/src/stage/__main__.py +8 -0
- stage_cli-1.0.0/src/stage/banner.py +32 -0
- stage_cli-1.0.0/src/stage/bootstrap/__init__.py +0 -0
- stage_cli-1.0.0/src/stage/bootstrap/openjobs.py +392 -0
- stage_cli-1.0.0/src/stage/classify/__init__.py +29 -0
- stage_cli-1.0.0/src/stage/classify/eligibility.py +115 -0
- stage_cli-1.0.0/src/stage/classify/internship.py +64 -0
- stage_cli-1.0.0/src/stage/classify/role.py +91 -0
- stage_cli-1.0.0/src/stage/classify/scope.py +47 -0
- stage_cli-1.0.0/src/stage/cli/__init__.py +0 -0
- stage_cli-1.0.0/src/stage/cli/app.py +4 -0
- stage_cli-1.0.0/src/stage/cli/commands/__init__.py +8 -0
- stage_cli-1.0.0/src/stage/cli/commands/discovery.py +294 -0
- stage_cli-1.0.0/src/stage/cli/commands/insight.py +494 -0
- stage_cli-1.0.0/src/stage/cli/commands/pipeline.py +337 -0
- stage_cli-1.0.0/src/stage/cli/commands/postings.py +473 -0
- stage_cli-1.0.0/src/stage/cli/commands/schedule.py +171 -0
- stage_cli-1.0.0/src/stage/cli/housekeeping.py +64 -0
- stage_cli-1.0.0/src/stage/cli/logfile.py +56 -0
- stage_cli-1.0.0/src/stage/cli/notify.py +170 -0
- stage_cli-1.0.0/src/stage/cli/options.py +678 -0
- stage_cli-1.0.0/src/stage/cli/render.py +1398 -0
- stage_cli-1.0.0/src/stage/cli/runlock.py +74 -0
- stage_cli-1.0.0/src/stage/cli/schedule.py +702 -0
- stage_cli-1.0.0/src/stage/cli/schedule_state.py +363 -0
- stage_cli-1.0.0/src/stage/cli/selection.py +83 -0
- stage_cli-1.0.0/src/stage/cli/serialize.py +196 -0
- stage_cli-1.0.0/src/stage/companies.py +542 -0
- stage_cli-1.0.0/src/stage/data/companies/a.yaml +1289 -0
- stage_cli-1.0.0/src/stage/data/companies/b.yaml +900 -0
- stage_cli-1.0.0/src/stage/data/companies/c.yaml +1377 -0
- stage_cli-1.0.0/src/stage/data/companies/d.yaml +497 -0
- stage_cli-1.0.0/src/stage/data/companies/e.yaml +519 -0
- stage_cli-1.0.0/src/stage/data/companies/f.yaml +454 -0
- stage_cli-1.0.0/src/stage/data/companies/g.yaml +601 -0
- stage_cli-1.0.0/src/stage/data/companies/h.yaml +446 -0
- stage_cli-1.0.0/src/stage/data/companies/i.yaml +503 -0
- stage_cli-1.0.0/src/stage/data/companies/j.yaml +138 -0
- stage_cli-1.0.0/src/stage/data/companies/k.yaml +278 -0
- stage_cli-1.0.0/src/stage/data/companies/l.yaml +402 -0
- stage_cli-1.0.0/src/stage/data/companies/m.yaml +937 -0
- stage_cli-1.0.0/src/stage/data/companies/n.yaml +549 -0
- stage_cli-1.0.0/src/stage/data/companies/o.yaml +371 -0
- stage_cli-1.0.0/src/stage/data/companies/other.yaml +58 -0
- stage_cli-1.0.0/src/stage/data/companies/p.yaml +825 -0
- stage_cli-1.0.0/src/stage/data/companies/q.yaml +121 -0
- stage_cli-1.0.0/src/stage/data/companies/r.yaml +583 -0
- stage_cli-1.0.0/src/stage/data/companies/s.yaml +1140 -0
- stage_cli-1.0.0/src/stage/data/companies/t.yaml +817 -0
- stage_cli-1.0.0/src/stage/data/companies/u.yaml +196 -0
- stage_cli-1.0.0/src/stage/data/companies/v.yaml +325 -0
- stage_cli-1.0.0/src/stage/data/companies/w.yaml +353 -0
- stage_cli-1.0.0/src/stage/data/companies/x.yaml +67 -0
- stage_cli-1.0.0/src/stage/data/companies/y.yaml +36 -0
- stage_cli-1.0.0/src/stage/data/companies/z.yaml +146 -0
- stage_cli-1.0.0/src/stage/data/fonts/DejaVuSans.LICENSE.txt +99 -0
- stage_cli-1.0.0/src/stage/data/fonts/DejaVuSans.ttf +0 -0
- stage_cli-1.0.0/src/stage/data/lexicon/company_tokens.yaml +228 -0
- stage_cli-1.0.0/src/stage/data/lexicon/eligibility.yaml +455 -0
- stage_cli-1.0.0/src/stage/data/lexicon/inclusive_suffixes.yaml +37 -0
- stage_cli-1.0.0/src/stage/data/lexicon/internship.yaml +187 -0
- stage_cli-1.0.0/src/stage/data/lexicon/language.yaml +226 -0
- stage_cli-1.0.0/src/stage/data/lexicon/locations.yaml +1159 -0
- stage_cli-1.0.0/src/stage/data/lexicon/roles.yaml +2012 -0
- stage_cli-1.0.0/src/stage/data/lexicon/terms.yaml +76 -0
- stage_cli-1.0.0/src/stage/data/lexicon/workday_facets.yaml +27 -0
- stage_cli-1.0.0/src/stage/data/seed_companies.yaml +198 -0
- stage_cli-1.0.0/src/stage/dedup/__init__.py +19 -0
- stage_cli-1.0.0/src/stage/dedup/identity.py +113 -0
- stage_cli-1.0.0/src/stage/dedup/resolve.py +97 -0
- stage_cli-1.0.0/src/stage/domain/__init__.py +244 -0
- stage_cli-1.0.0/src/stage/domain/company.py +49 -0
- stage_cli-1.0.0/src/stage/domain/coverage.py +86 -0
- stage_cli-1.0.0/src/stage/domain/custom_board.py +92 -0
- stage_cli-1.0.0/src/stage/domain/discovery.py +94 -0
- stage_cli-1.0.0/src/stage/domain/enums.py +114 -0
- stage_cli-1.0.0/src/stage/domain/events.py +204 -0
- stage_cli-1.0.0/src/stage/domain/filters.py +27 -0
- stage_cli-1.0.0/src/stage/domain/health.py +169 -0
- stage_cli-1.0.0/src/stage/domain/ids.py +48 -0
- stage_cli-1.0.0/src/stage/domain/job.py +47 -0
- stage_cli-1.0.0/src/stage/domain/matching.py +15 -0
- stage_cli-1.0.0/src/stage/domain/priority.py +34 -0
- stage_cli-1.0.0/src/stage/domain/quarantine.py +39 -0
- stage_cli-1.0.0/src/stage/domain/rate_state.py +78 -0
- stage_cli-1.0.0/src/stage/domain/retention.py +20 -0
- stage_cli-1.0.0/src/stage/domain/rotation.py +46 -0
- stage_cli-1.0.0/src/stage/domain/signals.py +12 -0
- stage_cli-1.0.0/src/stage/domain/sync_run.py +35 -0
- stage_cli-1.0.0/src/stage/domain/text.py +113 -0
- stage_cli-1.0.0/src/stage/domain/validator.py +14 -0
- stage_cli-1.0.0/src/stage/domain/visits.py +60 -0
- stage_cli-1.0.0/src/stage/domain/workday.py +38 -0
- stage_cli-1.0.0/src/stage/http/__init__.py +58 -0
- stage_cli-1.0.0/src/stage/http/breaker.py +53 -0
- stage_cli-1.0.0/src/stage/http/cache.py +44 -0
- stage_cli-1.0.0/src/stage/http/client.py +725 -0
- stage_cli-1.0.0/src/stage/http/profiles.py +101 -0
- stage_cli-1.0.0/src/stage/lexicon.py +370 -0
- stage_cli-1.0.0/src/stage/normalize/__init__.py +16 -0
- stage_cli-1.0.0/src/stage/normalize/language.py +47 -0
- stage_cli-1.0.0/src/stage/normalize/location.py +271 -0
- stage_cli-1.0.0/src/stage/normalize/terms.py +153 -0
- stage_cli-1.0.0/src/stage/normalize/urls.py +122 -0
- stage_cli-1.0.0/src/stage/paths.py +86 -0
- stage_cli-1.0.0/src/stage/py.typed +0 -0
- stage_cli-1.0.0/src/stage/services/__init__.py +0 -0
- stage_cli-1.0.0/src/stage/services/canary.py +120 -0
- stage_cli-1.0.0/src/stage/services/coverage.py +231 -0
- stage_cli-1.0.0/src/stage/services/discover.py +747 -0
- stage_cli-1.0.0/src/stage/services/export.py +274 -0
- stage_cli-1.0.0/src/stage/services/health.py +237 -0
- stage_cli-1.0.0/src/stage/services/maintenance.py +225 -0
- stage_cli-1.0.0/src/stage/services/quarantine.py +20 -0
- stage_cli-1.0.0/src/stage/services/query.py +86 -0
- stage_cli-1.0.0/src/stage/services/sync.py +1257 -0
- stage_cli-1.0.0/src/stage/sources/__init__.py +82 -0
- stage_cli-1.0.0/src/stage/sources/_text.py +79 -0
- stage_cli-1.0.0/src/stage/sources/ashby.py +93 -0
- stage_cli-1.0.0/src/stage/sources/bamboohr.py +80 -0
- stage_cli-1.0.0/src/stage/sources/base.py +225 -0
- stage_cli-1.0.0/src/stage/sources/breezy.py +90 -0
- stage_cli-1.0.0/src/stage/sources/collage.py +60 -0
- stage_cli-1.0.0/src/stage/sources/community_feeds.py +142 -0
- stage_cli-1.0.0/src/stage/sources/curated_markdown.py +289 -0
- stage_cli-1.0.0/src/stage/sources/custom_json.py +610 -0
- stage_cli-1.0.0/src/stage/sources/espresso.py +154 -0
- stage_cli-1.0.0/src/stage/sources/feed.py +44 -0
- stage_cli-1.0.0/src/stage/sources/greenhouse.py +104 -0
- stage_cli-1.0.0/src/stage/sources/jobbank.py +147 -0
- stage_cli-1.0.0/src/stage/sources/jobvite.py +133 -0
- stage_cli-1.0.0/src/stage/sources/lever.py +76 -0
- stage_cli-1.0.0/src/stage/sources/oracle_cloud.py +187 -0
- stage_cli-1.0.0/src/stage/sources/platforms.py +609 -0
- stage_cli-1.0.0/src/stage/sources/quebec_emploi.py +146 -0
- stage_cli-1.0.0/src/stage/sources/recruitee.py +96 -0
- stage_cli-1.0.0/src/stage/sources/simplify.py +110 -0
- stage_cli-1.0.0/src/stage/sources/smartrecruiters.py +216 -0
- stage_cli-1.0.0/src/stage/sources/speedyapply.py +200 -0
- stage_cli-1.0.0/src/stage/sources/themuse.py +157 -0
- stage_cli-1.0.0/src/stage/sources/workable.py +83 -0
- stage_cli-1.0.0/src/stage/sources/workday.py +524 -0
- stage_cli-1.0.0/src/stage/sources/zshah.py +99 -0
- stage_cli-1.0.0/src/stage/storage/__init__.py +29 -0
- stage_cli-1.0.0/src/stage/storage/migrations/0001_initial.sql +239 -0
- stage_cli-1.0.0/src/stage/storage/migrations/__init__.py +135 -0
- stage_cli-1.0.0/src/stage/storage/repository.py +213 -0
- stage_cli-1.0.0/src/stage/storage/search.py +28 -0
- stage_cli-1.0.0/src/stage/storage/sqlite_repo.py +1586 -0
- stage_cli-1.0.0/src/stage/storage/writer.py +249 -0
- stage_cli-1.0.0/src/stage/tui/__init__.py +0 -0
- stage_cli-1.0.0/src/stage/tui/app.py +82 -0
- stage_cli-1.0.0/src/stage/tui/help.py +26 -0
- stage_cli-1.0.0/src/stage/tui/safe.py +21 -0
- stage_cli-1.0.0/src/stage/tui/screens/__init__.py +0 -0
- stage_cli-1.0.0/src/stage/tui/screens/boards.py +186 -0
- stage_cli-1.0.0/src/stage/tui/screens/postings.py +509 -0
- stage_cli-1.0.0/src/stage/tui/screens/review.py +209 -0
- stage_cli-1.0.0/src/stage/tui/screens/splash.py +37 -0
- stage_cli-1.0.0/src/stage/tui/screens/stats.py +124 -0
- stage_cli-1.0.0/src/stage/tui/screens/sync.py +194 -0
- stage_cli-1.0.0/src/stage/tui/state.py +160 -0
- stage_cli-1.0.0/src/stage/tui/theme.tcss +205 -0
- stage_cli-1.0.0/src/stage/tui/widgets/__init__.py +0 -0
- stage_cli-1.0.0/tests/conftest.py +55 -0
- stage_cli-1.0.0/tests/fixtures/bilingual_titles.json +344 -0
- stage_cli-1.0.0/tests/fixtures/blackrock_search.html +98 -0
- stage_cli-1.0.0/tests/fixtures/croesus_career.html +87 -0
- stage_cli-1.0.0/tests/fixtures/disney_search.html +32 -0
- stage_cli-1.0.0/tests/fixtures/ea_searchjobs.html +116 -0
- stage_cli-1.0.0/tests/fixtures/espresso_search.html +144 -0
- stage_cli-1.0.0/tests/fixtures/gildan_openpositions.html +130 -0
- stage_cli-1.0.0/tests/fixtures/greenhouse_jobs.json +31 -0
- stage_cli-1.0.0/tests/fixtures/jobbank_search.html +176 -0
- stage_cli-1.0.0/tests/fixtures/jobvite_agscareer.html +233 -0
- stage_cli-1.0.0/tests/fixtures/jobvite_resolver.html +217 -0
- stage_cli-1.0.0/tests/fixtures/l3harris_search.html +26 -0
- stage_cli-1.0.0/tests/fixtures/lightspeed_openings.html +45 -0
- stage_cli-1.0.0/tests/fixtures/loblaw_jobs.html +8 -0
- stage_cli-1.0.0/tests/fixtures/nbc_search.html +51 -0
- stage_cli-1.0.0/tests/fixtures/parsons_search.html +8 -0
- stage_cli-1.0.0/tests/fixtures/publicissapient_sitemap.html +14 -0
- stage_cli-1.0.0/tests/fixtures/rentec_careers.html +44 -0
- stage_cli-1.0.0/tests/fixtures/sega_vacancies.html +7 -0
- stage_cli-1.0.0/tests/fixtures/sfrmk_search.html +65 -0
- stage_cli-1.0.0/tests/fixtures/shopify_internships.html +12 -0
- stage_cli-1.0.0/tests/fixtures/twosigma_openroles.html +110 -0
- stage_cli-1.0.0/tests/golden/classification.json +701 -0
- stage_cli-1.0.0/tests/test_adapter_salvage.py +134 -0
- stage_cli-1.0.0/tests/test_adopt_unregistered.py +191 -0
- stage_cli-1.0.0/tests/test_board_identity.py +149 -0
- stage_cli-1.0.0/tests/test_board_rest.py +112 -0
- stage_cli-1.0.0/tests/test_canary.py +132 -0
- stage_cli-1.0.0/tests/test_canary_events.py +72 -0
- stage_cli-1.0.0/tests/test_classify.py +799 -0
- stage_cli-1.0.0/tests/test_cli_help.py +401 -0
- stage_cli-1.0.0/tests/test_cli_options.py +1117 -0
- stage_cli-1.0.0/tests/test_collage_adapter.py +54 -0
- stage_cli-1.0.0/tests/test_companies_registry.py +314 -0
- stage_cli-1.0.0/tests/test_company_fold_key.py +147 -0
- stage_cli-1.0.0/tests/test_coverage.py +476 -0
- stage_cli-1.0.0/tests/test_curated_markdown.py +188 -0
- stage_cli-1.0.0/tests/test_custom_json.py +440 -0
- stage_cli-1.0.0/tests/test_custom_json_buckets.py +92 -0
- stage_cli-1.0.0/tests/test_custom_json_formats.py +212 -0
- stage_cli-1.0.0/tests/test_custom_json_pagination.py +239 -0
- stage_cli-1.0.0/tests/test_dedup.py +416 -0
- stage_cli-1.0.0/tests/test_degree_and_disciplines.py +193 -0
- stage_cli-1.0.0/tests/test_detail_queue.py +318 -0
- stage_cli-1.0.0/tests/test_discover.py +686 -0
- stage_cli-1.0.0/tests/test_eligibility.py +549 -0
- stage_cli-1.0.0/tests/test_export.py +419 -0
- stage_cli-1.0.0/tests/test_feed_adapters.py +311 -0
- stage_cli-1.0.0/tests/test_feeds.py +384 -0
- stage_cli-1.0.0/tests/test_fetch_authority.py +218 -0
- stage_cli-1.0.0/tests/test_fold_inclusive.py +207 -0
- stage_cli-1.0.0/tests/test_golden.py +106 -0
- stage_cli-1.0.0/tests/test_greenhouse_adapter.py +144 -0
- stage_cli-1.0.0/tests/test_health.py +291 -0
- stage_cli-1.0.0/tests/test_housekeeping.py +133 -0
- stage_cli-1.0.0/tests/test_html_boards.py +734 -0
- stage_cli-1.0.0/tests/test_html_text.py +87 -0
- stage_cli-1.0.0/tests/test_http_akamai.py +282 -0
- stage_cli-1.0.0/tests/test_http_client.py +330 -0
- stage_cli-1.0.0/tests/test_http_conditional.py +114 -0
- stage_cli-1.0.0/tests/test_http_resilience.py +208 -0
- stage_cli-1.0.0/tests/test_integrity_repair.py +156 -0
- stage_cli-1.0.0/tests/test_jobvite_adapter.py +134 -0
- stage_cli-1.0.0/tests/test_language.py +107 -0
- stage_cli-1.0.0/tests/test_layering.py +272 -0
- stage_cli-1.0.0/tests/test_lever_adapter.py +148 -0
- stage_cli-1.0.0/tests/test_lexicon.py +83 -0
- stage_cli-1.0.0/tests/test_location.py +392 -0
- stage_cli-1.0.0/tests/test_malformed_input.py +262 -0
- stage_cli-1.0.0/tests/test_migrations.py +123 -0
- stage_cli-1.0.0/tests/test_new_adapters.py +450 -0
- stage_cli-1.0.0/tests/test_notify.py +512 -0
- stage_cli-1.0.0/tests/test_openjobs_crossref.py +217 -0
- stage_cli-1.0.0/tests/test_oracle_cloud_adapter.py +108 -0
- stage_cli-1.0.0/tests/test_paths.py +68 -0
- stage_cli-1.0.0/tests/test_permissions.py +56 -0
- stage_cli-1.0.0/tests/test_platform_shapes.py +176 -0
- stage_cli-1.0.0/tests/test_profiles.py +147 -0
- stage_cli-1.0.0/tests/test_quarantine.py +294 -0
- stage_cli-1.0.0/tests/test_quarantine_relabel.py +85 -0
- stage_cli-1.0.0/tests/test_quebec_emploi.py +75 -0
- stage_cli-1.0.0/tests/test_rate_state.py +831 -0
- stage_cli-1.0.0/tests/test_recall_limits.py +164 -0
- stage_cli-1.0.0/tests/test_recheck.py +221 -0
- stage_cli-1.0.0/tests/test_registry_shards.py +77 -0
- stage_cli-1.0.0/tests/test_render.py +403 -0
- stage_cli-1.0.0/tests/test_request_log.py +101 -0
- stage_cli-1.0.0/tests/test_rescreen.py +444 -0
- stage_cli-1.0.0/tests/test_review_contradictions.py +66 -0
- stage_cli-1.0.0/tests/test_rotation.py +688 -0
- stage_cli-1.0.0/tests/test_run_budget.py +345 -0
- stage_cli-1.0.0/tests/test_schedule.py +475 -0
- stage_cli-1.0.0/tests/test_schedule_state.py +297 -0
- stage_cli-1.0.0/tests/test_schema_baseline.py +119 -0
- stage_cli-1.0.0/tests/test_search.py +280 -0
- stage_cli-1.0.0/tests/test_serialize.py +40 -0
- stage_cli-1.0.0/tests/test_show.py +152 -0
- stage_cli-1.0.0/tests/test_signals_persist.py +187 -0
- stage_cli-1.0.0/tests/test_smartrecruiters_adapter.py +195 -0
- stage_cli-1.0.0/tests/test_speedyapply.py +134 -0
- stage_cli-1.0.0/tests/test_startup_budget.py +103 -0
- stage_cli-1.0.0/tests/test_sync_e2e.py +318 -0
- stage_cli-1.0.0/tests/test_sync_shape.py +135 -0
- stage_cli-1.0.0/tests/test_sync_step2.py +402 -0
- stage_cli-1.0.0/tests/test_terms.py +141 -0
- stage_cli-1.0.0/tests/test_tombstones.py +431 -0
- stage_cli-1.0.0/tests/test_tui.py +2121 -0
- stage_cli-1.0.0/tests/test_unregistered_quarantine.py +130 -0
- stage_cli-1.0.0/tests/test_untrusted_display.py +267 -0
- stage_cli-1.0.0/tests/test_update_badges.py +265 -0
- stage_cli-1.0.0/tests/test_verify_registry.py +122 -0
- stage_cli-1.0.0/tests/test_workday_adapter.py +1534 -0
- stage_cli-1.0.0/tests/test_workday_urls.py +139 -0
- stage_cli-1.0.0/tests/test_writer.py +95 -0
- stage_cli-1.0.0/uv.lock +956 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
* text=auto
|
|
2
|
+
|
|
3
|
+
*.py text diff=python
|
|
4
|
+
*.pyi text diff=python
|
|
5
|
+
*.sql text
|
|
6
|
+
*.yaml text
|
|
7
|
+
*.yml text
|
|
8
|
+
*.json text
|
|
9
|
+
*.toml text
|
|
10
|
+
*.md text diff=markdown
|
|
11
|
+
*.cfg text
|
|
12
|
+
*.ini text
|
|
13
|
+
*.txt text
|
|
14
|
+
*.tcss text
|
|
15
|
+
*.rb text
|
|
16
|
+
*.svg text
|
|
17
|
+
.python-version text
|
|
18
|
+
.gitignore text
|
|
19
|
+
.gitattributes text
|
|
20
|
+
|
|
21
|
+
*.sh text eol=lf
|
|
22
|
+
*.bash text eol=lf
|
|
23
|
+
*.bat text eol=crlf
|
|
24
|
+
*.cmd text eol=crlf
|
|
25
|
+
|
|
26
|
+
*.db binary
|
|
27
|
+
*.db-wal binary
|
|
28
|
+
*.db-shm binary
|
|
29
|
+
*.sqlite binary
|
|
30
|
+
*.sqlite3 binary
|
|
31
|
+
*.ttf binary
|
|
32
|
+
*.otf binary
|
|
33
|
+
*.woff binary
|
|
34
|
+
*.woff2 binary
|
|
35
|
+
*.png binary
|
|
36
|
+
*.jpg binary
|
|
37
|
+
*.jpeg binary
|
|
38
|
+
*.gif binary
|
|
39
|
+
*.ico binary
|
|
40
|
+
*.pdf binary
|
|
41
|
+
*.zip binary
|
|
42
|
+
*.gz binary
|
|
43
|
+
*.whl binary
|
|
44
|
+
|
|
45
|
+
uv.lock text linguist-generated=true
|
|
46
|
+
|
|
47
|
+
tests/fixtures/** linguist-vendored=true
|
|
48
|
+
tests/golden/** linguist-generated=true
|
|
49
|
+
|
|
50
|
+
src/stage/data/companies/** text
|
|
51
|
+
src/stage/data/lexicon/** text
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
docs/
|
|
2
|
+
|
|
3
|
+
.venv/
|
|
4
|
+
venv/
|
|
5
|
+
env/
|
|
6
|
+
ENV/
|
|
7
|
+
.python-version.local
|
|
8
|
+
|
|
9
|
+
__pycache__/
|
|
10
|
+
*.py[cod]
|
|
11
|
+
*$py.class
|
|
12
|
+
*.so
|
|
13
|
+
.Python
|
|
14
|
+
build/
|
|
15
|
+
dist/
|
|
16
|
+
wheels/
|
|
17
|
+
*.egg-info/
|
|
18
|
+
.eggs/
|
|
19
|
+
pip-wheel-metadata/
|
|
20
|
+
|
|
21
|
+
.mypy_cache/
|
|
22
|
+
.ruff_cache/
|
|
23
|
+
.pytest_cache/
|
|
24
|
+
.tox/
|
|
25
|
+
.nox/
|
|
26
|
+
.hypothesis/
|
|
27
|
+
.coverage
|
|
28
|
+
.coverage.*
|
|
29
|
+
coverage.xml
|
|
30
|
+
htmlcov/
|
|
31
|
+
.benchmarks/
|
|
32
|
+
|
|
33
|
+
*.db
|
|
34
|
+
*.db-wal
|
|
35
|
+
*.db-shm
|
|
36
|
+
*.db.bak-*
|
|
37
|
+
*.sqlite
|
|
38
|
+
*.sqlite3
|
|
39
|
+
|
|
40
|
+
.*.lock
|
|
41
|
+
|
|
42
|
+
tests/captured/
|
|
43
|
+
captured/
|
|
44
|
+
*.log
|
|
45
|
+
*.log.[0-9]
|
|
46
|
+
*.jsonl
|
|
47
|
+
logs/
|
|
48
|
+
|
|
49
|
+
stage-export-*.csv
|
|
50
|
+
stage-export-*.json
|
|
51
|
+
stage-export-*.md
|
|
52
|
+
stage-export-*.pdf
|
|
53
|
+
stage-*.csv
|
|
54
|
+
stage-*.pdf
|
|
55
|
+
last-listing.json
|
|
56
|
+
|
|
57
|
+
.env
|
|
58
|
+
.env.*
|
|
59
|
+
!.env.example
|
|
60
|
+
*.pem
|
|
61
|
+
*.key
|
|
62
|
+
*.p12
|
|
63
|
+
secrets.yaml
|
|
64
|
+
secrets.yml
|
|
65
|
+
|
|
66
|
+
node_modules/
|
|
67
|
+
.direnv/
|
|
68
|
+
*.tmp
|
|
69
|
+
|
|
70
|
+
.idea/
|
|
71
|
+
.vscode/*
|
|
72
|
+
!.vscode/extensions.json
|
|
73
|
+
*.swp
|
|
74
|
+
*.swo
|
|
75
|
+
*~
|
|
76
|
+
*.bak
|
|
77
|
+
*.orig
|
|
78
|
+
*.rej
|
|
79
|
+
|
|
80
|
+
.DS_Store
|
|
81
|
+
.AppleDouble
|
|
82
|
+
.LSOverride
|
|
83
|
+
Thumbs.db
|
|
84
|
+
Thumbs.db:encryptable
|
|
85
|
+
ehthumbs.db
|
|
86
|
+
Desktop.ini
|
|
87
|
+
desktop.ini
|
|
88
|
+
$RECYCLE.BIN/
|
|
89
|
+
*.lnk
|
|
90
|
+
|
|
91
|
+
# Local-only release runbook
|
|
92
|
+
packaging/RELEASING.md
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
|
|
5
|
+
follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [1.0.0] - 2026-09-01
|
|
8
|
+
|
|
9
|
+
First release. Stage collects CS internship postings from company job boards
|
|
10
|
+
and community feeds, keeps the ones a CS undergraduate can apply to, and stores
|
|
11
|
+
them in a SQLite database on your own machine.
|
|
12
|
+
|
|
13
|
+
### What it does
|
|
14
|
+
|
|
15
|
+
- **Reads 1,487 employers** across 15 ATS platforms — Greenhouse, Lever, Ashby,
|
|
16
|
+
Workday, SmartRecruiters, Workable and more — through 13 adapters, plus 11
|
|
17
|
+
community feeds.
|
|
18
|
+
- **Filters hard.** It has kept 4,165 postings and rejected 134,368. Senior
|
|
19
|
+
roles, graduate-only research posts and work outside Canada and the United
|
|
20
|
+
States are all rejected. A posting whose location cannot be read is kept,
|
|
21
|
+
since a board that publishes no place is missing data rather than advertising
|
|
22
|
+
a foreign one. Every rejection records the rule that caught it, so
|
|
23
|
+
`stage quarantine` shows what was skipped and why.
|
|
24
|
+
- **Classifies each posting** by role, internship scope, degree eligibility,
|
|
25
|
+
location and language, in English and French. Filter with `--role`,
|
|
26
|
+
`--location`, `--term`, `--lang`, `--source` and `--company`, and narrow by
|
|
27
|
+
date with `--last`.
|
|
28
|
+
- **Browses from the terminal.** `stage list`, `stage search`, and a full-screen
|
|
29
|
+
`stage tui`. Rows are numbered, so `stage show 3` and `stage open 3 5 9` act
|
|
30
|
+
on what you just saw. The TUI keeps its filters in one panel, marks rows with
|
|
31
|
+
`space` to open them together, and switches theme with `ctrl+t`.
|
|
32
|
+
- **Exports** to CSV, JSON, Markdown or PDF.
|
|
33
|
+
- **Runs on a schedule** through launchd, systemd or Task Scheduler, and can
|
|
34
|
+
post new postings to a Discord channel through a webhook.
|
|
35
|
+
- **Stays polite.** Requests are rate limited per host, cached with validators,
|
|
36
|
+
and backed off when a board pushes back. A cooldown longer than the retry
|
|
37
|
+
ceiling stops the run for that host rather than hammering it.
|
|
38
|
+
- **Stays local.** No account, no server, no telemetry. The only network traffic
|
|
39
|
+
is Stage reading public job boards.
|
|
40
|
+
|
|
41
|
+
### Requirements
|
|
42
|
+
|
|
43
|
+
Python 3.12, 3.13 or 3.14 on macOS, Linux or Windows.
|
|
44
|
+
|
|
45
|
+
[1.0.0]: https://github.com/NicholasXydis/Stage/releases/tag/v1.0.0
|
stage_cli-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nicholas Xydis
|
|
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.
|
stage_cli-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: stage-cli
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Aggregates CS internship postings (Canada/US, EN + FR) into a local SQLite database
|
|
5
|
+
Project-URL: Homepage, https://github.com/NicholasXydis/Stage
|
|
6
|
+
Project-URL: Repository, https://github.com/NicholasXydis/Stage
|
|
7
|
+
Project-URL: Issues, https://github.com/NicholasXydis/Stage/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/NicholasXydis/Stage/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: Nicholas Xydis <NicholasXydis@outlook.com>
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: cli,co-op,internship,python,stage,student,tui
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: MacOS
|
|
18
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
19
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
|
|
25
|
+
Classifier: Topic :: Utilities
|
|
26
|
+
Classifier: Typing :: Typed
|
|
27
|
+
Requires-Python: <3.15,>=3.12
|
|
28
|
+
Requires-Dist: beautifulsoup4>=4.15.0
|
|
29
|
+
Requires-Dist: fpdf2>=2.8.4
|
|
30
|
+
Requires-Dist: httpx>=0.28.1
|
|
31
|
+
Requires-Dist: platformdirs>=4.3.6
|
|
32
|
+
Requires-Dist: pydantic>=2.10.0
|
|
33
|
+
Requires-Dist: pyyaml>=6.0.2
|
|
34
|
+
Requires-Dist: rich>=13.9.4
|
|
35
|
+
Requires-Dist: tenacity>=9.1.4
|
|
36
|
+
Requires-Dist: textual>=8.2.8
|
|
37
|
+
Requires-Dist: typer<0.28,>=0.27.1
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
|
|
40
|
+
<div align="center">
|
|
41
|
+
<table><tr><td>
|
|
42
|
+
<pre>
|
|
43
|
+
____ _
|
|
44
|
+
/ ___|| |_ __ _ __ _ ___
|
|
45
|
+
\___ \| __| / _` | / _` | / _ \
|
|
46
|
+
___) | |_ | (_| || (_| || __/
|
|
47
|
+
|____/ \__| \__,_| \__, | \___|
|
|
48
|
+
|___/
|
|
49
|
+
</pre>
|
|
50
|
+
</td></tr></table>
|
|
51
|
+
|
|
52
|
+
### One command. Thousands of internships.
|
|
53
|
+
|
|
54
|
+
**Every CS internship you can actually apply to, in one place.**
|
|
55
|
+
|
|
56
|
+
Free and open source. No account, no server, no telemetry.
|
|
57
|
+
|
|
58
|
+
<br>
|
|
59
|
+
|
|
60
|
+

|
|
61
|
+
|
|
62
|
+
  [](https://github.com/NicholasXydis/Stage/actions/workflows/ci.yml)
|
|
63
|
+
|
|
64
|
+
<br>
|
|
65
|
+
|
|
66
|
+
<p align="center"><sub><b>Made with</b></sub></p>
|
|
67
|
+
|
|
68
|
+
<p align="center">
|
|
69
|
+
<img src="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.16.0/icons/python/python-original.svg" alt="Python" width="46" height="46">
|
|
70
|
+
|
|
71
|
+
<img src="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.16.0/icons/sqlite/sqlite-original.svg" alt="SQLite" width="46" height="46">
|
|
72
|
+
|
|
73
|
+
<img src="https://avatars.githubusercontent.com/u/93378883?s=200" alt="Textual" width="46" height="46">
|
|
74
|
+
</p>
|
|
75
|
+
|
|
76
|
+
<p align="center"><sub><b>Works on</b></sub></p>
|
|
77
|
+
|
|
78
|
+
<p align="center">
|
|
79
|
+
<img src="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.16.0/icons/windows8/windows8-original.svg" alt="Windows" width="46" height="46">
|
|
80
|
+
|
|
81
|
+
<img src="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.16.0/icons/linux/linux-original.svg" alt="Linux" width="46" height="46">
|
|
82
|
+
|
|
83
|
+
<img src="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.16.0/icons/apple/apple-original.svg" alt="macOS" width="46" height="46">
|
|
84
|
+
</p>
|
|
85
|
+
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
## Why
|
|
89
|
+
Internships disappear fast. By the time you find the right posting, hundreds of students may already be ahead of you. Most job boards make it worse by burying relevant roles under senior positions, research jobs, and listings nowhere near you.
|
|
90
|
+
|
|
91
|
+
Stage scans 1,450+ employer career boards and finds internships CS undergrads in Canada and the U.S. can actually apply to. Every rejected posting is logged with the exact reason it was filtered out.
|
|
92
|
+
|
|
93
|
+
It has kept **4,165** postings out of **138,533** screened so far.
|
|
94
|
+
|
|
95
|
+
## A look at it
|
|
96
|
+
|
|
97
|
+
<div align="center">
|
|
98
|
+
<img src="assets/tui.png" alt="The Stage TUI listing internship postings, with columns for age, company, title and location, a search bar, and the full description of the highlighted posting below" width="100%">
|
|
99
|
+
<br><sub><b>stage tui</b> — browse everything, filter with <b>f</b>, open with <b>o</b></sub>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<br>
|
|
103
|
+
|
|
104
|
+
<div align="center">
|
|
105
|
+
<img src="assets/cli.png" alt="The stage command line help, showing commands grouped into Everyday, Keeping current, Registry and maintenance, and Health" width="100%">
|
|
106
|
+
<br><sub><b>stage --help</b> — commands grouped by what you are doing</sub>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
## Install
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
uv tool install stage-cli
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
<details>
|
|
116
|
+
<summary><b>Don't have uv?</b> Install it first, then reopen your terminal.</summary>
|
|
117
|
+
|
|
118
|
+
<br>
|
|
119
|
+
|
|
120
|
+
If you already have Python, this works everywhere:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
pip install uv
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Or use your usual package manager — `brew install uv` on macOS, `winget install
|
|
127
|
+
astral-sh.uv` on Windows. More options at
|
|
128
|
+
[docs.astral.sh/uv](https://docs.astral.sh/uv/getting-started/installation/).
|
|
129
|
+
|
|
130
|
+
</details>
|
|
131
|
+
|
|
132
|
+
<details>
|
|
133
|
+
<summary><b>Other ways to install</b></summary>
|
|
134
|
+
|
|
135
|
+
<br>
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
pipx install stage-cli # isolates the tool, like uv
|
|
139
|
+
pip install stage-cli # installs into the active environment
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
`uv tool install` and `pipx` both keep Stage away from your system Python, which
|
|
143
|
+
is what you want for a command line tool.
|
|
144
|
+
|
|
145
|
+
</details>
|
|
146
|
+
|
|
147
|
+
Needs Python 3.12, 3.13, or 3.14. Nothing else to set up — no database to
|
|
148
|
+
configure, no API key, no config file.
|
|
149
|
+
|
|
150
|
+
### First run
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
stage sync # fetch every board once (grab a coffee)
|
|
154
|
+
stage tui # browse what came back
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The first sync visits every board and takes a few minutes. Later ones are much
|
|
158
|
+
quicker, since Stage remembers what it already has and only asks each board for
|
|
159
|
+
what changed.
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
stage --install-completion # tab-complete commands and filters
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Completion needs a fresh terminal. After that, `stage <TAB>` lists commands and
|
|
166
|
+
`stage list --<TAB>` lists filters.
|
|
167
|
+
|
|
168
|
+
## The basics
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
stage sync # fetch the latest postings
|
|
172
|
+
stage list # what is new, newest first
|
|
173
|
+
stage search "machine learning" # search titles, employers, descriptions
|
|
174
|
+
stage tui # browse it all in a full screen
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Rows are numbered, so you can act on what you just saw:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
stage list --role swe --location montreal
|
|
181
|
+
stage show 3 # read the third row in full
|
|
182
|
+
stage open 3 5 9 # open those three in your browser
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Filters work the same on `list`, `search`, and `export`:
|
|
186
|
+
|
|
187
|
+
| Filter | What it takes |
|
|
188
|
+
| --- | --- |
|
|
189
|
+
| `--role` | swe, security, data, ml-ai, quant, infra, embedded, general-cs |
|
|
190
|
+
| `--location` | montreal, canada, usa, unknown |
|
|
191
|
+
| `--term` | summer-2027, fall-2026, winter-2027, and so on |
|
|
192
|
+
| `--lang` | en, fr, bilingual |
|
|
193
|
+
| `--source` | greenhouse, lever, ashby, workday, and more |
|
|
194
|
+
| `--company` | one exact employer |
|
|
195
|
+
| `--all` | every match, not just the first page |
|
|
196
|
+
| `--last N` | look back N days instead of the default 14; `--last 0` removes the limit. On `search`, which already covers every row, it narrows instead |
|
|
197
|
+
|
|
198
|
+
Mistype one and Stage tells you what it accepts, rather than showing you an
|
|
199
|
+
empty list.
|
|
200
|
+
|
|
201
|
+
## The full-screen browser
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
stage tui
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Press `/` to search and `f` for filters — arrows move, enter picks, escape
|
|
208
|
+
closes. Role, location, language, how far back to look, new-since-last-sync and
|
|
209
|
+
the employer you are sitting on all live in that one panel.
|
|
210
|
+
`space` marks rows and `o` opens every one you marked. `w` expands the full
|
|
211
|
+
description, `e` exports what you are looking at, and `ctrl+t` changes the theme.
|
|
212
|
+
Press `?` for the full key list.
|
|
213
|
+
|
|
214
|
+
Same database as the CLI. The panel covers role, location, language, the date
|
|
215
|
+
window and new-since-last-sync; `--term` and `--source` stay CLI-only. Row
|
|
216
|
+
numbers are a CLI idea — in the TUI you move a cursor instead.
|
|
217
|
+
|
|
218
|
+
## Keeping it fresh
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
stage schedule enable # sync quietly in the background
|
|
222
|
+
stage schedule status # check on it
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Opt-in, and it uses whatever scheduler your system already has: launchd,
|
|
226
|
+
systemd, or Task Scheduler. Nothing runs until you turn it on.
|
|
227
|
+
|
|
228
|
+
Want to hear about new postings without checking? Point Stage at a Discord
|
|
229
|
+
channel you manage:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
stage schedule notify https://discord.com/api/webhooks/...
|
|
233
|
+
stage schedule test-notify # make sure it works
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Create a Discord webhook in the channel you want Stage to post to. It is just a URL Stage uses to send new internships.
|
|
237
|
+
|
|
238
|
+
Notifications only follow scheduled syncs, not stage sync. Your machine also needs to be awake. If it misses runs, Stage catches up once when it can run again.
|
|
239
|
+
|
|
240
|
+
<details>
|
|
241
|
+
<summary><b>Want notifications around the clock?</b></summary>
|
|
242
|
+
|
|
243
|
+
<br>
|
|
244
|
+
|
|
245
|
+
Put Stage on something that stays on — a VPS, a Raspberry Pi, an old laptop:
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
uv tool install stage-cli
|
|
249
|
+
stage sync # first sync, a few minutes
|
|
250
|
+
stage schedule notify <webhook>
|
|
251
|
+
stage schedule enable # systemd, launchd, or Task Scheduler
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
That machine keeps its own database and posts to Discord on its own schedule.
|
|
255
|
+
Yours stays free for browsing.
|
|
256
|
+
|
|
257
|
+
</details>
|
|
258
|
+
|
|
259
|
+
## Architecture
|
|
260
|
+
|
|
261
|
+
```text
|
|
262
|
+
Stage/
|
|
263
|
+
├─ src/stage/
|
|
264
|
+
│ ├─ cli/ Typer commands, Rich rendering, scheduling, notify
|
|
265
|
+
│ ├─ tui/ Textual screens over the same services; safe.py escapes output
|
|
266
|
+
│ ├─ domain/ the types every layer speaks: jobs, filters, enums, events
|
|
267
|
+
│ ├─ sources/ 13 ATS adapters + 11 curated feeds
|
|
268
|
+
│ ├─ services/ sync, discover, query, export, quarantine, health
|
|
269
|
+
│ ├─ classify/ role, internship scope, degree eligibility, location
|
|
270
|
+
│ ├─ normalize/ location, term, language, apply-url canonicalisation
|
|
271
|
+
│ ├─ dedup/ cross-source duplicate resolution
|
|
272
|
+
│ ├─ http/ rate posture, circuit breaker, validator cache
|
|
273
|
+
│ ├─ storage/ SQLite repository, migrations, FTS
|
|
274
|
+
│ └─ data/ packaged registry (a directory) and lexicons
|
|
275
|
+
├─ tests/ 2,375 tests, 89% branch coverage
|
|
276
|
+
└─ .github/workflows/ CI, CodeQL, scheduled canary, release
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
<div align="center">
|
|
280
|
+
<pre>
|
|
281
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
282
|
+
│ 1,450+ employer boards │
|
|
283
|
+
│ Greenhouse · Lever · Ashby · Workday · SmartRecruiters │
|
|
284
|
+
│ + 11 curated internship feeds │
|
|
285
|
+
└───────────────────────────┬──────────────────────────────────┘
|
|
286
|
+
│ rate-limited, cached, backed off
|
|
287
|
+
▼
|
|
288
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
289
|
+
│ normalize │
|
|
290
|
+
│ location · term · language · canonical apply URL │
|
|
291
|
+
└───────────────────────────┬──────────────────────────────────┘
|
|
292
|
+
▼
|
|
293
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
294
|
+
│ screen │
|
|
295
|
+
│ internship? · CS role? · degree scope? · Canada or US? │
|
|
296
|
+
│ every rejection keeps the rule that caught it │
|
|
297
|
+
└──────────────┬───────────────────────────────┬───────────────┘
|
|
298
|
+
│ kept │ rejected
|
|
299
|
+
▼ ▼
|
|
300
|
+
┌─────────────┐ ┌─────────────┐
|
|
301
|
+
│ jobs │ │ quarantine │
|
|
302
|
+
│ 4,165 │ │ 134,368 │
|
|
303
|
+
└──────┬──────┘ └─────────────┘
|
|
304
|
+
│ dedup collapses the same posting across sources
|
|
305
|
+
▼
|
|
306
|
+
┌───────────────────────┐
|
|
307
|
+
│ SQLite + FTS5 │
|
|
308
|
+
│ on your machine │
|
|
309
|
+
└──────┬────────┬───────┘
|
|
310
|
+
▼ ▼
|
|
311
|
+
stage list stage tui
|
|
312
|
+
search full-screen browser
|
|
313
|
+
export Discord on a schedule
|
|
314
|
+
</pre>
|
|
315
|
+
</div>
|
|
316
|
+
|
|
317
|
+
## CI
|
|
318
|
+
|
|
319
|
+
| Workflow | File | Purpose |
|
|
320
|
+
| --- | --- | --- |
|
|
321
|
+
| CI | `.github/workflows/ci.yml` | Ruff, format, mypy strict, 2,375 tests, coverage floor, badge drift |
|
|
322
|
+
| Supported Pythons | `.github/workflows/ci.yml` | The full suite again on 3.13 and 3.14 |
|
|
323
|
+
| Wheel | `.github/workflows/ci.yml` | Builds and smoke-tests the wheel on Ubuntu, Windows and macOS |
|
|
324
|
+
| CodeQL | `.github/workflows/codeql.yml` | Static analysis for Python and the workflows themselves |
|
|
325
|
+
| Canary | `.github/workflows/canary.yml` | Probes one live board per platform and opens an issue on drift |
|
|
326
|
+
| Release | `.github/workflows/release.yml` | Re-runs every gate plus pip-audit, then publishes from a `v*` tag |
|
|
327
|
+
|
|
328
|
+
<div align="center">
|
|
329
|
+
<img src="assets/ci-flow.svg" alt="Quality checks on Python 3.12, then the full suite on 3.13 and 3.14, a wheel smoke test on three operating systems, and CodeQL. All gates must pass for CI to pass" width="100%">
|
|
330
|
+
</div>
|
|
331
|
+
|
|
332
|
+
Any failing gate blocks the merge. The canary runs weekly and opens an issue
|
|
333
|
+
when a board changes shape, so parser drift surfaces before a sync does.
|
|
334
|
+
|
|
335
|
+
Every run also builds the wheel and installs it into a clean environment outside
|
|
336
|
+
the checkout on Ubuntu, Windows and macOS, checks the registry, lexicons and font
|
|
337
|
+
resolve from inside the installed package, then runs `help`, `list`, `search`,
|
|
338
|
+
`doctor`, `coverage`, `stats`, a dry-run sync and all four export formats against
|
|
339
|
+
it. What ships is what gets tested.
|
|
340
|
+
|
|
341
|
+
## Good to know
|
|
342
|
+
|
|
343
|
+
- Everything sits in one SQLite file you own. Open it with any SQLite client,
|
|
344
|
+
back it up, or delete it.
|
|
345
|
+
- The only network traffic is Stage reading public job boards. No account, no
|
|
346
|
+
telemetry.
|
|
347
|
+
- Requests are rate limited per host, cached, and retried with backoff, so it
|
|
348
|
+
does not hammer anyone's site.
|
|
349
|
+
- Four in five postings arrive with no description at all. Stage marks those
|
|
350
|
+
fields `unknown` instead of guessing.
|
|
351
|
+
- Missing something you expected? `stage quarantine` shows which rule caught it.
|
|
352
|
+
|
|
353
|
+
## Learn more
|
|
354
|
+
|
|
355
|
+
`stage help` walks through the common workflows with real examples.
|
|
356
|
+
`stage help COMMAND` explains one command; `stage --help` lists them all.
|
|
357
|
+
|
|
358
|
+
[CHANGELOG.md](CHANGELOG.md) lists what changed in each release.
|
|
359
|
+
|
|
360
|
+
## Found a bug?
|
|
361
|
+
|
|
362
|
+
Open an issue, especially if a job board stopped working, since those break
|
|
363
|
+
without warning and I cannot watch all of them myself. I read everything.
|
|
364
|
+
|
|
365
|
+
## Security
|
|
366
|
+
|
|
367
|
+
Stage reads public job boards and writes to a local file. It never asks for
|
|
368
|
+
credentials. Found a security issue? Open a
|
|
369
|
+
[security advisory](https://github.com/NicholasXydis/Stage/security/advisories/new)
|
|
370
|
+
instead of a public issue.
|
|
371
|
+
|
|
372
|
+
## License
|
|
373
|
+
|
|
374
|
+
MIT. See [LICENSE](LICENSE).
|
|
375
|
+
|
|
376
|
+
<div align="center">
|
|
377
|
+
<br>
|
|
378
|
+
<sub>Built for students who would rather be studying than refreshing career pages.</sub>
|
|
379
|
+
</div>
|