fndr 0.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.
- fndr-0.0.1/.github/FUNDING.yml +1 -0
- fndr-0.0.1/.github/workflows/ci.yml +60 -0
- fndr-0.0.1/.github/workflows/release.yml +210 -0
- fndr-0.0.1/.github/workflows/security.yml +73 -0
- fndr-0.0.1/.gitignore +30 -0
- fndr-0.0.1/.gitleaksignore +6 -0
- fndr-0.0.1/.pre-commit-config.yaml +56 -0
- fndr-0.0.1/.python-version +1 -0
- fndr-0.0.1/CONTRIBUTING.md +40 -0
- fndr-0.0.1/LICENSE +21 -0
- fndr-0.0.1/Makefile +27 -0
- fndr-0.0.1/PKG-INFO +527 -0
- fndr-0.0.1/README.md +491 -0
- fndr-0.0.1/SECURITY.md +121 -0
- fndr-0.0.1/docs/apps.md +124 -0
- fndr-0.0.1/fnd/__init__.py +9 -0
- fndr-0.0.1/fnd/__main__.py +4 -0
- fndr-0.0.1/fnd/_perms.py +75 -0
- fndr-0.0.1/fnd/apps.py +820 -0
- fndr-0.0.1/fnd/cache.py +220 -0
- fndr-0.0.1/fnd/cascade.py +515 -0
- fndr-0.0.1/fnd/cli.py +750 -0
- fndr-0.0.1/fnd/config.py +792 -0
- fndr-0.0.1/fnd/dismissed_pdfs.py +55 -0
- fndr-0.0.1/fnd/explain.py +181 -0
- fndr-0.0.1/fnd/extract/__init__.py +51 -0
- fndr-0.0.1/fnd/extract/_docling_daemon.py +156 -0
- fndr-0.0.1/fnd/extract/_docling_helper.py +89 -0
- fndr-0.0.1/fnd/extract/_limits.py +39 -0
- fndr-0.0.1/fnd/extract/_ooxml.py +46 -0
- fndr-0.0.1/fnd/extract/_worker.py +485 -0
- fndr-0.0.1/fnd/extract/base.py +78 -0
- fndr-0.0.1/fnd/extract/docx.py +317 -0
- fndr-0.0.1/fnd/extract/markdown.py +221 -0
- fndr-0.0.1/fnd/extract/pdf.py +694 -0
- fndr-0.0.1/fnd/extract/plain.py +57 -0
- fndr-0.0.1/fnd/extract/pptx.py +232 -0
- fndr-0.0.1/fnd/extras.py +423 -0
- fndr-0.0.1/fnd/filter_dsl.py +445 -0
- fndr-0.0.1/fnd/frontmatter.py +185 -0
- fndr-0.0.1/fnd/fusion.py +528 -0
- fndr-0.0.1/fnd/index.py +302 -0
- fndr-0.0.1/fnd/index_runner.py +674 -0
- fndr-0.0.1/fnd/layered.py +282 -0
- fndr-0.0.1/fnd/matching.py +291 -0
- fndr-0.0.1/fnd/meta_blob.py +51 -0
- fndr-0.0.1/fnd/migrate.py +128 -0
- fndr-0.0.1/fnd/opener.py +210 -0
- fndr-0.0.1/fnd/query.py +589 -0
- fndr-0.0.1/fnd/query_dsl.py +214 -0
- fndr-0.0.1/fnd/render.py +407 -0
- fndr-0.0.1/fnd/rerank.py +246 -0
- fndr-0.0.1/fnd/schema.py +120 -0
- fndr-0.0.1/fnd/seen_log.py +55 -0
- fndr-0.0.1/fnd/state.py +103 -0
- fndr-0.0.1/fnd/struct.py +21 -0
- fndr-0.0.1/fnd/synonyms.py +139 -0
- fndr-0.0.1/fnd/tui/__init__.py +5 -0
- fndr-0.0.1/fnd/tui/_md_flat.py +104 -0
- fndr-0.0.1/fnd/tui/_md_hybrid.py +302 -0
- fndr-0.0.1/fnd/tui/_perf.py +76 -0
- fndr-0.0.1/fnd/tui/actions.py +318 -0
- fndr-0.0.1/fnd/tui/app.py +5619 -0
- fndr-0.0.1/fnd/tui/ax_permission_screen.py +150 -0
- fndr-0.0.1/fnd/tui/config_recovery_screen.py +248 -0
- fndr-0.0.1/fnd/tui/cost_estimate.py +238 -0
- fndr-0.0.1/fnd/tui/extras_install_progress.py +393 -0
- fndr-0.0.1/fnd/tui/failure_log.py +138 -0
- fndr-0.0.1/fnd/tui/first_reindex_warning.py +201 -0
- fndr-0.0.1/fnd/tui/indexer_modal.py +999 -0
- fndr-0.0.1/fnd/tui/lazy_trailing.py +113 -0
- fndr-0.0.1/fnd/tui/line_buffer.py +729 -0
- fndr-0.0.1/fnd/tui/live_progress.py +135 -0
- fndr-0.0.1/fnd/tui/menu.py +2382 -0
- fndr-0.0.1/fnd/tui/open_with_screen.py +293 -0
- fndr-0.0.1/fnd/tui/preview_dispatcher.py +77 -0
- fndr-0.0.1/fnd/tui/preview_scrollbar.py +328 -0
- fndr-0.0.1/fnd/tui/progress.py +198 -0
- fndr-0.0.1/fnd/tui/settings_screen.py +4193 -0
- fndr-0.0.1/fnd/tui/upgrade_banner.py +202 -0
- fndr-0.0.1/fnd/tui/widgets/__init__.py +5 -0
- fndr-0.0.1/fnd/tui/widgets/detail_strip.py +79 -0
- fndr-0.0.1/fnd/walk.py +183 -0
- fndr-0.0.1/pyproject.toml +124 -0
- fndr-0.0.1/pyrightconfig.json +14 -0
- fndr-0.0.1/ruff.toml +62 -0
- fndr-0.0.1/tests/__init__.py +0 -0
- fndr-0.0.1/tests/_pilot_wait.py +96 -0
- fndr-0.0.1/tests/conftest.py +218 -0
- fndr-0.0.1/tests/fixtures/_build.py +195 -0
- fndr-0.0.1/tests/fixtures/docs/methods.docx +0 -0
- fndr-0.0.1/tests/fixtures/malformed/README.md +15 -0
- fndr-0.0.1/tests/fixtures/notes/index.md +23 -0
- fndr-0.0.1/tests/fixtures/papers/test.pdf +0 -0
- fndr-0.0.1/tests/fixtures/plain/short.txt +6 -0
- fndr-0.0.1/tests/fixtures/slides/deck.pptx +0 -0
- fndr-0.0.1/tests/fuzz/__init__.py +0 -0
- fndr-0.0.1/tests/fuzz/test_extractor_fuzz.py +171 -0
- fndr-0.0.1/tests/test_actions_keymap.py +178 -0
- fndr-0.0.1/tests/test_app_defaults_picker.py +124 -0
- fndr-0.0.1/tests/test_app_defaults_picker_ui.py +91 -0
- fndr-0.0.1/tests/test_app_flat_buffer_path.py +391 -0
- fndr-0.0.1/tests/test_apps_registry.py +641 -0
- fndr-0.0.1/tests/test_ax_permission_modal.py +174 -0
- fndr-0.0.1/tests/test_cache_cli.py +116 -0
- fndr-0.0.1/tests/test_cli_collection_add.py +159 -0
- fndr-0.0.1/tests/test_cli_migrate.py +98 -0
- fndr-0.0.1/tests/test_cli_routing.py +120 -0
- fndr-0.0.1/tests/test_cli_search_meta.py +71 -0
- fndr-0.0.1/tests/test_clone_source.py +152 -0
- fndr-0.0.1/tests/test_collection_name_validation.py +66 -0
- fndr-0.0.1/tests/test_collection_tristate_toggle.py +238 -0
- fndr-0.0.1/tests/test_collections.py +165 -0
- fndr-0.0.1/tests/test_config_recovery_screen.py +52 -0
- fndr-0.0.1/tests/test_config_sources.py +120 -0
- fndr-0.0.1/tests/test_config_validate_filter.py +52 -0
- fndr-0.0.1/tests/test_config_write_collection.py +126 -0
- fndr-0.0.1/tests/test_config_write_setting.py +94 -0
- fndr-0.0.1/tests/test_cost_estimate.py +154 -0
- fndr-0.0.1/tests/test_cursor_on_expand.py +72 -0
- fndr-0.0.1/tests/test_delete_source.py +94 -0
- fndr-0.0.1/tests/test_delete_source_e2e.py +164 -0
- fndr-0.0.1/tests/test_detail_strip_advisory.py +159 -0
- fndr-0.0.1/tests/test_escape_cascade.py +61 -0
- fndr-0.0.1/tests/test_extract_pdf_subprocess.py +85 -0
- fndr-0.0.1/tests/test_extract_pdf_wedge_detector.py +113 -0
- fndr-0.0.1/tests/test_extract_pool_fd_recovery.py +52 -0
- fndr-0.0.1/tests/test_extract_pptx_docx.py +42 -0
- fndr-0.0.1/tests/test_extract_safety.py +204 -0
- fndr-0.0.1/tests/test_extraction_cache.py +253 -0
- fndr-0.0.1/tests/test_extractor_docx_emits_markdown.py +133 -0
- fndr-0.0.1/tests/test_extractor_md_preserves_source.py +187 -0
- fndr-0.0.1/tests/test_extractor_pptx_emits_markdown.py +130 -0
- fndr-0.0.1/tests/test_extras_cli.py +115 -0
- fndr-0.0.1/tests/test_extras_detection.py +137 -0
- fndr-0.0.1/tests/test_extras_install_repair.py +63 -0
- fndr-0.0.1/tests/test_filename_elision.py +129 -0
- fndr-0.0.1/tests/test_filter_dsl.py +282 -0
- fndr-0.0.1/tests/test_first_reindex_warning.py +98 -0
- fndr-0.0.1/tests/test_frontmatter.py +99 -0
- fndr-0.0.1/tests/test_frontmatter_limits.py +56 -0
- fndr-0.0.1/tests/test_full_document_preview.py +169 -0
- fndr-0.0.1/tests/test_fuzzy_toggle.py +145 -0
- fndr-0.0.1/tests/test_fuzzy_toggle_tui.py +96 -0
- fndr-0.0.1/tests/test_get_file_chunks_parallel.py +148 -0
- fndr-0.0.1/tests/test_grouped_search.py +48 -0
- fndr-0.0.1/tests/test_highlight_covers_fuzzy_and_synonyms.py +247 -0
- fndr-0.0.1/tests/test_highlights_toggle.py +137 -0
- fndr-0.0.1/tests/test_hit_field_preservation.py +73 -0
- fndr-0.0.1/tests/test_index_meta_blob.py +70 -0
- fndr-0.0.1/tests/test_index_per_source_filter.py +56 -0
- fndr-0.0.1/tests/test_index_query_roundtrip.py +53 -0
- fndr-0.0.1/tests/test_index_runner.py +334 -0
- fndr-0.0.1/tests/test_indexer_modal_status_lines.py +31 -0
- fndr-0.0.1/tests/test_keybindings_help_overlay.py +211 -0
- fndr-0.0.1/tests/test_keybindings_provider.py +153 -0
- fndr-0.0.1/tests/test_lazy_mount_on_scroll.py +281 -0
- fndr-0.0.1/tests/test_line_buffer.py +578 -0
- fndr-0.0.1/tests/test_markdown_heading_marker.py +181 -0
- fndr-0.0.1/tests/test_md_line_tracking.py +87 -0
- fndr-0.0.1/tests/test_meta_blob.py +67 -0
- fndr-0.0.1/tests/test_migrate.py +35 -0
- fndr-0.0.1/tests/test_open_with_screen.py +301 -0
- fndr-0.0.1/tests/test_opener.py +155 -0
- fndr-0.0.1/tests/test_opener_injection.py +103 -0
- fndr-0.0.1/tests/test_pdf_docling_daemon.py +210 -0
- fndr-0.0.1/tests/test_pdf_extract_structured.py +75 -0
- fndr-0.0.1/tests/test_pdf_extras_optional.py +85 -0
- fndr-0.0.1/tests/test_pdf_page_label.py +260 -0
- fndr-0.0.1/tests/test_perms.py +66 -0
- fndr-0.0.1/tests/test_phase_5_6_polish.py +273 -0
- fndr-0.0.1/tests/test_phase_5_7_polish.py +176 -0
- fndr-0.0.1/tests/test_phase_5_8_scroll_to_match.py +205 -0
- fndr-0.0.1/tests/test_phase_5_9_stem_highlight.py +138 -0
- fndr-0.0.1/tests/test_phase_7_rerank.py +383 -0
- fndr-0.0.1/tests/test_phase_8_cascade_synonyms.py +256 -0
- fndr-0.0.1/tests/test_phase_9_fusion.py +358 -0
- fndr-0.0.1/tests/test_pizza_highlight.py +40 -0
- fndr-0.0.1/tests/test_pr_review_fixes.py +293 -0
- fndr-0.0.1/tests/test_preview_dispatcher.py +103 -0
- fndr-0.0.1/tests/test_preview_focus_class_toggle.py +110 -0
- fndr-0.0.1/tests/test_preview_leak_probe.py +90 -0
- fndr-0.0.1/tests/test_preview_load_debounce.py +114 -0
- fndr-0.0.1/tests/test_preview_markdown_widget.py +457 -0
- fndr-0.0.1/tests/test_preview_prefetch.py +285 -0
- fndr-0.0.1/tests/test_preview_scrolls_to_match.py +311 -0
- fndr-0.0.1/tests/test_query_dsl.py +136 -0
- fndr-0.0.1/tests/test_query_dsl_split.py +65 -0
- fndr-0.0.1/tests/test_query_limits.py +38 -0
- fndr-0.0.1/tests/test_query_metadata_filter.py +124 -0
- fndr-0.0.1/tests/test_render.py +61 -0
- fndr-0.0.1/tests/test_results_preview_arrow_bridge.py +81 -0
- fndr-0.0.1/tests/test_schema_bump.py +28 -0
- fndr-0.0.1/tests/test_schema_meta_blob.py +142 -0
- fndr-0.0.1/tests/test_settings_async_loading.py +195 -0
- fndr-0.0.1/tests/test_settings_cache_maintenance.py +334 -0
- fndr-0.0.1/tests/test_settings_confirm_body.py +94 -0
- fndr-0.0.1/tests/test_settings_description_invariants.py +131 -0
- fndr-0.0.1/tests/test_settings_extras_install_progress.py +224 -0
- fndr-0.0.1/tests/test_settings_hint_per_kind.py +160 -0
- fndr-0.0.1/tests/test_settings_indexing.py +252 -0
- fndr-0.0.1/tests/test_settings_indexing_search.py +100 -0
- fndr-0.0.1/tests/test_settings_input_focus_flow.py +199 -0
- fndr-0.0.1/tests/test_settings_menu_p2.py +269 -0
- fndr-0.0.1/tests/test_settings_nav_fixes.py +107 -0
- fndr-0.0.1/tests/test_settings_p3_keybindings_invoke.py +112 -0
- fndr-0.0.1/tests/test_settings_p3_reveal.py +75 -0
- fndr-0.0.1/tests/test_settings_p3_search.py +251 -0
- fndr-0.0.1/tests/test_settings_p3_visual.py +651 -0
- fndr-0.0.1/tests/test_settings_p3_wizard.py +481 -0
- fndr-0.0.1/tests/test_settings_structured_pdf.py +257 -0
- fndr-0.0.1/tests/test_settings_visual_language.py +333 -0
- fndr-0.0.1/tests/test_sidebar_preview_regressions.py +251 -0
- fndr-0.0.1/tests/test_smoke.py +10 -0
- fndr-0.0.1/tests/test_source_app_config.py +158 -0
- fndr-0.0.1/tests/test_source_form_app_picker.py +124 -0
- fndr-0.0.1/tests/test_state.py +113 -0
- fndr-0.0.1/tests/test_tui.py +121 -0
- fndr-0.0.1/tests/test_tui_metadata_filter.py +68 -0
- fndr-0.0.1/tests/test_update_all_collections.py +136 -0
- fndr-0.0.1/tests/test_ux_a2_footer.py +92 -0
- fndr-0.0.1/tests/test_ux_a_pane_titles.py +115 -0
- fndr-0.0.1/tests/test_ux_b_persistence_and_collapse.py +131 -0
- fndr-0.0.1/tests/test_ux_b_smart_collapse.py +110 -0
- fndr-0.0.1/tests/test_ux_c_score_bars.py +56 -0
- fndr-0.0.1/tests/test_ux_d_collections_panel.py +136 -0
- fndr-0.0.1/tests/test_ux_f_filters_panel.py +221 -0
- fndr-0.0.1/tests/test_ux_g_preview_polish.py +161 -0
- fndr-0.0.1/tests/test_ux_j_cascade_fallback.py +101 -0
- fndr-0.0.1/tests/test_ux_j_fusion_regression.py +127 -0
- fndr-0.0.1/tests/test_ux_j_scrollbar_markers.py +225 -0
- fndr-0.0.1/tests/test_uxp4_cli_explain.py +94 -0
- fndr-0.0.1/tests/test_uxp4_explain_trace.py +228 -0
- fndr-0.0.1/tests/test_uxp4_intent_line.py +133 -0
- fndr-0.0.1/tests/test_uxp4_preview_worker.py +257 -0
- fndr-0.0.1/tests/test_uxp4_strong_signal.py +153 -0
- fndr-0.0.1/tests/test_uxp4_tui_explain.py +105 -0
- fndr-0.0.1/tests/test_vault_detect.py +55 -0
- fndr-0.0.1/tests/test_walk_icloud.py +42 -0
- fndr-0.0.1/tests/test_walk_per_source.py +95 -0
- fndr-0.0.1/tests/test_walk_symlinks.py +52 -0
- fndr-0.0.1/tests/workflows/README.md +52 -0
- fndr-0.0.1/tests/workflows/__init__.py +11 -0
- fndr-0.0.1/tests/workflows/conftest.py +89 -0
- fndr-0.0.1/tests/workflows/test_cache_actions.py +92 -0
- fndr-0.0.1/tests/workflows/test_cache_hits_across_runs.py +81 -0
- fndr-0.0.1/tests/workflows/test_delete_collection.py +52 -0
- fndr-0.0.1/tests/workflows/test_first_reindex_warning.py +114 -0
- fndr-0.0.1/tests/workflows/test_install_pdf_structure.py +108 -0
- fndr-0.0.1/tests/workflows/test_per_collection_update.py +59 -0
- fndr-0.0.1/tests/workflows/test_toggles.py +84 -0
- fndr-0.0.1/tests/workflows/test_uninstall_pdf_structure.py +87 -0
- fndr-0.0.1/tests/workflows/test_update_all.py +98 -0
- fndr-0.0.1/tests/workflows/test_update_all_index_docs.py +117 -0
- fndr-0.0.1/uv.lock +1155 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
custom: ["https://buymeacoffee.com/ben.dev.au"]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
test:
|
|
14
|
+
name: pytest (full suite, serial)
|
|
15
|
+
# macOS so the path resolution (platformdirs uses
|
|
16
|
+
# ``~/Library/Application Support/fnd`` here) matches what users
|
|
17
|
+
# actually run. Free tier on public repos; private repos use the
|
|
18
|
+
# 10x multiplier so keep an eye on the minutes budget while the
|
|
19
|
+
# repo is still private.
|
|
20
|
+
runs-on: macos-14
|
|
21
|
+
timeout-minutes: 30
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
- name: Install uv
|
|
25
|
+
uses: astral-sh/setup-uv@v3
|
|
26
|
+
with:
|
|
27
|
+
enable-cache: true
|
|
28
|
+
- name: Set up Python 3.13
|
|
29
|
+
run: uv python install 3.13
|
|
30
|
+
- name: Sync deps (frozen lockfile)
|
|
31
|
+
run: uv sync --frozen
|
|
32
|
+
- name: Test
|
|
33
|
+
# Serial, no --testmon. CI is the gold-standard catch-all
|
|
34
|
+
# behind the local pre-commit hook, which uses --testmon for
|
|
35
|
+
# speed and may skip tests the developer's diff doesn't reach.
|
|
36
|
+
run: uv run pytest -q -m "not slow"
|
|
37
|
+
|
|
38
|
+
format:
|
|
39
|
+
name: ruff-format
|
|
40
|
+
# Format check is a separate small job so a formatting miss is
|
|
41
|
+
# obvious in the PR check list without re-running the slow tests.
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
steps:
|
|
44
|
+
- uses: actions/checkout@v4
|
|
45
|
+
- uses: astral-sh/setup-uv@v3
|
|
46
|
+
- run: uv python install 3.13
|
|
47
|
+
- run: uv sync --frozen --group dev
|
|
48
|
+
- name: Ruff format (check only)
|
|
49
|
+
run: uv run ruff format --check .
|
|
50
|
+
|
|
51
|
+
typecheck:
|
|
52
|
+
name: pyright (strict)
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
steps:
|
|
55
|
+
- uses: actions/checkout@v4
|
|
56
|
+
- uses: astral-sh/setup-uv@v3
|
|
57
|
+
- run: uv python install 3.13
|
|
58
|
+
- run: uv sync --frozen
|
|
59
|
+
- name: Pyright
|
|
60
|
+
run: uv run pyright
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
# Triggered on any `v*` tag. Builds sdist + wheel, emits SBOM and a
|
|
4
|
+
# SLSA-style build-provenance attestation, publishes to PyPI via a
|
|
5
|
+
# Trusted Publisher (OIDC — no API token in repo secrets), and opens
|
|
6
|
+
# a bump-PR against the Homebrew tap with the new sdist SHA-256.
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
tags:
|
|
11
|
+
- "v*"
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
build:
|
|
18
|
+
name: build sdist + wheel
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
permissions:
|
|
21
|
+
contents: read
|
|
22
|
+
# `id-token: write` lets the build job mint an OIDC token for
|
|
23
|
+
# the attestation step (and later for PyPI Trusted Publisher).
|
|
24
|
+
id-token: write
|
|
25
|
+
# `attestations: write` lets actions/attest-build-provenance
|
|
26
|
+
# record the SLSA-style provenance for the artifacts we produce.
|
|
27
|
+
attestations: write
|
|
28
|
+
outputs:
|
|
29
|
+
sdist-name: ${{ steps.meta.outputs.sdist }}
|
|
30
|
+
sdist-sha256: ${{ steps.meta.outputs.sha256 }}
|
|
31
|
+
version: ${{ steps.meta.outputs.version }}
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
with:
|
|
35
|
+
fetch-depth: 0
|
|
36
|
+
|
|
37
|
+
- name: Guard — release tag matches pyproject version
|
|
38
|
+
# Fail fast (before build/publish) if the `vX.Y.Z` tag and the
|
|
39
|
+
# declared version have drifted. pyproject is the single source;
|
|
40
|
+
# this just stops a mismatched tag from shipping.
|
|
41
|
+
run: |
|
|
42
|
+
set -euo pipefail
|
|
43
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
44
|
+
ver=$(python3 -c "import tomllib;print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
45
|
+
[ "$tag" = "$ver" ] || { echo "::error::tag v$tag != pyproject version $ver — bump pyproject.toml before tagging"; exit 1; }
|
|
46
|
+
echo "tag and pyproject version agree: $ver"
|
|
47
|
+
|
|
48
|
+
- name: Install uv
|
|
49
|
+
uses: astral-sh/setup-uv@v3
|
|
50
|
+
with:
|
|
51
|
+
enable-cache: true
|
|
52
|
+
|
|
53
|
+
- run: uv python install 3.13
|
|
54
|
+
|
|
55
|
+
- name: Build sdist + wheel
|
|
56
|
+
run: uv build --sdist --wheel --out-dir dist
|
|
57
|
+
|
|
58
|
+
- name: Generate CycloneDX SBOM (S2)
|
|
59
|
+
# SBOM lives outside dist/ so the PyPI publish step (which
|
|
60
|
+
# uploads the whole packages-dir via twine) doesn't try to
|
|
61
|
+
# upload a JSON file as a distribution and fail.
|
|
62
|
+
run: |
|
|
63
|
+
mkdir -p sbom
|
|
64
|
+
uv tool install cyclonedx-bom
|
|
65
|
+
uv export --format requirements-txt --no-hashes -o requirements.frozen.txt
|
|
66
|
+
uv tool run cyclonedx-py requirements requirements.frozen.txt \
|
|
67
|
+
--output-format JSON \
|
|
68
|
+
--output-file sbom/sbom.cdx.json
|
|
69
|
+
|
|
70
|
+
- name: Attest build provenance (S2)
|
|
71
|
+
uses: actions/attest-build-provenance@v1
|
|
72
|
+
with:
|
|
73
|
+
subject-path: |
|
|
74
|
+
dist/*.tar.gz
|
|
75
|
+
dist/*.whl
|
|
76
|
+
|
|
77
|
+
- name: Compute sdist metadata
|
|
78
|
+
id: meta
|
|
79
|
+
run: |
|
|
80
|
+
set -euo pipefail
|
|
81
|
+
sdist=$(ls dist/*.tar.gz | head -n1)
|
|
82
|
+
sha=$(shasum -a 256 "$sdist" | awk '{print $1}')
|
|
83
|
+
# PEP 625 sdist name is `{name}-{version}.tar.gz` and the name
|
|
84
|
+
# part carries no `-` (normalised to `_`), so the first `-`
|
|
85
|
+
# splits name from version — name-agnostic (fndr, fnd_x, …).
|
|
86
|
+
base=$(basename "$sdist" .tar.gz)
|
|
87
|
+
ver="${base#*-}"
|
|
88
|
+
echo "sdist=$(basename "$sdist")" >> "$GITHUB_OUTPUT"
|
|
89
|
+
echo "sha256=$sha" >> "$GITHUB_OUTPUT"
|
|
90
|
+
echo "version=$ver" >> "$GITHUB_OUTPUT"
|
|
91
|
+
|
|
92
|
+
- name: Upload dist artifacts
|
|
93
|
+
uses: actions/upload-artifact@v4
|
|
94
|
+
with:
|
|
95
|
+
name: dist
|
|
96
|
+
path: dist/
|
|
97
|
+
|
|
98
|
+
- name: Upload SBOM artifact
|
|
99
|
+
uses: actions/upload-artifact@v4
|
|
100
|
+
with:
|
|
101
|
+
name: sbom
|
|
102
|
+
path: sbom/
|
|
103
|
+
|
|
104
|
+
publish-pypi:
|
|
105
|
+
name: publish to PyPI (Trusted Publisher)
|
|
106
|
+
needs: build
|
|
107
|
+
runs-on: ubuntu-latest
|
|
108
|
+
environment: pypi
|
|
109
|
+
permissions:
|
|
110
|
+
contents: read
|
|
111
|
+
id-token: write
|
|
112
|
+
steps:
|
|
113
|
+
- name: Download dist
|
|
114
|
+
uses: actions/download-artifact@v4
|
|
115
|
+
with:
|
|
116
|
+
name: dist
|
|
117
|
+
path: dist/
|
|
118
|
+
|
|
119
|
+
- name: Publish (OIDC, no token)
|
|
120
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
121
|
+
with:
|
|
122
|
+
# Configure the Trusted Publisher at
|
|
123
|
+
# https://pypi.org/manage/account/publishing/ pointing at
|
|
124
|
+
# this repo + `release.yml` workflow + `pypi` environment.
|
|
125
|
+
packages-dir: dist
|
|
126
|
+
skip-existing: true
|
|
127
|
+
|
|
128
|
+
release-notes:
|
|
129
|
+
name: publish GitHub Release
|
|
130
|
+
needs: build
|
|
131
|
+
runs-on: ubuntu-latest
|
|
132
|
+
permissions:
|
|
133
|
+
contents: write
|
|
134
|
+
steps:
|
|
135
|
+
- uses: actions/checkout@v4
|
|
136
|
+
- name: Download dist
|
|
137
|
+
uses: actions/download-artifact@v4
|
|
138
|
+
with:
|
|
139
|
+
name: dist
|
|
140
|
+
path: dist/
|
|
141
|
+
- name: Download SBOM
|
|
142
|
+
uses: actions/download-artifact@v4
|
|
143
|
+
with:
|
|
144
|
+
name: sbom
|
|
145
|
+
path: sbom/
|
|
146
|
+
- name: Create GitHub Release
|
|
147
|
+
env:
|
|
148
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
149
|
+
run: |
|
|
150
|
+
set -euo pipefail
|
|
151
|
+
tag="${GITHUB_REF##*/}"
|
|
152
|
+
# Attach sdist, wheel, and SBOM; the provenance attestation
|
|
153
|
+
# is fetched separately via `gh attestation verify` and is
|
|
154
|
+
# not uploaded as a release asset (GitHub stores it).
|
|
155
|
+
gh release create "$tag" \
|
|
156
|
+
--title "fnd $tag" \
|
|
157
|
+
--notes "See CHANGELOG / commit log for details. SHA-256: ${{ needs.build.outputs.sdist-sha256 }}" \
|
|
158
|
+
dist/*.tar.gz dist/*.whl sbom/sbom.cdx.json
|
|
159
|
+
|
|
160
|
+
bump-homebrew-tap:
|
|
161
|
+
name: bump homebrew-tap formula
|
|
162
|
+
# Needs publish-pypi so the sdist exists when we resolve its URL.
|
|
163
|
+
needs: [build, publish-pypi]
|
|
164
|
+
runs-on: ubuntu-latest
|
|
165
|
+
permissions:
|
|
166
|
+
contents: read
|
|
167
|
+
steps:
|
|
168
|
+
- name: Open formula bump PR
|
|
169
|
+
env:
|
|
170
|
+
# HOMEBREW_TAP_PAT: fine-scoped PAT with PR-write to the tap
|
|
171
|
+
# (the default GITHUB_TOKEN can't push cross-repo). Both live
|
|
172
|
+
# on THIS repo, not the tap — the workflow runs here.
|
|
173
|
+
# HOMEBREW_TAP_REPO = ben-dev-au/homebrew-tap.
|
|
174
|
+
GH_TOKEN: ${{ secrets.HOMEBREW_TAP_PAT }}
|
|
175
|
+
TAP_REPO: ${{ vars.HOMEBREW_TAP_REPO }}
|
|
176
|
+
VERSION: ${{ needs.build.outputs.version }}
|
|
177
|
+
if: ${{ vars.HOMEBREW_TAP_REPO != '' }}
|
|
178
|
+
run: |
|
|
179
|
+
set -euo pipefail
|
|
180
|
+
# Resolve url + sha256 from PyPI's JSON API — one source of
|
|
181
|
+
# truth, immune to name normalisation and the hashed storage
|
|
182
|
+
# path. The index can lag the publish by a few seconds, so retry.
|
|
183
|
+
meta=""
|
|
184
|
+
for i in 1 2 3 4 5 6; do
|
|
185
|
+
meta=$(curl -fsSL "https://pypi.org/pypi/fndr/${VERSION}/json" \
|
|
186
|
+
| python3 -c "import sys,json;u=next(x for x in json.load(sys.stdin)['urls'] if x['packagetype']=='sdist');print(u['url']);print(u['digests']['sha256'])") \
|
|
187
|
+
&& [ -n "$meta" ] && break
|
|
188
|
+
echo "PyPI JSON not ready (attempt $i); retrying…" >&2
|
|
189
|
+
sleep 10
|
|
190
|
+
done
|
|
191
|
+
[ -n "$meta" ] || { echo "could not resolve sdist for fndr $VERSION" >&2; exit 1; }
|
|
192
|
+
new_url=$(printf '%s\n' "$meta" | sed -n 1p)
|
|
193
|
+
new_sha=$(printf '%s\n' "$meta" | sed -n 2p)
|
|
194
|
+
|
|
195
|
+
tmp=$(mktemp -d)
|
|
196
|
+
git clone "https://x-access-token:$GH_TOKEN@github.com/$TAP_REPO.git" "$tmp/tap"
|
|
197
|
+
cd "$tmp/tap"
|
|
198
|
+
formula=Formula/fnd.rb
|
|
199
|
+
sed -i.bak "s|^ url \".*\"| url \"$new_url\"|" "$formula"
|
|
200
|
+
sed -i.bak "s|^ sha256 \".*\"| sha256 \"$new_sha\"|" "$formula"
|
|
201
|
+
rm -f "$formula.bak"
|
|
202
|
+
branch="bump/fnd-$VERSION"
|
|
203
|
+
git checkout -b "$branch"
|
|
204
|
+
git add "$formula"
|
|
205
|
+
git -c user.email="actions@github.com" -c user.name="fnd-release-bot" \
|
|
206
|
+
commit -m "fnd $VERSION"
|
|
207
|
+
git push -u origin "$branch"
|
|
208
|
+
gh pr create \
|
|
209
|
+
--title "fnd $VERSION" \
|
|
210
|
+
--body "Automated bump from ben-dev-au/fnd@${GITHUB_REF##*/}. sha256 \`$new_sha\`."
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
name: security
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
schedule:
|
|
9
|
+
# Weekly Monday 09:00 UTC — catches CVEs published since the last
|
|
10
|
+
# human-driven push.
|
|
11
|
+
- cron: "0 9 * * 1"
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
pip-audit:
|
|
18
|
+
name: pip-audit (lockfile)
|
|
19
|
+
runs-on: macos-14
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
- name: Install uv
|
|
23
|
+
uses: astral-sh/setup-uv@v3
|
|
24
|
+
with:
|
|
25
|
+
enable-cache: true
|
|
26
|
+
- name: Set up Python 3.13
|
|
27
|
+
run: uv python install 3.13
|
|
28
|
+
- name: Sync deps (frozen lockfile)
|
|
29
|
+
run: uv sync --frozen --all-extras --group dev
|
|
30
|
+
- name: Install pip-audit
|
|
31
|
+
run: uv tool install pip-audit
|
|
32
|
+
- name: Export resolved requirements
|
|
33
|
+
# `--no-emit-project` drops the local `fnd` package (which uv
|
|
34
|
+
# emits as `-e .`); pip-audit can't look up an unpublished
|
|
35
|
+
# editable install and would fail the whole audit. Once Phase
|
|
36
|
+
# 0 of the publish runbook lands `fnd` on PyPI, the flag stays
|
|
37
|
+
# — we don't need to audit ourselves, only our deps.
|
|
38
|
+
run: |
|
|
39
|
+
uv export \
|
|
40
|
+
--format requirements-txt \
|
|
41
|
+
--no-hashes \
|
|
42
|
+
--no-emit-project \
|
|
43
|
+
-o requirements.frozen.txt
|
|
44
|
+
- name: Audit
|
|
45
|
+
run: uv tool run pip-audit -r requirements.frozen.txt --strict
|
|
46
|
+
|
|
47
|
+
ruff-security:
|
|
48
|
+
name: ruff (bandit ruleset)
|
|
49
|
+
runs-on: macos-14
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@v4
|
|
52
|
+
- uses: astral-sh/setup-uv@v3
|
|
53
|
+
- run: uv python install 3.13
|
|
54
|
+
- run: uv sync --frozen --group dev
|
|
55
|
+
- name: Ruff (project config includes select=S)
|
|
56
|
+
run: uv run ruff check .
|
|
57
|
+
|
|
58
|
+
gitleaks:
|
|
59
|
+
name: gitleaks
|
|
60
|
+
runs-on: ubuntu-latest
|
|
61
|
+
permissions:
|
|
62
|
+
contents: read
|
|
63
|
+
# gitleaks-action posts a PR-summary comment on findings; without
|
|
64
|
+
# `pull-requests: write` the action fails with
|
|
65
|
+
# `Resource not accessible by integration`.
|
|
66
|
+
pull-requests: write
|
|
67
|
+
steps:
|
|
68
|
+
- uses: actions/checkout@v4
|
|
69
|
+
with:
|
|
70
|
+
fetch-depth: 0
|
|
71
|
+
- uses: gitleaks/gitleaks-action@v2
|
|
72
|
+
env:
|
|
73
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
fndr-0.0.1/.gitignore
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
.venv/
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.pyc
|
|
4
|
+
.pytest_cache/
|
|
5
|
+
.ruff_cache/
|
|
6
|
+
.pyright/
|
|
7
|
+
.mypy_cache/
|
|
8
|
+
dist/
|
|
9
|
+
build/
|
|
10
|
+
*.egg-info/
|
|
11
|
+
.DS_Store
|
|
12
|
+
.coverage
|
|
13
|
+
htmlcov/
|
|
14
|
+
# pytest-testmon per-developer test-impact cache
|
|
15
|
+
.testmondata
|
|
16
|
+
.testmondata-journal
|
|
17
|
+
.testmondata-shm
|
|
18
|
+
.testmondata-wal
|
|
19
|
+
.vscode/
|
|
20
|
+
.idea/
|
|
21
|
+
tests/fixtures/.cache/
|
|
22
|
+
# Index data is never committed (lives in user's Library on macOS)
|
|
23
|
+
index/
|
|
24
|
+
# Per-user Claude Code config / scheduled-task state
|
|
25
|
+
.claude/
|
|
26
|
+
# Per-user operational notes (release runbooks, account setup) and
|
|
27
|
+
# all developer-only tooling. Lives in a separate private repo at
|
|
28
|
+
# dev/. Not part of the public repo.
|
|
29
|
+
private_dev_docs/
|
|
30
|
+
dev/
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# False positive: `key = "corruptkey--v1"` is a literal cache-key
|
|
2
|
+
# fixture for the corrupt-JSON test. Renamed in a later commit on
|
|
3
|
+
# this branch, but the historical commit still trips the
|
|
4
|
+
# generic-api-key heuristic because gitleaks scans the full commit
|
|
5
|
+
# range, not just HEAD.
|
|
6
|
+
ac26e70dc720c7a3b44a5a09497a395814514b4f:tests/test_extraction_cache.py:generic-api-key:84
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v4.6.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: check-toml
|
|
6
|
+
- id: check-yaml
|
|
7
|
+
- id: end-of-file-fixer
|
|
8
|
+
- id: trailing-whitespace
|
|
9
|
+
- id: check-added-large-files
|
|
10
|
+
args: ["--maxkb=2000"]
|
|
11
|
+
|
|
12
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
13
|
+
# Keep in sync with the ruff pin in pyproject.toml dev deps; CI runs
|
|
14
|
+
# `uv run ruff format --check .` so drift here will reformat files
|
|
15
|
+
# locally that CI then rejects.
|
|
16
|
+
rev: v0.15.14
|
|
17
|
+
hooks:
|
|
18
|
+
- id: ruff-format
|
|
19
|
+
- id: ruff
|
|
20
|
+
args: ["--fix"]
|
|
21
|
+
|
|
22
|
+
- repo: local
|
|
23
|
+
hooks:
|
|
24
|
+
- id: pyright
|
|
25
|
+
name: pyright (strict)
|
|
26
|
+
entry: uv run pyright
|
|
27
|
+
language: system
|
|
28
|
+
types: [python]
|
|
29
|
+
pass_filenames: false
|
|
30
|
+
|
|
31
|
+
- id: pytest-fast
|
|
32
|
+
name: "pytest (testmon: only tests whose code paths changed)"
|
|
33
|
+
# --testmon caches per-test code dependencies in
|
|
34
|
+
# ``.testmondata`` (gitignored). First run pays the full suite
|
|
35
|
+
# cost while the baseline is built; subsequent commits only
|
|
36
|
+
# re-run tests whose tracked files changed in the diff. CI
|
|
37
|
+
# runs the full serial suite without --testmon as the
|
|
38
|
+
# gold-standard catch-all.
|
|
39
|
+
entry: uv run pytest -q -m "not slow" --testmon
|
|
40
|
+
language: system
|
|
41
|
+
types: [python]
|
|
42
|
+
pass_filenames: false
|
|
43
|
+
|
|
44
|
+
- id: harness-indexing
|
|
45
|
+
name: "harness: indexer status lines (synthetic, fast)"
|
|
46
|
+
# Pre-push only - runs the synthetic indexer harness when
|
|
47
|
+
# touching code that controls how PDFs reach the index.
|
|
48
|
+
# Catches cascading worker death and counter regressions
|
|
49
|
+
# without paying the cost on every commit. Real-corpus
|
|
50
|
+
# validation lives behind FND_AUDIT_REAL_CORPUS=1 and is
|
|
51
|
+
# opt-in via `pre-commit run --hook-stage manual`.
|
|
52
|
+
stages: [pre-push]
|
|
53
|
+
entry: uv run python dev/tools/workflow_audit_tmux.py indexer_status_lines indexer_status_lines_md_only
|
|
54
|
+
language: system
|
|
55
|
+
files: ^(fnd/extract/|fnd/index|fnd/walk\.py|fnd/tui/indexer_modal\.py|dev/tools/workflow_audit_tmux\.py)
|
|
56
|
+
pass_filenames: false
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Contributing to fnd
|
|
2
|
+
|
|
3
|
+
Thanks for your interest. fnd is a macOS-only document-search CLI/TUI; it's
|
|
4
|
+
early but actively developed. Bug reports, app-catalogue entries, and focused
|
|
5
|
+
PRs are all welcome.
|
|
6
|
+
|
|
7
|
+
## Development setup
|
|
8
|
+
|
|
9
|
+
Requires macOS, Python 3.13, and [uv](https://docs.astral.sh/uv/).
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
git clone https://github.com/ben-dev-au/fnd.git
|
|
13
|
+
cd fnd
|
|
14
|
+
make sync # uv sync --all-extras --group dev
|
|
15
|
+
make install-hooks # pre-commit hooks (ruff + pyright)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Run fnd from the checkout with `uv run python -m fnd <command>`.
|
|
19
|
+
|
|
20
|
+
## Before opening a PR
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
make test # full pytest suite
|
|
24
|
+
make lint # ruff + pyright (strict)
|
|
25
|
+
make fmt # auto-format and apply safe fixes
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
CI runs the same checks on macOS. Keep PRs scoped to one change; match the
|
|
29
|
+
surrounding code's style and comment density.
|
|
30
|
+
|
|
31
|
+
## Adding an app to the "Open with…" catalogue
|
|
32
|
+
|
|
33
|
+
Third-party app integrations live in [`docs/apps.md`](docs/apps.md). To
|
|
34
|
+
contribute one, add your `[apps.<id>]` config block to the catalogue; see that
|
|
35
|
+
page for the schema and safety rules.
|
|
36
|
+
|
|
37
|
+
## Security
|
|
38
|
+
|
|
39
|
+
Please **do not** open public issues for security-sensitive findings. See
|
|
40
|
+
[`SECURITY.md`](SECURITY.md) for private reporting and the threat model.
|
fndr-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ben Davidson
|
|
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.
|
fndr-0.0.1/Makefile
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
.PHONY: sync test lint fmt tui snapshot install-hooks
|
|
2
|
+
|
|
3
|
+
sync:
|
|
4
|
+
uv sync --all-extras --group dev
|
|
5
|
+
|
|
6
|
+
test:
|
|
7
|
+
uv run pytest -q
|
|
8
|
+
|
|
9
|
+
test-fast:
|
|
10
|
+
uv run pytest -q -m "not slow"
|
|
11
|
+
|
|
12
|
+
lint:
|
|
13
|
+
uv run ruff check .
|
|
14
|
+
uv run pyright
|
|
15
|
+
|
|
16
|
+
fmt:
|
|
17
|
+
uv run ruff format .
|
|
18
|
+
uv run ruff check --fix .
|
|
19
|
+
|
|
20
|
+
tui:
|
|
21
|
+
uv run textual run --dev fnd.tui.app:FNDApp
|
|
22
|
+
|
|
23
|
+
snapshot:
|
|
24
|
+
uv run pytest --snapshot-update
|
|
25
|
+
|
|
26
|
+
install-hooks:
|
|
27
|
+
uv run pre-commit install
|