codebase-receipts-cli 1.0.0__tar.gz → 1.0.1__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 (145) hide show
  1. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/CHANGELOG.md +16 -0
  2. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/PKG-INFO +1 -1
  3. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/pyproject.toml +1 -1
  4. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/cli.py +18 -0
  5. codebase_receipts_cli-1.0.1/tests/test_setup_config.py +97 -0
  6. codebase_receipts_cli-1.0.0/tests/test_setup_config.py +0 -47
  7. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/.env.example +0 -0
  8. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/.github/workflows/ci.yml +0 -0
  9. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/.github/workflows/publish.yml +0 -0
  10. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/.gitignore +0 -0
  11. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/.pre-commit-config.yaml +0 -0
  12. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/CLAUDE.md +0 -0
  13. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/LEARN.md +0 -0
  14. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/LICENSE +0 -0
  15. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/README.md +0 -0
  16. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/SETUP.md +0 -0
  17. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/__init__.py +0 -0
  18. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/ama/__init__.py +0 -0
  19. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/ama/interviewer.py +0 -0
  20. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/ats/__init__.py +0 -0
  21. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/ats/comparator.py +0 -0
  22. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/ats/scorer.py +0 -0
  23. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/ats/stats.py +0 -0
  24. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/config.py +0 -0
  25. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/errors.py +0 -0
  26. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/export.py +0 -0
  27. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/ingest/__init__.py +0 -0
  28. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/ingest/artifact_extractor.py +0 -0
  29. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/ingest/git_source.py +0 -0
  30. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/ingest/manifest.py +0 -0
  31. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/ingest/scanner.py +0 -0
  32. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/ingest/secrets_scanner.py +0 -0
  33. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/interactive/__init__.py +0 -0
  34. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/interactive/repl.py +0 -0
  35. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/ledger/__init__.py +0 -0
  36. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/ledger/pricing_table.py +0 -0
  37. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/ledger/token_ledger.py +0 -0
  38. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/llm/__init__.py +0 -0
  39. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/llm/anthropic_provider.py +0 -0
  40. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/llm/factory.py +0 -0
  41. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/llm/fake_provider.py +0 -0
  42. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/llm/gemini_provider.py +0 -0
  43. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/llm/json_utils.py +0 -0
  44. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/llm/metered.py +0 -0
  45. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/llm/ollama_provider.py +0 -0
  46. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/llm/openai_provider.py +0 -0
  47. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/llm/provider.py +0 -0
  48. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/llm/split.py +0 -0
  49. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/llm/token_estimate.py +0 -0
  50. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/mine/__init__.py +0 -0
  51. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/mine/code_metrics.py +0 -0
  52. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/mine/git_evidence.py +0 -0
  53. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/prep/__init__.py +0 -0
  54. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/prep/dossier.py +0 -0
  55. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/prep/readiness.py +0 -0
  56. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/resume/__init__.py +0 -0
  57. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/resume/claim_extractor.py +0 -0
  58. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/resume/loader.py +0 -0
  59. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/resume/pdf_parser.py +0 -0
  60. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/resume/tex_parser.py +0 -0
  61. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/rewrite/__init__.py +0 -0
  62. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/rewrite/compiler.py +0 -0
  63. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/rewrite/optimizer.py +0 -0
  64. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/rewrite/tex_rewriter.py +0 -0
  65. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/tui/__init__.py +0 -0
  66. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/tui/ama_app.py +0 -0
  67. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/tui/app.py +0 -0
  68. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/tui/dossier_app.py +0 -0
  69. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/tui/hub.py +0 -0
  70. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/tui/ingest_app.py +0 -0
  71. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/tui/rewrite_app.py +0 -0
  72. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/tui/score_app.py +0 -0
  73. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/tui/widgets/__init__.py +0 -0
  74. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/tui/widgets/claims_table.py +0 -0
  75. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/tui/widgets/diff_view.py +0 -0
  76. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/tui/widgets/status_bar.py +0 -0
  77. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/verify/__init__.py +0 -0
  78. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/verify/bm25.py +0 -0
  79. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/verify/enrich.py +0 -0
  80. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/verify/jd_fetch.py +0 -0
  81. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/verify/kb.py +0 -0
  82. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/verify/keyword_gap.py +0 -0
  83. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/verify/query_expand.py +0 -0
  84. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/verify/rerank.py +0 -0
  85. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/verify/rewriter.py +0 -0
  86. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/verify/router.py +0 -0
  87. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/verify/summaries.py +0 -0
  88. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/receipts/verify/verifier.py +0 -0
  89. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/__init__.py +0 -0
  90. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/conftest.py +0 -0
  91. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_artifact_extractor.py +0 -0
  92. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_ats_comparator.py +0 -0
  93. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_ats_keyword_fix.py +0 -0
  94. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_ats_scorer.py +0 -0
  95. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_ats_stats.py +0 -0
  96. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_ats_v2.py +0 -0
  97. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_batch_parser_hardening.py +0 -0
  98. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_claim_extractor.py +0 -0
  99. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_cli.py +0 -0
  100. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_code_metrics.py +0 -0
  101. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_compiler.py +0 -0
  102. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_config.py +0 -0
  103. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_dossier.py +0 -0
  104. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_entry_filter.py +0 -0
  105. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_export.py +0 -0
  106. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_factory.py +0 -0
  107. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_fake_provider.py +0 -0
  108. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_gemini_provider.py +0 -0
  109. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_git_evidence.py +0 -0
  110. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_git_source.py +0 -0
  111. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_hybrid_retrieval.py +0 -0
  112. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_ingest_cli.py +0 -0
  113. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_interactive.py +0 -0
  114. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_interview_styles.py +0 -0
  115. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_interviewer.py +0 -0
  116. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_json_utils.py +0 -0
  117. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_kb.py +0 -0
  118. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_keyword_gap.py +0 -0
  119. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_language_coverage.py +0 -0
  120. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_ledger.py +0 -0
  121. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_metered_provider.py +0 -0
  122. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_new_tuis.py +0 -0
  123. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_openai_base_url.py +0 -0
  124. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_optimizer.py +0 -0
  125. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_pdf_parser.py +0 -0
  126. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_provider_contract.py +0 -0
  127. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_readiness.py +0 -0
  128. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_remote_sources.py +0 -0
  129. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_retrieval_upgrades.py +0 -0
  130. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_rewriter.py +0 -0
  131. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_rewriter_grounding.py +0 -0
  132. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_router.py +0 -0
  133. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_scanner.py +0 -0
  134. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_scanner_gitignored_venv.py +0 -0
  135. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_secrets_scanner.py +0 -0
  136. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_split_provider.py +0 -0
  137. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_tex_identity_replacement.py +0 -0
  138. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_tex_parser.py +0 -0
  139. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_tex_rewriter.py +0 -0
  140. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_tui.py +0 -0
  141. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_tui_rewrite.py +0 -0
  142. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_tui_score.py +0 -0
  143. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_verifier.py +0 -0
  144. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/tests/test_verifier_batch.py +0 -0
  145. {codebase_receipts_cli-1.0.0 → codebase_receipts_cli-1.0.1}/uv.lock +0 -0
@@ -8,6 +8,22 @@
8
8
 
9
9
  ---
10
10
 
11
+ ## `1.0.1` — 2026-07-18
12
+
13
+ > Patch release — one real bug, found by using the published package.
14
+
15
+ ### Fixes
16
+
17
+ - `receipts setup`'s Gemini branch never set `RECEIPTS_EMBED_PROVIDER`, so a
18
+ project first ingested under Ollama and later switched to Gemini would hit
19
+ `InvalidArgumentError` from ChromaDB on the next ingest (a collection is
20
+ dimension-locked to whichever model first embedded into it — Ollama's
21
+ `nomic-embed-text` is 768-dim, Gemini's embedding model is 3072-dim). The
22
+ wizard now asks (default yes) whether to route embeddings through Ollama
23
+ instead, mirroring the existing Anthropic-branch prompt.
24
+
25
+ ---
26
+
11
27
  ## `1.0.0` — 2026-07-17
12
28
 
13
29
  > Phases 8 through 23. Multi-repo, scale, remote sources, universal
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codebase-receipts-cli
3
- Version: 1.0.0
3
+ Version: 1.0.1
4
4
  Summary: Receipts — resume claim verifier & codebase-grounded interview prep CLI
5
5
  Project-URL: Homepage, https://github.com/Ishaan-1606/receipts-cli
6
6
  Project-URL: Repository, https://github.com/Ishaan-1606/receipts-cli
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "codebase-receipts-cli"
3
- version = "1.0.0"
3
+ version = "1.0.1"
4
4
  description = "Receipts — resume claim verifier & codebase-grounded interview prep CLI"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -100,6 +100,24 @@ def setup() -> None:
100
100
  values["GEMINI_API_KEY"] = typer.prompt(
101
101
  "GEMINI_API_KEY", hide_input=True
102
102
  ).strip()
103
+ typer.echo("")
104
+ typer.echo(
105
+ "Gemini's free-tier embedding quota is limited, and a ChromaDB"
106
+ " collection is locked to whichever model embedded it first —"
107
+ " mixing providers on the same knowledge base breaks it."
108
+ )
109
+ use_ollama_embed = (
110
+ typer.prompt(
111
+ "Use local Ollama for embeddings instead? Recommended;"
112
+ " needs Ollama + nomic-embed-text pulled (y/n)",
113
+ default="y",
114
+ )
115
+ .strip()
116
+ .lower()
117
+ .startswith("y")
118
+ )
119
+ if use_ollama_embed:
120
+ values["RECEIPTS_EMBED_PROVIDER"] = "ollama"
103
121
  elif provider == "anthropic":
104
122
  typer.echo("")
105
123
  typer.echo("Get a key at https://console.anthropic.com/ (paid).")
@@ -0,0 +1,97 @@
1
+ """Setup wizard config tests — user-level ~/.receipts/.env handling."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from unittest import mock
6
+
7
+ from typer.testing import CliRunner
8
+
9
+ import receipts.config as config_mod
10
+ from receipts.cli import _write_global_config, app
11
+
12
+ runner = CliRunner()
13
+
14
+
15
+ def test_write_global_config_creates_file(tmp_path, monkeypatch):
16
+ target = tmp_path / ".receipts" / ".env"
17
+ monkeypatch.setattr(config_mod, "GLOBAL_CONFIG_PATH", target)
18
+
19
+ _write_global_config({"RECEIPTS_PROVIDER": "gemini", "GEMINI_API_KEY": "k123"})
20
+
21
+ content = target.read_text(encoding="utf-8")
22
+ assert "RECEIPTS_PROVIDER=gemini" in content
23
+ assert "GEMINI_API_KEY=k123" in content
24
+
25
+
26
+ def test_write_global_config_updates_existing_keys(tmp_path, monkeypatch):
27
+ target = tmp_path / ".env"
28
+ target.write_text(
29
+ "RECEIPTS_PROVIDER=ollama\nCUSTOM_SETTING=keep-me\n", encoding="utf-8"
30
+ )
31
+ monkeypatch.setattr(config_mod, "GLOBAL_CONFIG_PATH", target)
32
+
33
+ _write_global_config({"RECEIPTS_PROVIDER": "gemini"})
34
+
35
+ content = target.read_text(encoding="utf-8")
36
+ assert "RECEIPTS_PROVIDER=gemini" in content
37
+ assert "RECEIPTS_PROVIDER=ollama" not in content
38
+ assert "CUSTOM_SETTING=keep-me" in content # unrelated lines preserved
39
+
40
+
41
+ def test_global_config_is_loaded_as_fallback(tmp_path, monkeypatch):
42
+ """~/.receipts/.env fills in what the environment doesn't set."""
43
+ target = tmp_path / ".env"
44
+ target.write_text("RECEIPTS_TEST_SENTINEL=from-global\n", encoding="utf-8")
45
+ monkeypatch.setattr(config_mod, "GLOBAL_CONFIG_PATH", target)
46
+ monkeypatch.delenv("RECEIPTS_TEST_SENTINEL", raising=False)
47
+
48
+ import os
49
+
50
+ config_mod.load_settings() # triggers the dotenv cascade
51
+
52
+ assert os.environ.get("RECEIPTS_TEST_SENTINEL") == "from-global"
53
+ monkeypatch.delenv("RECEIPTS_TEST_SENTINEL", raising=False)
54
+
55
+
56
+ def _cleanup_setup_env(monkeypatch) -> None:
57
+ """load_settings() dotenv-loads into the real process env — undo it."""
58
+ for key in ("RECEIPTS_PROVIDER", "GEMINI_API_KEY", "RECEIPTS_EMBED_PROVIDER"):
59
+ monkeypatch.delenv(key, raising=False)
60
+
61
+
62
+ def test_setup_gemini_defaults_to_ollama_embeddings(tmp_path, monkeypatch):
63
+ """Answering 'y' to the embed-provider prompt avoids the dimension-
64
+ mismatch bug: a ChromaDB collection is locked to whichever embedding
65
+ model first wrote to it, so Gemini chat + Gemini embeddings on a
66
+ collection previously embedded via Ollama throws InvalidArgumentError.
67
+ """
68
+ target = tmp_path / ".env"
69
+ monkeypatch.setattr(config_mod, "GLOBAL_CONFIG_PATH", target)
70
+
71
+ fake_provider = mock.Mock()
72
+ fake_provider.name = "gemini"
73
+ with mock.patch("receipts.llm.factory.get_provider", return_value=fake_provider):
74
+ result = runner.invoke(app, ["setup"], input="2\ntest-key-123\ny\n")
75
+
76
+ _cleanup_setup_env(monkeypatch)
77
+ assert result.exit_code == 0, result.output
78
+ content = target.read_text(encoding="utf-8")
79
+ assert "RECEIPTS_PROVIDER=gemini" in content
80
+ assert "GEMINI_API_KEY=test-key-123" in content
81
+ assert "RECEIPTS_EMBED_PROVIDER=ollama" in content
82
+
83
+
84
+ def test_setup_gemini_can_decline_ollama_embeddings(tmp_path, monkeypatch):
85
+ target = tmp_path / ".env"
86
+ monkeypatch.setattr(config_mod, "GLOBAL_CONFIG_PATH", target)
87
+
88
+ fake_provider = mock.Mock()
89
+ fake_provider.name = "gemini"
90
+ with mock.patch("receipts.llm.factory.get_provider", return_value=fake_provider):
91
+ result = runner.invoke(app, ["setup"], input="2\ntest-key-123\nn\n")
92
+
93
+ _cleanup_setup_env(monkeypatch)
94
+ assert result.exit_code == 0, result.output
95
+ content = target.read_text(encoding="utf-8")
96
+ assert "RECEIPTS_PROVIDER=gemini" in content
97
+ assert "RECEIPTS_EMBED_PROVIDER" not in content
@@ -1,47 +0,0 @@
1
- """Setup wizard config tests — user-level ~/.receipts/.env handling."""
2
-
3
- from __future__ import annotations
4
-
5
- import receipts.config as config_mod
6
- from receipts.cli import _write_global_config
7
-
8
-
9
- def test_write_global_config_creates_file(tmp_path, monkeypatch):
10
- target = tmp_path / ".receipts" / ".env"
11
- monkeypatch.setattr(config_mod, "GLOBAL_CONFIG_PATH", target)
12
-
13
- _write_global_config({"RECEIPTS_PROVIDER": "gemini", "GEMINI_API_KEY": "k123"})
14
-
15
- content = target.read_text(encoding="utf-8")
16
- assert "RECEIPTS_PROVIDER=gemini" in content
17
- assert "GEMINI_API_KEY=k123" in content
18
-
19
-
20
- def test_write_global_config_updates_existing_keys(tmp_path, monkeypatch):
21
- target = tmp_path / ".env"
22
- target.write_text(
23
- "RECEIPTS_PROVIDER=ollama\nCUSTOM_SETTING=keep-me\n", encoding="utf-8"
24
- )
25
- monkeypatch.setattr(config_mod, "GLOBAL_CONFIG_PATH", target)
26
-
27
- _write_global_config({"RECEIPTS_PROVIDER": "gemini"})
28
-
29
- content = target.read_text(encoding="utf-8")
30
- assert "RECEIPTS_PROVIDER=gemini" in content
31
- assert "RECEIPTS_PROVIDER=ollama" not in content
32
- assert "CUSTOM_SETTING=keep-me" in content # unrelated lines preserved
33
-
34
-
35
- def test_global_config_is_loaded_as_fallback(tmp_path, monkeypatch):
36
- """~/.receipts/.env fills in what the environment doesn't set."""
37
- target = tmp_path / ".env"
38
- target.write_text("RECEIPTS_TEST_SENTINEL=from-global\n", encoding="utf-8")
39
- monkeypatch.setattr(config_mod, "GLOBAL_CONFIG_PATH", target)
40
- monkeypatch.delenv("RECEIPTS_TEST_SENTINEL", raising=False)
41
-
42
- import os
43
-
44
- config_mod.load_settings() # triggers the dotenv cascade
45
-
46
- assert os.environ.get("RECEIPTS_TEST_SENTINEL") == "from-global"
47
- monkeypatch.delenv("RECEIPTS_TEST_SENTINEL", raising=False)