vacancy-radar 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 (216) hide show
  1. vacancy_radar-0.1.0/.gitignore +12 -0
  2. vacancy_radar-0.1.0/CHANGELOG.md +66 -0
  3. vacancy_radar-0.1.0/LICENSE +21 -0
  4. vacancy_radar-0.1.0/PKG-INFO +449 -0
  5. vacancy_radar-0.1.0/README.md +418 -0
  6. vacancy_radar-0.1.0/docs/evaluation.md +112 -0
  7. vacancy_radar-0.1.0/docs/import-guide.md +196 -0
  8. vacancy_radar-0.1.0/docs/releasing.md +177 -0
  9. vacancy_radar-0.1.0/eval/__init__.py +10 -0
  10. vacancy_radar-0.1.0/eval/metrics.py +43 -0
  11. vacancy_radar-0.1.0/eval/queries.yaml +321 -0
  12. vacancy_radar-0.1.0/eval/run.py +267 -0
  13. vacancy_radar-0.1.0/pyproject.toml +108 -0
  14. vacancy_radar-0.1.0/src/vacancy_radar/__init__.py +8 -0
  15. vacancy_radar-0.1.0/src/vacancy_radar/cli.py +800 -0
  16. vacancy_radar-0.1.0/src/vacancy_radar/config.py +227 -0
  17. vacancy_radar-0.1.0/src/vacancy_radar/core.py +1258 -0
  18. vacancy_radar-0.1.0/src/vacancy_radar/frontmatter.py +86 -0
  19. vacancy_radar-0.1.0/src/vacancy_radar/ids.py +47 -0
  20. vacancy_radar-0.1.0/src/vacancy_radar/index/__init__.py +6 -0
  21. vacancy_radar-0.1.0/src/vacancy_radar/index/build.py +425 -0
  22. vacancy_radar-0.1.0/src/vacancy_radar/index/embed.py +38 -0
  23. vacancy_radar-0.1.0/src/vacancy_radar/index/extract.py +148 -0
  24. vacancy_radar-0.1.0/src/vacancy_radar/index/schema.py +76 -0
  25. vacancy_radar-0.1.0/src/vacancy_radar/index/search.py +429 -0
  26. vacancy_radar-0.1.0/src/vacancy_radar/index/snippet.py +50 -0
  27. vacancy_radar-0.1.0/src/vacancy_radar/index/store.py +53 -0
  28. vacancy_radar-0.1.0/src/vacancy_radar/mcp_server.py +609 -0
  29. vacancy_radar-0.1.0/src/vacancy_radar/models.py +201 -0
  30. vacancy_radar-0.1.0/src/vacancy_radar/vault/__init__.py +5 -0
  31. vacancy_radar-0.1.0/src/vacancy_radar/vault/hashing.py +20 -0
  32. vacancy_radar-0.1.0/src/vacancy_radar/vault/paths.py +120 -0
  33. vacancy_radar-0.1.0/src/vacancy_radar/vault/reader.py +394 -0
  34. vacancy_radar-0.1.0/tests/conftest.py +18 -0
  35. vacancy_radar-0.1.0/tests/eval/test_findings.py +83 -0
  36. vacancy_radar-0.1.0/tests/eval/test_metrics.py +71 -0
  37. vacancy_radar-0.1.0/tests/eval/test_queries.py +159 -0
  38. vacancy_radar-0.1.0/tests/eval/test_run.py +141 -0
  39. vacancy_radar-0.1.0/tests/fixtures/generate.py +748 -0
  40. vacancy_radar-0.1.0/tests/fixtures/pdfs.py +95 -0
  41. vacancy_radar-0.1.0/tests/fixtures/test_generate.py +400 -0
  42. vacancy_radar-0.1.0/tests/fixtures/test_pdfs.py +55 -0
  43. vacancy_radar-0.1.0/tests/fixtures/vault/.vacancy-radar/.gitkeep +0 -0
  44. vacancy_radar-0.1.0/tests/fixtures/vault/acme-corp/2022-03-22-acme-corp-backend-engineer/documents/2022-03-22-acme-corp-backend-engineer-doc1.pdf +1 -0
  45. vacancy_radar-0.1.0/tests/fixtures/vault/acme-corp/2022-03-22-acme-corp-backend-engineer/documents/2022-03-22-acme-corp-backend-engineer-doc2.md +8 -0
  46. vacancy_radar-0.1.0/tests/fixtures/vault/acme-corp/2022-03-22-acme-corp-backend-engineer/documents/2022-03-22-acme-corp-backend-engineer-doc3.md +8 -0
  47. vacancy_radar-0.1.0/tests/fixtures/vault/acme-corp/2022-03-22-acme-corp-backend-engineer/documents/2022-03-22-acme-corp-backend-engineer-doc4.md +8 -0
  48. vacancy_radar-0.1.0/tests/fixtures/vault/acme-corp/2022-03-22-acme-corp-backend-engineer/documents/2022-03-22-acme-corp-backend-engineer-doc5.md +8 -0
  49. vacancy_radar-0.1.0/tests/fixtures/vault/acme-corp/2022-03-22-acme-corp-backend-engineer/documents/2022-03-22-acme-corp-backend-engineer-doc6.md +9 -0
  50. vacancy_radar-0.1.0/tests/fixtures/vault/acme-corp/2022-03-22-acme-corp-backend-engineer/vacancy.md +14 -0
  51. vacancy_radar-0.1.0/tests/fixtures/vault/acme-corp/2024-12-14-acme-corp-frontend-developer/documents/2024-12-14-acme-corp-frontend-developer-doc1.md +8 -0
  52. vacancy_radar-0.1.0/tests/fixtures/vault/acme-corp/2024-12-14-acme-corp-frontend-developer/documents/2024-12-14-acme-corp-frontend-developer-doc2.md +8 -0
  53. vacancy_radar-0.1.0/tests/fixtures/vault/acme-corp/2024-12-14-acme-corp-frontend-developer/documents/2024-12-14-acme-corp-frontend-developer-doc3.md +9 -0
  54. vacancy_radar-0.1.0/tests/fixtures/vault/acme-corp/2024-12-14-acme-corp-frontend-developer/documents/2024-12-14-acme-corp-frontend-developer-doc4.md +9 -0
  55. vacancy_radar-0.1.0/tests/fixtures/vault/acme-corp/2024-12-14-acme-corp-frontend-developer/vacancy.md +14 -0
  56. vacancy_radar-0.1.0/tests/fixtures/vault/acme-corp/_company.md +9 -0
  57. vacancy_radar-0.1.0/tests/fixtures/vault/blueprint-works/2022-06-14-blueprint-works-backend-engineer/documents/2022-06-14-blueprint-works-backend-engineer-doc1.md +8 -0
  58. vacancy_radar-0.1.0/tests/fixtures/vault/blueprint-works/2022-06-14-blueprint-works-backend-engineer/documents/2022-06-14-blueprint-works-backend-engineer-doc2.md +8 -0
  59. vacancy_radar-0.1.0/tests/fixtures/vault/blueprint-works/2022-06-14-blueprint-works-backend-engineer/documents/2022-06-14-blueprint-works-backend-engineer-doc3.md +8 -0
  60. vacancy_radar-0.1.0/tests/fixtures/vault/blueprint-works/2022-06-14-blueprint-works-backend-engineer/documents/2022-06-14-blueprint-works-backend-engineer-doc4.md +8 -0
  61. vacancy_radar-0.1.0/tests/fixtures/vault/blueprint-works/2022-06-14-blueprint-works-backend-engineer/documents/2022-06-14-blueprint-works-backend-engineer-doc5.md +8 -0
  62. vacancy_radar-0.1.0/tests/fixtures/vault/blueprint-works/2022-06-14-blueprint-works-backend-engineer/vacancy.md +15 -0
  63. vacancy_radar-0.1.0/tests/fixtures/vault/blueprint-works/2024-06-19-blueprint-works-qa-engineer/documents/2024-06-19-blueprint-works-qa-engineer-doc1.md +8 -0
  64. vacancy_radar-0.1.0/tests/fixtures/vault/blueprint-works/2024-06-19-blueprint-works-qa-engineer/documents/2024-06-19-blueprint-works-qa-engineer-doc2.md +8 -0
  65. vacancy_radar-0.1.0/tests/fixtures/vault/blueprint-works/2024-06-19-blueprint-works-qa-engineer/documents/2024-06-19-blueprint-works-qa-engineer-doc3.md +8 -0
  66. vacancy_radar-0.1.0/tests/fixtures/vault/blueprint-works/2024-06-19-blueprint-works-qa-engineer/vacancy.md +15 -0
  67. vacancy_radar-0.1.0/tests/fixtures/vault/blueprint-works/_company.md +8 -0
  68. vacancy_radar-0.1.0/tests/fixtures/vault/dummy-data-co/2022-04-26-dummy-data-co-data-analyst/documents/2022-04-26-dummy-data-co-data-analyst-doc1.md +8 -0
  69. vacancy_radar-0.1.0/tests/fixtures/vault/dummy-data-co/2022-04-26-dummy-data-co-data-analyst/documents/2022-04-26-dummy-data-co-data-analyst-doc2.md +8 -0
  70. vacancy_radar-0.1.0/tests/fixtures/vault/dummy-data-co/2022-04-26-dummy-data-co-data-analyst/documents/2022-04-26-dummy-data-co-data-analyst-doc3.md +8 -0
  71. vacancy_radar-0.1.0/tests/fixtures/vault/dummy-data-co/2022-04-26-dummy-data-co-data-analyst/documents/2022-04-26-dummy-data-co-data-analyst-doc4.md +9 -0
  72. vacancy_radar-0.1.0/tests/fixtures/vault/dummy-data-co/2022-04-26-dummy-data-co-data-analyst/vacancy.md +15 -0
  73. vacancy_radar-0.1.0/tests/fixtures/vault/dummy-data-co/2022-06-18-dummy-data-co-technical-writer/documents/2022-06-18-dummy-data-co-technical-writer-doc1.md +8 -0
  74. vacancy_radar-0.1.0/tests/fixtures/vault/dummy-data-co/2022-06-18-dummy-data-co-technical-writer/documents/2022-06-18-dummy-data-co-technical-writer-doc2.md +9 -0
  75. vacancy_radar-0.1.0/tests/fixtures/vault/dummy-data-co/2022-06-18-dummy-data-co-technical-writer/documents/2022-06-18-dummy-data-co-technical-writer-doc3.md +8 -0
  76. vacancy_radar-0.1.0/tests/fixtures/vault/dummy-data-co/2022-06-18-dummy-data-co-technical-writer/documents/2022-06-18-dummy-data-co-technical-writer-doc4.md +9 -0
  77. vacancy_radar-0.1.0/tests/fixtures/vault/dummy-data-co/2022-06-18-dummy-data-co-technical-writer/documents/2022-06-18-dummy-data-co-technical-writer-doc5.md +8 -0
  78. vacancy_radar-0.1.0/tests/fixtures/vault/dummy-data-co/2022-06-18-dummy-data-co-technical-writer/vacancy.md +13 -0
  79. vacancy_radar-0.1.0/tests/fixtures/vault/dummy-data-co/2024-07-12-dummy-data-co-backend-engineer/documents/2024-07-12-dummy-data-co-backend-engineer-doc1.md +8 -0
  80. vacancy_radar-0.1.0/tests/fixtures/vault/dummy-data-co/2024-07-12-dummy-data-co-backend-engineer/documents/2024-07-12-dummy-data-co-backend-engineer-doc2.md +8 -0
  81. vacancy_radar-0.1.0/tests/fixtures/vault/dummy-data-co/2024-07-12-dummy-data-co-backend-engineer/documents/2024-07-12-dummy-data-co-backend-engineer-doc3.md +8 -0
  82. vacancy_radar-0.1.0/tests/fixtures/vault/dummy-data-co/2024-07-12-dummy-data-co-backend-engineer/documents/2024-07-12-dummy-data-co-backend-engineer-doc4.md +8 -0
  83. vacancy_radar-0.1.0/tests/fixtures/vault/dummy-data-co/2024-07-12-dummy-data-co-backend-engineer/documents/2024-07-12-dummy-data-co-backend-engineer-doc5.md +8 -0
  84. vacancy_radar-0.1.0/tests/fixtures/vault/dummy-data-co/2024-07-12-dummy-data-co-backend-engineer/vacancy.md +14 -0
  85. vacancy_radar-0.1.0/tests/fixtures/vault/dummy-data-co/_company.md +9 -0
  86. vacancy_radar-0.1.0/tests/fixtures/vault/example-systems/2022-06-09-example-systems-product-designer/documents/2022-06-09-example-systems-product-designer-doc1.md +8 -0
  87. vacancy_radar-0.1.0/tests/fixtures/vault/example-systems/2022-06-09-example-systems-product-designer/documents/2022-06-09-example-systems-product-designer-doc2.md +8 -0
  88. vacancy_radar-0.1.0/tests/fixtures/vault/example-systems/2022-06-09-example-systems-product-designer/documents/2022-06-09-example-systems-product-designer-doc3.md +8 -0
  89. vacancy_radar-0.1.0/tests/fixtures/vault/example-systems/2022-06-09-example-systems-product-designer/documents/2022-06-09-example-systems-product-designer-doc4.md +8 -0
  90. vacancy_radar-0.1.0/tests/fixtures/vault/example-systems/2022-06-09-example-systems-product-designer/documents/2022-06-09-example-systems-product-designer-doc5.md +9 -0
  91. vacancy_radar-0.1.0/tests/fixtures/vault/example-systems/2022-06-09-example-systems-product-designer/vacancy.md +15 -0
  92. vacancy_radar-0.1.0/tests/fixtures/vault/example-systems/2023-10-24-example-systems-backend-engineer/documents/2023-10-24-example-systems-backend-engineer-doc1.md +8 -0
  93. vacancy_radar-0.1.0/tests/fixtures/vault/example-systems/2023-10-24-example-systems-backend-engineer/documents/2023-10-24-example-systems-backend-engineer-doc2.md +8 -0
  94. vacancy_radar-0.1.0/tests/fixtures/vault/example-systems/2023-10-24-example-systems-backend-engineer/documents/2023-10-24-example-systems-backend-engineer-doc3.md +8 -0
  95. vacancy_radar-0.1.0/tests/fixtures/vault/example-systems/2023-10-24-example-systems-backend-engineer/documents/2023-10-24-example-systems-backend-engineer-doc4.md +9 -0
  96. vacancy_radar-0.1.0/tests/fixtures/vault/example-systems/2023-10-24-example-systems-backend-engineer/documents/2023-10-24-example-systems-backend-engineer-doc5.md +8 -0
  97. vacancy_radar-0.1.0/tests/fixtures/vault/example-systems/2023-10-24-example-systems-backend-engineer/vacancy.md +15 -0
  98. vacancy_radar-0.1.0/tests/fixtures/vault/example-systems/_company.md +9 -0
  99. vacancy_radar-0.1.0/tests/fixtures/vault/foobar-industries/2022-06-06-foobar-industries-frontend-developer/documents/2022-06-06-foobar-industries-frontend-developer-doc1.md +8 -0
  100. vacancy_radar-0.1.0/tests/fixtures/vault/foobar-industries/2022-06-06-foobar-industries-frontend-developer/documents/2022-06-06-foobar-industries-frontend-developer-doc2.md +8 -0
  101. vacancy_radar-0.1.0/tests/fixtures/vault/foobar-industries/2022-06-06-foobar-industries-frontend-developer/documents/2022-06-06-foobar-industries-frontend-developer-doc3.md +8 -0
  102. vacancy_radar-0.1.0/tests/fixtures/vault/foobar-industries/2022-06-06-foobar-industries-frontend-developer/documents/2022-06-06-foobar-industries-frontend-developer-doc4.md +8 -0
  103. vacancy_radar-0.1.0/tests/fixtures/vault/foobar-industries/2022-06-06-foobar-industries-frontend-developer/vacancy.md +14 -0
  104. vacancy_radar-0.1.0/tests/fixtures/vault/foobar-industries/2022-11-20-foobar-industries-engineering-manager/documents/2022-11-20-foobar-industries-engineering-manager-doc1.md +8 -0
  105. vacancy_radar-0.1.0/tests/fixtures/vault/foobar-industries/2022-11-20-foobar-industries-engineering-manager/documents/2022-11-20-foobar-industries-engineering-manager-doc2.md +8 -0
  106. vacancy_radar-0.1.0/tests/fixtures/vault/foobar-industries/2022-11-20-foobar-industries-engineering-manager/documents/2022-11-20-foobar-industries-engineering-manager-doc3.md +8 -0
  107. vacancy_radar-0.1.0/tests/fixtures/vault/foobar-industries/2022-11-20-foobar-industries-engineering-manager/documents/2022-11-20-foobar-industries-engineering-manager-doc4.md +8 -0
  108. vacancy_radar-0.1.0/tests/fixtures/vault/foobar-industries/2022-11-20-foobar-industries-engineering-manager/documents/2022-11-20-foobar-industries-engineering-manager-doc5.md +8 -0
  109. vacancy_radar-0.1.0/tests/fixtures/vault/foobar-industries/2022-11-20-foobar-industries-engineering-manager/documents/2022-11-20-foobar-industries-engineering-manager-doc6.md +8 -0
  110. vacancy_radar-0.1.0/tests/fixtures/vault/foobar-industries/2022-11-20-foobar-industries-engineering-manager/vacancy.md +13 -0
  111. vacancy_radar-0.1.0/tests/fixtures/vault/foobar-industries/_company.md +9 -0
  112. vacancy_radar-0.1.0/tests/fixtures/vault/kolokolchik/2022-10-07-kolokolchik-qa-engineer/documents/2022-10-07-kolokolchik-qa-engineer-doc1.md +8 -0
  113. vacancy_radar-0.1.0/tests/fixtures/vault/kolokolchik/2022-10-07-kolokolchik-qa-engineer/documents/2022-10-07-kolokolchik-qa-engineer-doc2.md +8 -0
  114. vacancy_radar-0.1.0/tests/fixtures/vault/kolokolchik/2022-10-07-kolokolchik-qa-engineer/vacancy.md +13 -0
  115. vacancy_radar-0.1.0/tests/fixtures/vault/kolokolchik/2023-06-11-kolokolchik-product-designer/documents/2023-06-11-kolokolchik-product-designer-doc1.md +8 -0
  116. vacancy_radar-0.1.0/tests/fixtures/vault/kolokolchik/2023-06-11-kolokolchik-product-designer/documents/2023-06-11-kolokolchik-product-designer-doc2.md +8 -0
  117. vacancy_radar-0.1.0/tests/fixtures/vault/kolokolchik/2023-06-11-kolokolchik-product-designer/documents/2023-06-11-kolokolchik-product-designer-doc3.md +8 -0
  118. vacancy_radar-0.1.0/tests/fixtures/vault/kolokolchik/2023-06-11-kolokolchik-product-designer/documents/2023-06-11-kolokolchik-product-designer-doc4.md +8 -0
  119. vacancy_radar-0.1.0/tests/fixtures/vault/kolokolchik/2023-06-11-kolokolchik-product-designer/documents/2023-06-11-kolokolchik-product-designer-doc5.md +9 -0
  120. vacancy_radar-0.1.0/tests/fixtures/vault/kolokolchik/2023-06-11-kolokolchik-product-designer/vacancy.md +15 -0
  121. vacancy_radar-0.1.0/tests/fixtures/vault/kolokolchik/2024-07-04-kolokolchik-technical-writer/documents/2024-07-04-kolokolchik-technical-writer-doc1.md +8 -0
  122. vacancy_radar-0.1.0/tests/fixtures/vault/kolokolchik/2024-07-04-kolokolchik-technical-writer/documents/2024-07-04-kolokolchik-technical-writer-doc2.md +8 -0
  123. vacancy_radar-0.1.0/tests/fixtures/vault/kolokolchik/2024-07-04-kolokolchik-technical-writer/documents/2024-07-04-kolokolchik-technical-writer-doc3.md +8 -0
  124. vacancy_radar-0.1.0/tests/fixtures/vault/kolokolchik/2024-07-04-kolokolchik-technical-writer/vacancy.md +15 -0
  125. vacancy_radar-0.1.0/tests/fixtures/vault/kolokolchik/_company.md +9 -0
  126. vacancy_radar-0.1.0/tests/fixtures/vault/mockup-media/2022-02-19-mockup-media-unknown/documents/2022-02-19-mockup-media-unknown-doc1.md +8 -0
  127. vacancy_radar-0.1.0/tests/fixtures/vault/mockup-media/2022-02-19-mockup-media-unknown/documents/2022-02-19-mockup-media-unknown-doc2.md +9 -0
  128. vacancy_radar-0.1.0/tests/fixtures/vault/mockup-media/2022-02-19-mockup-media-unknown/vacancy.md +14 -0
  129. vacancy_radar-0.1.0/tests/fixtures/vault/mockup-media/2025-01-13-mockup-media-unknown/documents/2025-01-13-mockup-media-unknown-doc1.md +8 -0
  130. vacancy_radar-0.1.0/tests/fixtures/vault/mockup-media/2025-01-13-mockup-media-unknown/documents/2025-01-13-mockup-media-unknown-doc2.md +8 -0
  131. vacancy_radar-0.1.0/tests/fixtures/vault/mockup-media/2025-01-13-mockup-media-unknown/documents/2025-01-13-mockup-media-unknown-doc3.md +8 -0
  132. vacancy_radar-0.1.0/tests/fixtures/vault/mockup-media/2025-01-13-mockup-media-unknown/documents/2025-01-13-mockup-media-unknown-doc4.md +8 -0
  133. vacancy_radar-0.1.0/tests/fixtures/vault/mockup-media/2025-01-13-mockup-media-unknown/documents/2025-01-13-mockup-media-unknown-doc5.md +8 -0
  134. vacancy_radar-0.1.0/tests/fixtures/vault/mockup-media/2025-01-13-mockup-media-unknown/documents/2025-01-13-mockup-media-unknown-doc6.md +8 -0
  135. vacancy_radar-0.1.0/tests/fixtures/vault/mockup-media/2025-01-13-mockup-media-unknown/vacancy.md +15 -0
  136. vacancy_radar-0.1.0/tests/fixtures/vault/mockup-media/2025-04-25-mockup-media-unknown/documents/2025-04-25-mockup-media-unknown-doc1.md +8 -0
  137. vacancy_radar-0.1.0/tests/fixtures/vault/mockup-media/2025-04-25-mockup-media-unknown/documents/2025-04-25-mockup-media-unknown-doc2.md +8 -0
  138. vacancy_radar-0.1.0/tests/fixtures/vault/mockup-media/2025-04-25-mockup-media-unknown/vacancy.md +14 -0
  139. vacancy_radar-0.1.0/tests/fixtures/vault/mockup-media/_company.md +9 -0
  140. vacancy_radar-0.1.0/tests/fixtures/vault/oduvanchik/2024-03-20-oduvanchik-data-analyst/documents/2024-03-20-oduvanchik-data-analyst-doc1.md +8 -0
  141. vacancy_radar-0.1.0/tests/fixtures/vault/oduvanchik/2024-03-20-oduvanchik-data-analyst/documents/2024-03-20-oduvanchik-data-analyst-doc2.md +9 -0
  142. vacancy_radar-0.1.0/tests/fixtures/vault/oduvanchik/2024-03-20-oduvanchik-data-analyst/documents/2024-03-20-oduvanchik-data-analyst-doc3.md +8 -0
  143. vacancy_radar-0.1.0/tests/fixtures/vault/oduvanchik/2024-03-20-oduvanchik-data-analyst/documents/2024-03-20-oduvanchik-data-analyst-doc4.md +8 -0
  144. vacancy_radar-0.1.0/tests/fixtures/vault/oduvanchik/2024-03-20-oduvanchik-data-analyst/documents/2024-03-20-oduvanchik-data-analyst-doc5.md +8 -0
  145. vacancy_radar-0.1.0/tests/fixtures/vault/oduvanchik/2024-03-20-oduvanchik-data-analyst/vacancy.md +14 -0
  146. vacancy_radar-0.1.0/tests/fixtures/vault/oduvanchik/_company.md +8 -0
  147. vacancy_radar-0.1.0/tests/fixtures/vault/sample-solutions/2022-08-05-sample-solutions-engineering-manager/documents/2022-08-05-sample-solutions-engineering-manager-doc1.md +8 -0
  148. vacancy_radar-0.1.0/tests/fixtures/vault/sample-solutions/2022-08-05-sample-solutions-engineering-manager/documents/2022-08-05-sample-solutions-engineering-manager-doc2.md +8 -0
  149. vacancy_radar-0.1.0/tests/fixtures/vault/sample-solutions/2022-08-05-sample-solutions-engineering-manager/documents/2022-08-05-sample-solutions-engineering-manager-doc3.md +8 -0
  150. vacancy_radar-0.1.0/tests/fixtures/vault/sample-solutions/2022-08-05-sample-solutions-engineering-manager/vacancy.md +15 -0
  151. vacancy_radar-0.1.0/tests/fixtures/vault/sample-solutions/_company.md +9 -0
  152. vacancy_radar-0.1.0/tests/fixtures/vault/sandbox-systems/2023-06-18-sandbox-systems-data-analyst/documents/2023-06-18-sandbox-systems-data-analyst-doc1.md +8 -0
  153. vacancy_radar-0.1.0/tests/fixtures/vault/sandbox-systems/2023-06-18-sandbox-systems-data-analyst/documents/2023-06-18-sandbox-systems-data-analyst-doc2.md +8 -0
  154. vacancy_radar-0.1.0/tests/fixtures/vault/sandbox-systems/2023-06-18-sandbox-systems-data-analyst/documents/2023-06-18-sandbox-systems-data-analyst-doc3.md +8 -0
  155. vacancy_radar-0.1.0/tests/fixtures/vault/sandbox-systems/2023-06-18-sandbox-systems-data-analyst/documents/2023-06-18-sandbox-systems-data-analyst-doc4.md +8 -0
  156. vacancy_radar-0.1.0/tests/fixtures/vault/sandbox-systems/2023-06-18-sandbox-systems-data-analyst/documents/2023-06-18-sandbox-systems-data-analyst-doc5.md +9 -0
  157. vacancy_radar-0.1.0/tests/fixtures/vault/sandbox-systems/2023-06-18-sandbox-systems-data-analyst/vacancy.md +15 -0
  158. vacancy_radar-0.1.0/tests/fixtures/vault/sandbox-systems/2023-06-20-sandbox-systems-backend-engineer/documents/2023-06-20-sandbox-systems-backend-engineer-doc1.md +9 -0
  159. vacancy_radar-0.1.0/tests/fixtures/vault/sandbox-systems/2023-06-20-sandbox-systems-backend-engineer/documents/2023-06-20-sandbox-systems-backend-engineer-doc2.md +8 -0
  160. vacancy_radar-0.1.0/tests/fixtures/vault/sandbox-systems/2023-06-20-sandbox-systems-backend-engineer/vacancy.md +14 -0
  161. vacancy_radar-0.1.0/tests/fixtures/vault/sandbox-systems/_company.md +8 -0
  162. vacancy_radar-0.1.0/tests/fixtures/vault/search-fixtures-corp/_company.md +7 -0
  163. vacancy_radar-0.1.0/tests/fixtures/vault/search-fixtures-corp/search-fixture-multi-a/documents/search-fixture-multi-a-doc1.md +8 -0
  164. vacancy_radar-0.1.0/tests/fixtures/vault/search-fixtures-corp/search-fixture-multi-a/vacancy.md +11 -0
  165. vacancy_radar-0.1.0/tests/fixtures/vault/search-fixtures-corp/search-fixture-multi-b/documents/search-fixture-multi-b-doc1.md +8 -0
  166. vacancy_radar-0.1.0/tests/fixtures/vault/search-fixtures-corp/search-fixture-multi-b/vacancy.md +11 -0
  167. vacancy_radar-0.1.0/tests/fixtures/vault/search-fixtures-corp/search-fixture-paraphrase/documents/search-fixture-paraphrase-doc1.md +8 -0
  168. vacancy_radar-0.1.0/tests/fixtures/vault/search-fixtures-corp/search-fixture-paraphrase/vacancy.md +11 -0
  169. vacancy_radar-0.1.0/tests/fixtures/vault/search-fixtures-corp/search-fixture-terms/documents/search-fixture-terms-doc1.md +8 -0
  170. vacancy_radar-0.1.0/tests/fixtures/vault/search-fixtures-corp/search-fixture-terms/documents/search-fixture-terms-doc2.md +8 -0
  171. vacancy_radar-0.1.0/tests/fixtures/vault/search-fixtures-corp/search-fixture-terms/documents/search-fixture-terms-doc3.md +8 -0
  172. vacancy_radar-0.1.0/tests/fixtures/vault/search-fixtures-corp/search-fixture-terms/vacancy.md +11 -0
  173. vacancy_radar-0.1.0/tests/fixtures/vault/vaporware-studio/2022-03-22-vaporware-studio-devops-engineer/documents/2022-03-22-vaporware-studio-devops-engineer-doc1.md +8 -0
  174. vacancy_radar-0.1.0/tests/fixtures/vault/vaporware-studio/2022-03-22-vaporware-studio-devops-engineer/documents/2022-03-22-vaporware-studio-devops-engineer-doc2.md +8 -0
  175. vacancy_radar-0.1.0/tests/fixtures/vault/vaporware-studio/2022-03-22-vaporware-studio-devops-engineer/vacancy.md +14 -0
  176. vacancy_radar-0.1.0/tests/fixtures/vault/vaporware-studio/2023-03-04-vaporware-studio-site-reliability-engineer/documents/2023-03-04-vaporware-studio-site-reliability-engineer-doc1.md +8 -0
  177. vacancy_radar-0.1.0/tests/fixtures/vault/vaporware-studio/2023-03-04-vaporware-studio-site-reliability-engineer/documents/2023-03-04-vaporware-studio-site-reliability-engineer-doc2.md +8 -0
  178. vacancy_radar-0.1.0/tests/fixtures/vault/vaporware-studio/2023-03-04-vaporware-studio-site-reliability-engineer/documents/2023-03-04-vaporware-studio-site-reliability-engineer-doc3.md +9 -0
  179. vacancy_radar-0.1.0/tests/fixtures/vault/vaporware-studio/2023-03-04-vaporware-studio-site-reliability-engineer/documents/2023-03-04-vaporware-studio-site-reliability-engineer-doc4.md +8 -0
  180. vacancy_radar-0.1.0/tests/fixtures/vault/vaporware-studio/2023-03-04-vaporware-studio-site-reliability-engineer/documents/2023-03-04-vaporware-studio-site-reliability-engineer-doc5.md +9 -0
  181. vacancy_radar-0.1.0/tests/fixtures/vault/vaporware-studio/2023-03-04-vaporware-studio-site-reliability-engineer/documents/2023-03-04-vaporware-studio-site-reliability-engineer-doc6.md +8 -0
  182. vacancy_radar-0.1.0/tests/fixtures/vault/vaporware-studio/2023-03-04-vaporware-studio-site-reliability-engineer/vacancy.md +15 -0
  183. vacancy_radar-0.1.0/tests/fixtures/vault/vaporware-studio/_company.md +8 -0
  184. vacancy_radar-0.1.0/tests/fixtures/vault/widget-works/2024-11-20-widget-works-machine-learning-engineer/documents/2024-11-20-widget-works-machine-learning-engineer-doc1.md +8 -0
  185. vacancy_radar-0.1.0/tests/fixtures/vault/widget-works/2024-11-20-widget-works-machine-learning-engineer/documents/2024-11-20-widget-works-machine-learning-engineer-doc2.md +8 -0
  186. vacancy_radar-0.1.0/tests/fixtures/vault/widget-works/2024-11-20-widget-works-machine-learning-engineer/documents/2024-11-20-widget-works-machine-learning-engineer-doc3.md +8 -0
  187. vacancy_radar-0.1.0/tests/fixtures/vault/widget-works/2024-11-20-widget-works-machine-learning-engineer/documents/2024-11-20-widget-works-machine-learning-engineer-doc4.md +8 -0
  188. vacancy_radar-0.1.0/tests/fixtures/vault/widget-works/2024-11-20-widget-works-machine-learning-engineer/documents/2024-11-20-widget-works-machine-learning-engineer-doc5.md +8 -0
  189. vacancy_radar-0.1.0/tests/fixtures/vault/widget-works/2024-11-20-widget-works-machine-learning-engineer/vacancy.md +14 -0
  190. vacancy_radar-0.1.0/tests/fixtures/vault/widget-works/2025-09-08-widget-works-frontend-developer/documents/2025-09-08-widget-works-frontend-developer-doc1.md +8 -0
  191. vacancy_radar-0.1.0/tests/fixtures/vault/widget-works/2025-09-08-widget-works-frontend-developer/documents/2025-09-08-widget-works-frontend-developer-doc2.md +8 -0
  192. vacancy_radar-0.1.0/tests/fixtures/vault/widget-works/2025-09-08-widget-works-frontend-developer/documents/2025-09-08-widget-works-frontend-developer-doc3.md +9 -0
  193. vacancy_radar-0.1.0/tests/fixtures/vault/widget-works/2025-09-08-widget-works-frontend-developer/documents/2025-09-08-widget-works-frontend-developer-doc4.md +8 -0
  194. vacancy_radar-0.1.0/tests/fixtures/vault/widget-works/2025-09-08-widget-works-frontend-developer/documents/2025-09-08-widget-works-frontend-developer-doc5.md +9 -0
  195. vacancy_radar-0.1.0/tests/fixtures/vault/widget-works/2025-09-08-widget-works-frontend-developer/vacancy.md +15 -0
  196. vacancy_radar-0.1.0/tests/fixtures/vault/widget-works/_company.md +8 -0
  197. vacancy_radar-0.1.0/tests/index/test_build.py +552 -0
  198. vacancy_radar-0.1.0/tests/index/test_embed.py +77 -0
  199. vacancy_radar-0.1.0/tests/index/test_extract.py +224 -0
  200. vacancy_radar-0.1.0/tests/index/test_schema.py +82 -0
  201. vacancy_radar-0.1.0/tests/index/test_search.py +1006 -0
  202. vacancy_radar-0.1.0/tests/index/test_snippet.py +68 -0
  203. vacancy_radar-0.1.0/tests/index/test_store.py +49 -0
  204. vacancy_radar-0.1.0/tests/test_cli.py +1301 -0
  205. vacancy_radar-0.1.0/tests/test_config.py +225 -0
  206. vacancy_radar-0.1.0/tests/test_core.py +1920 -0
  207. vacancy_radar-0.1.0/tests/test_frontmatter.py +70 -0
  208. vacancy_radar-0.1.0/tests/test_ids.py +98 -0
  209. vacancy_radar-0.1.0/tests/test_import.py +38 -0
  210. vacancy_radar-0.1.0/tests/test_mcp_server.py +1449 -0
  211. vacancy_radar-0.1.0/tests/test_models.py +354 -0
  212. vacancy_radar-0.1.0/tests/test_packaging.py +125 -0
  213. vacancy_radar-0.1.0/tests/test_version.py +7 -0
  214. vacancy_radar-0.1.0/tests/vault/test_hashing.py +41 -0
  215. vacancy_radar-0.1.0/tests/vault/test_paths.py +93 -0
  216. vacancy_radar-0.1.0/tests/vault/test_reader.py +717 -0
@@ -0,0 +1,12 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+ dist/
7
+ *.egg-info/
8
+ .index/
9
+ .env
10
+ # A locally-configured vault at the repo root only - not the committed
11
+ # test fixture tree at tests/fixtures/vault/.
12
+ /vault/
@@ -0,0 +1,66 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format
4
+ follows [Keep a Changelog](https://keepachangelog.com/), and this
5
+ project uses [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [0.1.0] - 2026-08-10
8
+
9
+ First release: a local-first job application tracker built around a
10
+ markdown vault, with hybrid search and an MCP server.
11
+
12
+ ### Added
13
+
14
+ - **Vault model and reader.** A markdown vault is the source of truth —
15
+ typed frontmatter, stable derived ids, company and vacancy documents,
16
+ and a reader that collects warnings rather than failing on a single
17
+ malformed file. `vacancy-radar init` creates or idempotently adopts a
18
+ vault directory without touching existing content.
19
+ - **Derived LanceDB index.** Chunked documents with BM25 and vector
20
+ columns, rebuilt from the vault by `vacancy-radar reindex`; every
21
+ write reindexes its own effect eagerly, so results are searchable
22
+ immediately.
23
+ - **Hybrid retrieval.** Keyword, semantic, and hybrid search modes with
24
+ configurable fusion. The shipped defaults — linear-combination fusion
25
+ at `vector_weight` 0.5 — were chosen from measurement, reversing the
26
+ design's original RRF guess.
27
+ - **Evaluation harness** (`vacancy-radar eval`): 30 labelled queries
28
+ across seven categories scored with recall@k and MRR against the
29
+ committed fixture vault. See [docs/evaluation.md](docs/evaluation.md).
30
+ - **Read CLI**: `list`, `show`, `search`, and `doctor`, each with a Rich
31
+ table for humans and a stable `--json` shape for piping.
32
+ - **Write CLI**: `new`, `note`, `status set`, `add`, and `reindex`.
33
+ Every write is append-only or a validated status transition; there is
34
+ no delete command anywhere in the CLI.
35
+ - **PDF sidecar extraction** (`reindex --extract-sidecars`): extracts
36
+ text from PDFs under `documents/` into git-tracked `.pdf.md`
37
+ sidecars, so they are searchable by content. An encrypted or
38
+ unparseable PDF warns and is skipped rather than failing the reindex.
39
+ - **MCP server** (`vacancy-radar-mcp`): eleven tools matching `core.py`
40
+ one for one, two resources, and three prompts, over stdio for any MCP
41
+ client. Typed errors surface as structured results a calling model
42
+ can act on, never a crash.
43
+ - **Conversational import** via `find_existing` + `create_vacancy`, for
44
+ moving an existing tracker into a vault by talking to a model. See
45
+ [docs/import-guide.md](docs/import-guide.md).
46
+ - **Published to PyPI.** `uvx vacancy-radar` and `uv tool install
47
+ vacancy-radar` expose the CLI, and `uvx --from vacancy-radar
48
+ vacancy-radar-mcp` the MCP server — no clone and no `uv.lock`
49
+ required. The distribution carries full landing-page metadata and an
50
+ SPDX `MIT` license expression, with the LICENSE file shipped in both
51
+ the wheel and the sdist.
52
+ - **Release automation** (`.github/workflows/release.yml`): a
53
+ tag-triggered release that builds once, verifies the built wheel by
54
+ installing and running it in a clean environment with no repository
55
+ checkout, and only then uploads it via PyPI Trusted Publishing. No
56
+ index credential is stored anywhere. A `workflow_dispatch` input runs
57
+ the same chain against TestPyPI as a dry run. See
58
+ [docs/releasing.md](docs/releasing.md).
59
+ - **Packaging test** (`tests/test_packaging.py`): asserts the built
60
+ distribution's entry points, license file, and landing-page metadata
61
+ on every test run, so a packaging regression fails CI instead of
62
+ degrading a release.
63
+ - CI on pushes to `main` and pull requests: pre-commit hooks and the
64
+ full test suite, with the embedding model cached by model name.
65
+
66
+ [0.1.0]: https://github.com/Akay7/vacancy-radar/releases/tag/v0.1.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Egor Poderiagin
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.
@@ -0,0 +1,449 @@
1
+ Metadata-Version: 2.4
2
+ Name: vacancy-radar
3
+ Version: 0.1.0
4
+ Summary: Local-first job application tracker with hybrid search and an MCP server.
5
+ Project-URL: Homepage, https://github.com/Akay7/vacancy-radar
6
+ Project-URL: Repository, https://github.com/Akay7/vacancy-radar
7
+ Project-URL: Issues, https://github.com/Akay7/vacancy-radar/issues
8
+ Author-email: Egor Poderiagin <egor@crazyrussian.pro>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: hybrid-search,job-search,job-tracker,lancedb,markdown,mcp
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: End Users/Desktop
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Office/Business
19
+ Classifier: Topic :: Text Processing :: Indexing
20
+ Requires-Python: >=3.12
21
+ Requires-Dist: fastembed>=0.8.0
22
+ Requires-Dist: lancedb>=0.36.0
23
+ Requires-Dist: mcp>=2.0.0
24
+ Requires-Dist: pydantic>=2.13.4
25
+ Requires-Dist: pypdf>=6.14.2
26
+ Requires-Dist: rich>=15.0.0
27
+ Requires-Dist: ruamel-yaml>=0.19.1
28
+ Requires-Dist: text-unidecode>=1.3
29
+ Requires-Dist: typer>=0.27.1
30
+ Description-Content-Type: text/markdown
31
+
32
+ # vacancy-radar
33
+
34
+ A local-first job application tracker. A markdown vault is the source of
35
+ truth; a derived LanceDB index provides hybrid BM25 + vector search; an MCP
36
+ server exposes read/write tools to any LLM client.
37
+
38
+ ## Status
39
+
40
+ Vault, index, retrieval, CLI (read and write), and MCP server are all
41
+ implemented. Stage 9 (import) is too — conversationally, via
42
+ `create_vacancy`/`find_existing` over MCP (see
43
+ [docs/import-guide.md](docs/import-guide.md)), not an xlsx/CSV
44
+ importer; this project ships no spreadsheet parser or interchange
45
+ format.
46
+
47
+ ## How CLI and MCP fit together
48
+
49
+ Two front doors, one back end: a human runs the CLI, an LLM client
50
+ calls MCP tools, and both go through the same `core.py` functions onto
51
+ the same markdown vault and LanceDB index — neither interface has any
52
+ capability, or any data, the other doesn't see.
53
+
54
+ ```mermaid
55
+ flowchart TD
56
+ subgraph CLI["Human — terminal (vacancy-radar CLI)"]
57
+ direction TB
58
+ C1["init\ncreate the vault"]
59
+ C2["new\ncreate a vacancy"]
60
+ C3["add / note\nattach documents & notes"]
61
+ C4["status set\nmove it through the pipeline"]
62
+ C5["list / show / search\nread it back"]
63
+ C1 --> C2 --> C3 --> C4 --> C5
64
+ end
65
+
66
+ subgraph MCP["LLM client — MCP tools (vacancy-radar-mcp)"]
67
+ direction TB
68
+ M1["find_existing\ncheck before creating"]
69
+ M2["create_vacancy\n(conversational import)"]
70
+ M3["add_document / append_note"]
71
+ M4["set_status"]
72
+ M5["list_vacancies / get_vacancy / search"]
73
+ M1 --> M2 --> M3 --> M4 --> M5
74
+ end
75
+
76
+ C5 --> Vault[("Markdown vault\n+ LanceDB index")]
77
+ M5 --> Vault
78
+ ```
79
+
80
+ `init` and `reindex` are CLI-only (see their sections below for why);
81
+ everything else on both sides is the same underlying operation,
82
+ exposed twice. The CLI trusts the human typing the command; the MCP
83
+ tools add the extra checks a model calling them unsupervised needs —
84
+ `create_vacancy` never deduplicates on its own (call `find_existing`
85
+ first), and every typed error comes back as something the model reads
86
+ and can act on in the same turn, not a crash.
87
+
88
+ ## Install
89
+
90
+ ### Use it
91
+
92
+ You need [uv](https://docs.astral.sh/uv/). Nothing else — no clone, no
93
+ `uv.lock`.
94
+
95
+ ```bash
96
+ # Run it once, without installing anything permanently.
97
+ uvx vacancy-radar --version
98
+
99
+ # Or put `vacancy-radar` on PATH for good.
100
+ uv tool install vacancy-radar
101
+ ```
102
+
103
+ The MCP server ships in the same distribution, but its script name
104
+ differs from the distribution name, so it needs `--from` (see
105
+ [MCP server](#mcp-server) below for the client config):
106
+
107
+ ```bash
108
+ uvx --from vacancy-radar vacancy-radar-mcp --vault /path/to/vault
109
+ ```
110
+
111
+ **Expect the first run to be slow.** The dependency set is large
112
+ (`lancedb` and `fastembed` are hundreds of megabytes resolved), and the
113
+ first `search` downloads an embedding model on top of that. Both are
114
+ cached afterward — a slow first run is the download, not a hang.
115
+
116
+ ### Work on it
117
+
118
+ For contributing to vacancy-radar itself, from a clone:
119
+
120
+ ```bash
121
+ uv sync --all-groups
122
+ ```
123
+
124
+ Every command example below is written in this contributor form, with a
125
+ `uv run` prefix. **If you installed the tool, drop the `uv run`** —
126
+ `uv run vacancy-radar list` becomes `vacancy-radar list`.
127
+
128
+ ## Getting started
129
+
130
+ Every command below expects a vault to already exist at `VAULT_PATH`
131
+ (or an explicit path, where the command takes one). Create one first:
132
+
133
+ ```bash
134
+ uv run vacancy-radar init /path/to/vault
135
+ export VAULT_PATH=/path/to/vault
136
+ ```
137
+
138
+ `init` is idempotent - running it again against the same path is a
139
+ no-op, not an error - and it never touches anything already in a
140
+ directory it adopts (see [design.md](openspec/changes/vault-init/
141
+ design.md) for the full rationale).
142
+
143
+ ## Usage
144
+
145
+ ```bash
146
+ uv run vacancy-radar --version
147
+ ```
148
+
149
+ `list`, `show`, `search`, and `doctor` are read-only: they never write to
150
+ the vault or the index. Every command prints a Rich table for humans by
151
+ default; add `--json` for a machine-readable, stable, documented shape
152
+ suitable for piping (see `openspec/changes/archive/2026-08-06-cli-read/
153
+ design.md`'s `--json` contract).
154
+
155
+ ```bash
156
+ # List vacancies, optionally filtered (--status, --company, --since,
157
+ # --needs-review, --stale-days), sorted by last activity.
158
+ uv run vacancy-radar list --status applied --status interview
159
+
160
+ # Show one vacancy or company - which kind it is is auto-detected.
161
+ uv run vacancy-radar show acme-corp
162
+
163
+ # Search the vault's index (see "Retrieval evaluation" below);
164
+ # --scores prints each hit's keyword/semantic rank and fused score.
165
+ uv run vacancy-radar search "backend engineer" --scores
166
+
167
+ # Report vault and index health: marker, index freshness, embedding
168
+ # model availability, and every warning collected while reading.
169
+ uv run vacancy-radar doctor
170
+ ```
171
+
172
+ ### Writing
173
+
174
+ `note`, `status set`, `add`, `new`, and `reindex` write to the vault.
175
+ Every write is append-only or a validated status transition (see
176
+ `openspec/changes/archive/2026-08-06-cli-write/design.md`) - nothing
177
+ here deletes or overwrites existing content, and there is no delete
178
+ command anywhere in the CLI. `note`, `status set`, and `add` accept a
179
+ vacancy by id, company name, or unambiguous fuzzy match, resolved the
180
+ same way `show` resolves its argument; an ambiguous match prints the
181
+ candidates and exits non-zero without writing anything. `--json`
182
+ follows the same stable, documented-shape convention as the read
183
+ commands above.
184
+
185
+ ```bash
186
+ # A vacancy argument is an id, a company name/slug (if it resolves to
187
+ # exactly one vacancy), or an unambiguous fuzzy match - the same rule
188
+ # `show` uses. A company with several vacancies needs the vacancy id.
189
+
190
+ # Append a dated note. The filename is always derived from the date -
191
+ # never something you (or an LLM, at a later stage) supply.
192
+ uv run vacancy-radar note 2022-03-22-acme-corp-backend-engineer \
193
+ "Called the recruiter, no news yet."
194
+
195
+ # Move a vacancy's status. Backward transitions need --force; the first
196
+ # move off 'found' needs --applied-date.
197
+ uv run vacancy-radar status set 2022-03-22-acme-corp-backend-engineer \
198
+ applied --applied-date 2026-01-15
199
+ uv run vacancy-radar status set 2022-03-22-acme-corp-backend-engineer \
200
+ found --force
201
+
202
+ # File a *text* document (posting, email, note, or takehome) from a
203
+ # file or stdin. --external-id makes re-filing the same source (e.g.
204
+ # the same email thread) a no-op instead of a duplicate. `--file`/
205
+ # `--stdin` read UTF-8 text - binary content (a PDF) fails with a
206
+ # clear error naming the alternative below, not a crash.
207
+ uv run vacancy-radar add 2022-03-22-acme-corp-backend-engineer \
208
+ --kind email --title "Recruiter reply" --date 2026-01-16 \
209
+ --external-id gmail-thread-42 --stdin < email.txt
210
+
211
+ # Create a new vacancy (and its company directory, if new). --url is
212
+ # optional - omit it when the source has no link at all (e.g. a
213
+ # recruiter sent only a PDF attachment).
214
+ uv run vacancy-radar new --company "Acme Corp" --role "Backend Engineer" \
215
+ --url https://jobs.acme.example/1 --found-date 2026-01-10
216
+
217
+ # Rebuild the index from the vault's current state. Not required for
218
+ # ordinary use - every write above already reindexes its own effect
219
+ # eagerly, before returning, so it's searchable immediately.
220
+ uv run vacancy-radar reindex
221
+
222
+ # Extract text from any PDF under documents/ into a git-tracked
223
+ # <filename>.pdf.md sidecar, so it's searchable by content, not only
224
+ # by filename. Off by default - a build never silently creates sidecar
225
+ # files; pass this explicitly whenever a PDF was added or changed.
226
+ uv run vacancy-radar reindex --extract-sidecars
227
+ ```
228
+
229
+ #### Attaching a PDF
230
+
231
+ There is no CLI command for filing a PDF the way `add` files text -
232
+ copy it directly into the vacancy's `documents/` directory, then
233
+ reindex with extraction:
234
+
235
+ ```bash
236
+ cp ~/Downloads/posting.pdf \
237
+ /path/to/vault/acme-corp/2022-03-22-acme-corp-backend-engineer/documents/
238
+ uv run vacancy-radar reindex --extract-sidecars
239
+ ```
240
+
241
+ This registers the PDF as a document (`kind: takehome`, `title` set to
242
+ the filename, `date` set to the vacancy's `found_date` - none
243
+ independently settable this way; a posting or interview-prep PDF is
244
+ filed the same way, just under that `kind`) and writes a
245
+ `<filename>.pdf.md` sidecar containing its extracted text, so it's
246
+ searchable by content afterward. An encrypted or unparseable PDF fails
247
+ extraction without crashing the reindex - a warning naming the file
248
+ prints in `reindex`'s output (and appears under `--json`'s `warnings`
249
+ field too).
250
+
251
+ ## MCP server
252
+
253
+ `vacancy-radar-mcp` exposes the same `core.py` functions the CLI above
254
+ calls, as MCP tools/resources/prompts over stdio, for any MCP client
255
+ (Claude Desktop, etc.) - see `openspec/changes/mcp-server/design.md`
256
+ for the full rationale.
257
+
258
+ Point your client at the published distribution — no clone required.
259
+ For Claude Desktop, that is `claude_desktop_config.json`:
260
+
261
+ ```json
262
+ {
263
+ "mcpServers": {
264
+ "vacancy-radar": {
265
+ "command": "uvx",
266
+ "args": [
267
+ "--from", "vacancy-radar",
268
+ "vacancy-radar-mcp",
269
+ "--vault", "/path/to/vault"
270
+ ]
271
+ }
272
+ }
273
+ }
274
+ ```
275
+
276
+ `--from vacancy-radar` is required, not decorative: `uvx` resolves a
277
+ *distribution* named after the command it is given, and the script here
278
+ is `vacancy-radar-mcp` while the distribution is `vacancy-radar`. A
279
+ bare `uvx vacancy-radar-mcp` looks for a distribution that does not
280
+ exist.
281
+
282
+ For Claude Code, register the same command with `claude mcp add`
283
+ instead of editing JSON by hand:
284
+
285
+ ```bash
286
+ claude mcp add vacancy-radar -- \
287
+ uvx --from vacancy-radar vacancy-radar-mcp --vault /path/to/vault
288
+ ```
289
+
290
+ If you have installed it persistently with `uv tool install
291
+ vacancy-radar`, `vacancy-radar-mcp` is already on PATH and the config's
292
+ `command` can be that directly, with just `["--vault", "/path/to/vault"]`
293
+ as args.
294
+
295
+ Working on the server itself, from a clone (after `uv sync
296
+ --all-groups`):
297
+
298
+ ```bash
299
+ uv run vacancy-radar-mcp --vault /path/to/vault
300
+ ```
301
+
302
+ **Vault path**: `--vault` if given, else the `VAULT_PATH` environment
303
+ variable; neither present fails startup naming both. MCP's client-
304
+ supplied-roots capability is deliberately not used here - it is
305
+ deprecated as of the protocol revision the MCP Python SDK speaks by
306
+ default (2026-07-28, SEP-2577), so `--vault`/`VAULT_PATH` are the only
307
+ two ways to point the server at a vault, exactly like configuring any
308
+ other stdio MCP server.
309
+
310
+ **Eleven tools**, matching `core.py`'s functions one for one - argument
311
+ marshalling only, no filtering/sorting/business logic of its own:
312
+
313
+ | tool | delegates to |
314
+ | ---- | ------------- |
315
+ | `list_vacancies` | `core.list_vacancies` |
316
+ | `get_company` | `core.get_company` |
317
+ | `get_vacancy` | `core.get_vacancy` |
318
+ | `search` | `core.search` |
319
+ | `read_document` | `core.read_document` |
320
+ | `resolve` | `core.resolve` |
321
+ | `find_existing` | `core.find_existing` |
322
+ | `append_note` | `core.append_note` |
323
+ | `set_status` | `core.set_status` |
324
+ | `add_document` | `core.add_document` |
325
+ | `create_vacancy` | `core.create_vacancy` |
326
+
327
+ `reindex` is deliberately **not** exposed - every write tool already
328
+ refreshes the index itself, and there's no legitimate reason for a
329
+ client-connected model to trigger a full rebuild (design.md's exposure
330
+ decision). `create_vacancy` *is* exposed, for conversational import
331
+ (see [docs/import-guide.md](docs/import-guide.md)): it does not
332
+ deduplicate, so its own tool description tells a calling model to call
333
+ `find_existing` first and only create when that returns no match.
334
+ `search`'s description tells the model to prefer `mode="keyword"` for
335
+ exact-name/proper-noun queries and leave `mode` unset (hybrid)
336
+ otherwise, citing the measured numbers in "Retrieval evaluation" below
337
+ - without that guidance a model defaults to hybrid for everything and
338
+ the per-category tuning goes unused. A typed `core.py` error (an
339
+ ambiguous match, an invalid status transition, a nonexistent vacancy)
340
+ surfaces as a structured tool result the calling model can read and
341
+ act on, never a raw crash.
342
+
343
+ **Two resources**: `vacancy-radar://companies` (every company's slug,
344
+ display name, and vacancy count) and `vacancy-radar://statuses` (every
345
+ status and the transitions permitted from it, read live from
346
+ `models.py`).
347
+
348
+ **Three prompts**: `prep_interview(company)` (dossier, postings, prior
349
+ notes, take-homes), `draft_reply(vacancy_id)` (prior correspondence, to
350
+ match tone), and `weekly_review()` (stale applications, pending
351
+ take-homes, vacancies tagged `needs_review`). An unresolved or
352
+ ambiguous `company`/`vacancy_id` returns a message saying so - never a
353
+ fabricated dossier.
354
+
355
+ ## Development
356
+
357
+ ```bash
358
+ uv run pytest
359
+ uv run ruff check .
360
+ uv run ruff format --check .
361
+ ```
362
+
363
+ ### Pre-commit hooks
364
+
365
+ Install the git hooks once per clone so lint/format issues are caught
366
+ before you commit (the same checks CI runs):
367
+
368
+ ```bash
369
+ uv run pre-commit install
370
+ ```
371
+
372
+ Run them on demand against all files:
373
+
374
+ ```bash
375
+ uv run pre-commit run --all-files
376
+ ```
377
+
378
+ ## Retrieval evaluation
379
+
380
+ Measured with `eval/`'s harness: 30 labelled queries (`eval/queries.yaml`,
381
+ across seven categories) run against the committed fixture vault
382
+ (`tests/fixtures/vault/`), scored with `recall_at_k`/`mrr` over each
383
+ mode's top 5 results. Reproduce it with `uv run vacancy-radar eval`.
384
+
385
+ **Overall, by mode:**
386
+
387
+ | mode | vector_weight | fusion | recall@5 | MRR |
388
+ | ---- | -------------- | ------------------- | -------- | ----- |
389
+ | keyword | — | — | 0.761 | 0.675 |
390
+ | semantic | — | — | 0.678 | 0.583 |
391
+ | hybrid | 0.5 | rrf | 0.672 | 0.541 |
392
+ | hybrid (**shipped default**) | 0.5 | linear_combination | 0.794 | 0.687 |
393
+
394
+ **Per category, keyword vs. semantic vs. the shipped hybrid default (recall@5):**
395
+
396
+ | category | keyword | semantic | hybrid (0.5, linear_combination) |
397
+ | ------------ | ----- | ----- | ----- |
398
+ | exact_name | 1.000 | 0.800 | 1.000 |
399
+ | technical | 0.800 | 0.800 | 1.000 |
400
+ | paraphrase | 0.250 | 0.750 | 0.250 |
401
+ | temporal | 1.000 | 0.250 | 1.000 |
402
+ | multi_result | 0.417 | 0.250 | 0.417 |
403
+ | filtered | 0.792 | 0.833 | 0.792 |
404
+ | negative | 1.000 | 1.000 | 1.000 |
405
+ | **overall** | **0.761** | **0.678** | **0.794** |
406
+
407
+ Keyword dominates `exact_name` (company/recruiter proper nouns) exactly
408
+ as hypothesized at stage 4, and semantic strictly dominates `paraphrase`
409
+ (queries sharing no content word with their target) — the one category
410
+ keyword structurally cannot serve at all. Both single modes are weak on
411
+ `multi_result` and one of them is weak on `temporal`, which top-5
412
+ recall@5 punishes when several correct documents compete for five
413
+ slots. **The fusion strategy default did not survive contact with
414
+ measurement**: RRF (stage 4's original default) *underperforms plain
415
+ keyword search* on this corpus — 0.672 vs. 0.761 overall recall@5,
416
+ identical across every `vector_weight` since RRF is rank-based and
417
+ weight-agnostic — while Linear Combination fusion at `vector_weight`
418
+ 0.3–0.6 ties for the best result on every metric checked (0.3–0.6 score
419
+ identically; 0.7 ties on recall@5 but drops on MRR). `default_vector_weight`
420
+ is set to `0.5`, the middle of that tied band, and `fusion_strategy` to
421
+ `LINEAR_COMBINATION` — both a reversal of stage 4's guess, not a
422
+ confirmation of it. See `config.py`'s `SearchConfig` docstring and this
423
+ change's `tasks.md` (section 5) for the full per-category sweep.
424
+
425
+ **Limitations.** These numbers characterize the retrieval *design*
426
+ against a synthetic, generator-produced fixture vault — not a real
427
+ corpus of job-search correspondence. `tests/fixtures/generate.py`'s
428
+ proper nouns, phrasing patterns, and document lengths are artifacts of
429
+ the generator, not a sample of real vacancies, emails, or notes. Treat
430
+ this table as justification for a *default*, not a claim that
431
+ generalizes. See [docs/evaluation.md](docs/evaluation.md) for the
432
+ procedure to run a second eval against your own real vault after
433
+ import, and for which numbers to trust when retuning
434
+ `SEARCH_DEFAULT_VECTOR_WEIGHT`/`SEARCH_FUSION_STRATEGY` for your own
435
+ deployment.
436
+
437
+ **Future work, not built here.** `multi_result` and `temporal` recall
438
+ suggest a reranker could help beyond what fusion-weight tuning alone
439
+ achieves — not implemented in this change per its constraints. Whether
440
+ `negative`-category queries should return a score-floor-suppressed
441
+ empty result instead of five low-relevance ones is a real product
442
+ question, deliberately left open (see design.md): it would change
443
+ `search()`'s return contract and belongs in its own change if a real
444
+ vault's negative-query scores ever turn out close enough to real hits
445
+ to mislead.
446
+
447
+ ## License
448
+
449
+ MIT — see [LICENSE](LICENSE).