footprinter-cli 1.0.1__tar.gz → 1.0.2__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.
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/PKG-INFO +15 -8
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/README.md +9 -3
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/access.py +6 -6
- footprinter_cli-1.0.2/footprinter/api/__init__.py +4 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/api/entities.py +15 -7
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/api/search.py +2 -1
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/api/semantic.py +2 -1
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/bundled/config.example.yaml +18 -13
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/_common.py +2 -11
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/_policy_helpers.py +2 -2
- footprinter_cli-1.0.2/footprinter/cli/api_cmd.py +61 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/data.py +25 -25
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/delete.py +24 -9
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/doctor.py +5 -4
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/ingest.py +25 -13
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/mcp_cmd.py +2 -2
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/mcp_setup.py +29 -9
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/setup.py +75 -57
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/status.py +22 -8
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/uninstall.py +42 -17
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/upsert.py +4 -5
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/vectorize_cmd.py +3 -3
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/view.py +22 -1
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/browser.py +23 -7
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/chats.py +11 -24
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/clients.py +44 -4
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/emails.py +11 -4
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/files.py +23 -19
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/folders.py +118 -44
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/projects.py +47 -59
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/search.py +98 -29
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/status.py +18 -18
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/adapters/local_files.py +8 -2
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/adapters/local_folders.py +26 -2
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/adapters/protocol.py +5 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/cli.py +13 -13
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/db/migration.py +50 -7
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/db/schema.py +61 -24
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/file_indexer.py +13 -3
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/file_scanner.py +15 -5
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/folder_indexer.py +118 -13
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/orchestrator.py +8 -11
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/pipe_runner.py +9 -1
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/processing.py +1 -1
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/status.py +8 -7
- footprinter_cli-1.0.2/footprinter/mcp/resources/__init__.py +1 -0
- footprinter_cli-1.0.2/footprinter/mcp/resources/context.py +67 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/mcp/server.py +3 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/mcp/tools/navigation.py +18 -3
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/mcp/tools/read.py +2 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/mcp/tools/search.py +8 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/mcp/tools/semantic.py +13 -1
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/semantic/hybrid_search.py +2 -2
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/access_service.py +15 -5
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/client_service.py +14 -5
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/email_service.py +2 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/folder_service.py +16 -2
- footprinter_cli-1.0.2/footprinter/services/includes.py +48 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/ingest_service.py +68 -2
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/project_service.py +15 -6
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/search_service.py +18 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/semantic_service.py +24 -4
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/visit_service.py +3 -1
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter_cli.egg-info/PKG-INFO +15 -8
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter_cli.egg-info/SOURCES.txt +2 -1
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/pyproject.toml +6 -5
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_access_control_bypasses.py +2 -2
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_build_status_filter.py +7 -7
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_edit_recalculate.py +1 -1
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_files_surface.py +4 -4
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_resolver.py +23 -9
- footprinter_cli-1.0.1/footprinter/api/__init__.py +0 -1
- footprinter_cli-1.0.1/footprinter/cli/api_cmd.py +0 -32
- footprinter_cli-1.0.1/footprinter/ingest/chat_dedup.py +0 -156
- footprinter_cli-1.0.1/footprinter/services/includes.py +0 -19
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/LICENSE +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/__init__.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/api/db.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/api/server.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/api/status.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/bundled/__init__.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/bundled/patterns/context_patterns.yaml +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/bundled/patterns/extensions.yaml +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/bundled/patterns/filename_patterns.yaml +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/bundled/patterns/mime_mappings.yaml +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/bundled/patterns/salesforce_rules.yaml +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/bundled/patterns/security_patterns.yaml +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/__init__.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/__main__.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/_prompt.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/connect.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/search.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/search_cmd.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/status_cmd.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/connectors/__init__.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/connectors/config_utils.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/__init__.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/messages.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/policies.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/sql_utils.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/uploads.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/__init__.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/adapters/__init__.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/adapters/browser.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/adapters/chat.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/adapters/ingest.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/browser_indexer.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/chat_indexer.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/chat_parsers/__init__.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/chat_parsers/chatgpt_parser.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/chat_parsers/claude_parser.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/content_extractors.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/database.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/db/__init__.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/db/connector_schema.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/db/security.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/full_content_extractor.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/registry.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/run_record.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/mcp/__init__.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/mcp/__main__.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/mcp/db.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/mcp/errors.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/mcp/extraction.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/mcp/tools/__init__.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/mcp/tools/status.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/paths.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/permissions.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/semantic/__init__.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/semantic/chunking.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/semantic/embeddings.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/semantic/vector_store.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/__init__.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/chat_service.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/content_service.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/file_service.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/roles.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/status_service.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/source_registry.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/utils/__init__.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/utils/hash_utils.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/utils/logging_config.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/utils/mime.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/utils/text.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/utils/time.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/visibility.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter_cli.egg-info/dependency_links.txt +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter_cli.egg-info/entry_points.txt +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter_cli.egg-info/requires.txt +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter_cli.egg-info/top_level.txt +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/setup.cfg +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_access_control_docs.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_access_recalculate.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_bundled.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_e2e_install.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_e2e_pipeline.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_examples.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_files_rename.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_inherit_resolution.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_logging.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_no_project_root.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_package_init.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_paths_no_test_marker.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_pip_install_e2e.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_prompt_safety.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_security_layer.py +0 -0
- {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_security_permissions.py +0 -0
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: footprinter-cli
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: A local context layer for your files, browser history, chats, and email — searchable, user-owned, MCP-served.
|
|
5
5
|
Author: SwellCity Group
|
|
6
6
|
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/
|
|
8
|
-
Project-URL: Repository, https://github.com/
|
|
9
|
-
Project-URL: Issues, https://github.com/
|
|
10
|
-
Project-URL: Documentation, https://github.com/
|
|
7
|
+
Project-URL: Homepage, https://github.com/harringjohn/footprinter-cli
|
|
8
|
+
Project-URL: Repository, https://github.com/harringjohn/footprinter-cli
|
|
9
|
+
Project-URL: Issues, https://github.com/harringjohn/footprinter-cli/issues
|
|
10
|
+
Project-URL: Documentation, https://github.com/harringjohn/footprinter-cli/blob/main/README.md
|
|
11
|
+
Project-URL: Changelog, https://github.com/harringjohn/footprinter-cli/blob/main/CHANGELOG.md
|
|
11
12
|
Keywords: indexer,mcp,metadata,model-context-protocol,file-indexing,sqlite,context-engineering,personal-information-management
|
|
12
13
|
Classifier: Development Status :: 5 - Production/Stable
|
|
13
14
|
Classifier: Environment :: Console
|
|
@@ -52,6 +53,7 @@ Requires-Dist: httpx<1.0,>=0.27.0; extra == "dev"
|
|
|
52
53
|
# Footprinter
|
|
53
54
|
|
|
54
55
|
[](https://github.com/swellcitygroup/footprinter/actions/workflows/test.yml)
|
|
56
|
+
[](https://pypi.org/project/footprinter-cli/)
|
|
55
57
|
|
|
56
58
|
**A local context layer for your files, browser history, chats, and email — searchable, user-owned, and served to AI agents through [MCP](https://modelcontextprotocol.io/).**
|
|
57
59
|
|
|
@@ -75,7 +77,7 @@ curl -fsSL https://raw.githubusercontent.com/swellcitygroup/footprinter/main/scr
|
|
|
75
77
|
curl -fsSL https://raw.githubusercontent.com/swellcitygroup/footprinter/main/scripts/release/install-full.sh | bash
|
|
76
78
|
```
|
|
77
79
|
|
|
78
|
-
If you prefer to manage the install yourself, use **pipx** (recommended) —
|
|
80
|
+
If you prefer to manage the install yourself, use **pipx** (recommended) — it isolates Footprinter and sidesteps the macOS install caveats noted below:
|
|
79
81
|
|
|
80
82
|
```bash
|
|
81
83
|
brew install pipx
|
|
@@ -84,6 +86,11 @@ pipx install footprinter-cli
|
|
|
84
86
|
pipx install 'footprinter-cli[full]' # with semantic + parse
|
|
85
87
|
```
|
|
86
88
|
|
|
89
|
+
> **macOS caveats for manual installs:**
|
|
90
|
+
> - **zsh** treats `[...]` as a glob, so keep the single quotes around any bracketed extras specifier (e.g. `'footprinter-cli[full]'`). Without quotes you'll see `zsh: no matches found`.
|
|
91
|
+
> - **System and Homebrew Python** ship with PEP 668 enabled, which blocks bare `pip install` outside a venv. Use pipx (above) instead.
|
|
92
|
+
> - **python.org-distributed Python** doesn't enforce PEP 668, so a bare `pip install footprinter-cli` outside pipx or a venv may succeed but place `fp` in `/Library/Frameworks/Python.framework/Versions/<x.y>/bin`, which isn't on `PATH` by default. Use pipx (above) or the install script.
|
|
93
|
+
|
|
87
94
|
Inside an existing venv, `pip` works as expected:
|
|
88
95
|
|
|
89
96
|
```bash
|
|
@@ -171,9 +178,9 @@ All commands use the `fp` entry point.
|
|
|
171
178
|
| `fp mcp` | MCP server and access policies |
|
|
172
179
|
| `fp api` | Start the HTTP API server |
|
|
173
180
|
| `fp view` | Browse indexed data (files, folders, projects, clients, chats, emails, visits) |
|
|
174
|
-
| `fp upsert` | Create
|
|
181
|
+
| `fp upsert` | Create/update records, assign relationships, or soft-delete via `--status removed` |
|
|
175
182
|
| `fp data` | Export data, generate templates, or import metadata corrections |
|
|
176
|
-
| `fp delete` |
|
|
183
|
+
| `fp delete` | Hard-delete a super entity (irreversible) |
|
|
177
184
|
| `fp vectorize` | Manage per-record vectorization control |
|
|
178
185
|
| `fp doctor` | Post-install health check (Python version, install location, FDA, MCP wiring) |
|
|
179
186
|
| `fp uninstall` | Remove Footprinter — MCP entry, user data, package |
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Footprinter
|
|
2
2
|
|
|
3
3
|
[](https://github.com/swellcitygroup/footprinter/actions/workflows/test.yml)
|
|
4
|
+
[](https://pypi.org/project/footprinter-cli/)
|
|
4
5
|
|
|
5
6
|
**A local context layer for your files, browser history, chats, and email — searchable, user-owned, and served to AI agents through [MCP](https://modelcontextprotocol.io/).**
|
|
6
7
|
|
|
@@ -24,7 +25,7 @@ curl -fsSL https://raw.githubusercontent.com/swellcitygroup/footprinter/main/scr
|
|
|
24
25
|
curl -fsSL https://raw.githubusercontent.com/swellcitygroup/footprinter/main/scripts/release/install-full.sh | bash
|
|
25
26
|
```
|
|
26
27
|
|
|
27
|
-
If you prefer to manage the install yourself, use **pipx** (recommended) —
|
|
28
|
+
If you prefer to manage the install yourself, use **pipx** (recommended) — it isolates Footprinter and sidesteps the macOS install caveats noted below:
|
|
28
29
|
|
|
29
30
|
```bash
|
|
30
31
|
brew install pipx
|
|
@@ -33,6 +34,11 @@ pipx install footprinter-cli
|
|
|
33
34
|
pipx install 'footprinter-cli[full]' # with semantic + parse
|
|
34
35
|
```
|
|
35
36
|
|
|
37
|
+
> **macOS caveats for manual installs:**
|
|
38
|
+
> - **zsh** treats `[...]` as a glob, so keep the single quotes around any bracketed extras specifier (e.g. `'footprinter-cli[full]'`). Without quotes you'll see `zsh: no matches found`.
|
|
39
|
+
> - **System and Homebrew Python** ship with PEP 668 enabled, which blocks bare `pip install` outside a venv. Use pipx (above) instead.
|
|
40
|
+
> - **python.org-distributed Python** doesn't enforce PEP 668, so a bare `pip install footprinter-cli` outside pipx or a venv may succeed but place `fp` in `/Library/Frameworks/Python.framework/Versions/<x.y>/bin`, which isn't on `PATH` by default. Use pipx (above) or the install script.
|
|
41
|
+
|
|
36
42
|
Inside an existing venv, `pip` works as expected:
|
|
37
43
|
|
|
38
44
|
```bash
|
|
@@ -120,9 +126,9 @@ All commands use the `fp` entry point.
|
|
|
120
126
|
| `fp mcp` | MCP server and access policies |
|
|
121
127
|
| `fp api` | Start the HTTP API server |
|
|
122
128
|
| `fp view` | Browse indexed data (files, folders, projects, clients, chats, emails, visits) |
|
|
123
|
-
| `fp upsert` | Create
|
|
129
|
+
| `fp upsert` | Create/update records, assign relationships, or soft-delete via `--status removed` |
|
|
124
130
|
| `fp data` | Export data, generate templates, or import metadata corrections |
|
|
125
|
-
| `fp delete` |
|
|
131
|
+
| `fp delete` | Hard-delete a super entity (irreversible) |
|
|
126
132
|
| `fp vectorize` | Manage per-record vectorization control |
|
|
127
133
|
| `fp doctor` | Post-install health check (Python version, install location, FDA, MCP wiring) |
|
|
128
134
|
| `fp uninstall` | Remove Footprinter — MCP entry, user data, package |
|
|
@@ -46,7 +46,7 @@ def _is_inherit_source(source: str) -> bool:
|
|
|
46
46
|
# table: SQL table name
|
|
47
47
|
# has_visibility: has mcp_view column
|
|
48
48
|
# has_permissions: has mcp_read column
|
|
49
|
-
# has_status: has status column (filter WHERE status
|
|
49
|
+
# has_status: has status column (filter WHERE status = 'listed')
|
|
50
50
|
# has_project_id: has project_id FK
|
|
51
51
|
# has_client_id: has client_id FK
|
|
52
52
|
# has_account: has account column
|
|
@@ -129,7 +129,7 @@ def _get_all_ids(conn: sqlite3.Connection, entity_type: str) -> list[int]:
|
|
|
129
129
|
meta = ENTITY_META[entity_type]
|
|
130
130
|
table = meta["table"]
|
|
131
131
|
if meta["has_status"]:
|
|
132
|
-
rows = conn.execute(f"SELECT id FROM {table} WHERE status
|
|
132
|
+
rows = conn.execute(f"SELECT id FROM {table} WHERE status = 'listed'").fetchall()
|
|
133
133
|
else:
|
|
134
134
|
rows = conn.execute(f"SELECT id FROM {table}").fetchall()
|
|
135
135
|
return [r["id"] for r in rows]
|
|
@@ -162,7 +162,7 @@ def _get_ids_for_scope(conn: sqlite3.Connection, scope: str) -> dict[str, list[i
|
|
|
162
162
|
table = meta["table"]
|
|
163
163
|
where = "account = ?"
|
|
164
164
|
if meta["has_status"]:
|
|
165
|
-
where += " AND status
|
|
165
|
+
where += " AND status = 'listed'"
|
|
166
166
|
rows = conn.execute(f"SELECT id FROM {table} WHERE {where}", (value,)).fetchall()
|
|
167
167
|
ids = [r["id"] for r in rows]
|
|
168
168
|
if ids:
|
|
@@ -183,7 +183,7 @@ def _get_ids_for_scope(conn: sqlite3.Connection, scope: str) -> dict[str, list[i
|
|
|
183
183
|
table = meta["table"]
|
|
184
184
|
where = f"{path_col} LIKE ? ESCAPE '\\'"
|
|
185
185
|
if meta["has_status"]:
|
|
186
|
-
where += " AND status
|
|
186
|
+
where += " AND status = 'listed'"
|
|
187
187
|
rows = conn.execute(
|
|
188
188
|
f"SELECT id FROM {table} WHERE {where}",
|
|
189
189
|
(escaped + "%",),
|
|
@@ -210,7 +210,7 @@ def _get_ids_for_scope(conn: sqlite3.Connection, scope: str) -> dict[str, list[i
|
|
|
210
210
|
table = meta["table"]
|
|
211
211
|
where = "project_id = ?"
|
|
212
212
|
if meta["has_status"]:
|
|
213
|
-
where += " AND status
|
|
213
|
+
where += " AND status = 'listed'"
|
|
214
214
|
rows = conn.execute(f"SELECT id FROM {table} WHERE {where}", (project_id,)).fetchall()
|
|
215
215
|
ids = [r["id"] for r in rows]
|
|
216
216
|
if ids:
|
|
@@ -248,7 +248,7 @@ def _get_ids_for_scope(conn: sqlite3.Connection, scope: str) -> dict[str, list[i
|
|
|
248
248
|
table = meta["table"]
|
|
249
249
|
where = "client_id = ?"
|
|
250
250
|
if meta["has_status"]:
|
|
251
|
-
where += " AND status
|
|
251
|
+
where += " AND status = 'listed'"
|
|
252
252
|
rows = conn.execute(f"SELECT id FROM {table} WHERE {where}", (client_id,)).fetchall()
|
|
253
253
|
if rows:
|
|
254
254
|
id_sets.setdefault(etype, {}).update({r["id"]: None for r in rows})
|
|
@@ -4,6 +4,7 @@ from typing import Optional
|
|
|
4
4
|
|
|
5
5
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
6
6
|
|
|
7
|
+
from footprinter.api import MAX_LIMIT
|
|
7
8
|
from footprinter.api.db import get_conn
|
|
8
9
|
from footprinter.services import (
|
|
9
10
|
chat_service,
|
|
@@ -36,7 +37,7 @@ def list_files(
|
|
|
36
37
|
source: Optional[str] = Query(None, description="Comma-separated source filter"),
|
|
37
38
|
status: Optional[str] = Query(None, description="Comma-separated status filter"),
|
|
38
39
|
content_type: Optional[str] = None,
|
|
39
|
-
limit: int = 50,
|
|
40
|
+
limit: int = Query(50, ge=1, le=MAX_LIMIT),
|
|
40
41
|
page: int = 1,
|
|
41
42
|
):
|
|
42
43
|
source_list = [s.strip() for s in source.split(",")] if source else None
|
|
@@ -71,7 +72,7 @@ def list_emails(
|
|
|
71
72
|
has_attachments: Optional[bool] = None,
|
|
72
73
|
sort_by: str = "received_at",
|
|
73
74
|
order: str = "desc",
|
|
74
|
-
limit: int = 50,
|
|
75
|
+
limit: int = Query(50, ge=1, le=MAX_LIMIT),
|
|
75
76
|
page: int = 1,
|
|
76
77
|
):
|
|
77
78
|
return email_service.list_(
|
|
@@ -105,7 +106,7 @@ def list_chats(
|
|
|
105
106
|
sort_by: str = "modified_at",
|
|
106
107
|
order: str = "desc",
|
|
107
108
|
status: Optional[str] = Query(None, description="Comma-separated status filter"),
|
|
108
|
-
limit: int = 50,
|
|
109
|
+
limit: int = Query(50, ge=1, le=MAX_LIMIT),
|
|
109
110
|
page: int = 1,
|
|
110
111
|
):
|
|
111
112
|
status_list = [s.strip() for s in status.split(",")] if status else None
|
|
@@ -137,7 +138,7 @@ def list_projects(
|
|
|
137
138
|
status: Optional[str] = Query(None, description="Comma-separated status filter"),
|
|
138
139
|
client: Optional[str] = None,
|
|
139
140
|
project_type: Optional[str] = None,
|
|
140
|
-
limit: int = 50,
|
|
141
|
+
limit: int = Query(50, ge=1, le=MAX_LIMIT),
|
|
141
142
|
page: int = 1,
|
|
142
143
|
):
|
|
143
144
|
include_list = [s.strip() for s in include.split(",")] if include else None
|
|
@@ -172,7 +173,7 @@ def list_clients(
|
|
|
172
173
|
conn=Depends(get_conn),
|
|
173
174
|
include: Optional[str] = Query(None, description="Comma-separated includes"),
|
|
174
175
|
status: Optional[str] = Query(None, description="Comma-separated status filter"),
|
|
175
|
-
limit: int = 50,
|
|
176
|
+
limit: int = Query(50, ge=1, le=MAX_LIMIT),
|
|
176
177
|
page: int = 1,
|
|
177
178
|
):
|
|
178
179
|
include_list = [s.strip() for s in include.split(",")] if include else None
|
|
@@ -216,16 +217,19 @@ def list_folders(
|
|
|
216
217
|
project_id: Optional[int] = None,
|
|
217
218
|
depth: Optional[int] = 1,
|
|
218
219
|
include_hidden: bool = False,
|
|
220
|
+
status: Optional[str] = Query(None, description="Comma-separated status filter"),
|
|
219
221
|
sort_by: str = "size",
|
|
220
|
-
limit: int = 50,
|
|
222
|
+
limit: int = Query(50, ge=1, le=MAX_LIMIT),
|
|
221
223
|
page: int = 1,
|
|
222
224
|
):
|
|
225
|
+
status_list = [s.strip() for s in status.split(",")] if status else None
|
|
223
226
|
return folder_service.list_(
|
|
224
227
|
conn,
|
|
225
228
|
role=Role.ADMIN,
|
|
226
229
|
project_id=project_id,
|
|
227
230
|
depth=depth,
|
|
228
231
|
include_hidden=include_hidden,
|
|
232
|
+
status=status_list,
|
|
229
233
|
sort_by=sort_by,
|
|
230
234
|
limit=limit,
|
|
231
235
|
page=page,
|
|
@@ -241,7 +245,11 @@ def get_folder(folder_id: int, conn=Depends(get_conn)):
|
|
|
241
245
|
|
|
242
246
|
|
|
243
247
|
@router.get("/visits")
|
|
244
|
-
def list_visits(
|
|
248
|
+
def list_visits(
|
|
249
|
+
conn=Depends(get_conn),
|
|
250
|
+
limit: int = Query(50, ge=1, le=MAX_LIMIT),
|
|
251
|
+
page: int = 1,
|
|
252
|
+
):
|
|
245
253
|
return visit_service.list_(conn, role=Role.ADMIN, limit=limit, page=page)
|
|
246
254
|
|
|
247
255
|
|
|
@@ -4,6 +4,7 @@ from typing import Optional
|
|
|
4
4
|
|
|
5
5
|
from fastapi import APIRouter, Depends, Query
|
|
6
6
|
|
|
7
|
+
from footprinter.api import MAX_LIMIT
|
|
7
8
|
from footprinter.api.db import get_conn
|
|
8
9
|
from footprinter.services import search_service
|
|
9
10
|
from footprinter.services.roles import Role
|
|
@@ -20,7 +21,7 @@ def search(
|
|
|
20
21
|
client: Optional[str] = None,
|
|
21
22
|
date_from: Optional[str] = None,
|
|
22
23
|
date_to: Optional[str] = None,
|
|
23
|
-
limit: int = 50,
|
|
24
|
+
limit: int = Query(50, ge=1, le=MAX_LIMIT),
|
|
24
25
|
account: Optional[str] = None,
|
|
25
26
|
sender: Optional[str] = None,
|
|
26
27
|
days_back: Optional[int] = None,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
4
4
|
|
|
5
|
+
from footprinter.api import MAX_LIMIT
|
|
5
6
|
from footprinter.api.db import get_conn
|
|
6
7
|
from footprinter.services import semantic_service
|
|
7
8
|
from footprinter.services.roles import Role
|
|
@@ -16,7 +17,7 @@ def semantic_search(
|
|
|
16
17
|
conn=Depends(get_conn),
|
|
17
18
|
query: str = Query(..., min_length=3, description="Search query (minimum 3 characters)"),
|
|
18
19
|
source: str = Query("all", description="Source to search: chats, files, or all"),
|
|
19
|
-
limit: int = 10,
|
|
20
|
+
limit: int = Query(10, ge=1, le=MAX_LIMIT),
|
|
20
21
|
):
|
|
21
22
|
"""Semantic (vector) search across indexed content."""
|
|
22
23
|
if source not in _VALID_SOURCES:
|
|
@@ -17,7 +17,7 @@ browsers:
|
|
|
17
17
|
#
|
|
18
18
|
# Files matching these patterns are NEVER scanned — they don't appear in the
|
|
19
19
|
# database at all. This is different from hidden files (dot-files/dot-directories),
|
|
20
|
-
# which ARE scanned and indexed with status='
|
|
20
|
+
# which ARE scanned and indexed with status='unlisted' so they appear in the catalog
|
|
21
21
|
# but are excluded from Drive sync.
|
|
22
22
|
#
|
|
23
23
|
# Two tiers: always (all folders), sensitive (all folders)
|
|
@@ -41,8 +41,11 @@ exclusions:
|
|
|
41
41
|
- ".*/\\.sfdx/.*" # Salesforce DX CLI
|
|
42
42
|
- ".*/\\.docker/.*" # Docker config
|
|
43
43
|
- ".*/\\.cumulusci/.*" # CumulusCI cache
|
|
44
|
+
- ".*/\\.browser_state/.*" # Playwright/Puppeteer browser state
|
|
45
|
+
- ".*/\\.context/.*" # IDE/agent context directories
|
|
46
|
+
- ".*/\\.ai-dev/.*" # AI dev tool scratch dirs
|
|
44
47
|
# Home-level Claude dirs only (keep .claude within Work/Personal)
|
|
45
|
-
- "^~/\\.claude/.*" # Home-level .claude
|
|
48
|
+
- "^~/\\.claude/.*" # Home-level .claude (includes session-env snapshots)
|
|
46
49
|
- "^~/\\.claude-worktrees/.*" # Home-level .claude-worktrees
|
|
47
50
|
# Git internals (but NOT .gitignore, .gitattributes - those are kept)
|
|
48
51
|
- ".*/\\.git/.*" # Git internal directory contents
|
|
@@ -115,17 +118,19 @@ vectorization:
|
|
|
115
118
|
# but not embedded in the vector store.
|
|
116
119
|
#
|
|
117
120
|
# Common exclusions — system caches, IDE output, and tool output that
|
|
118
|
-
# match text file extensions but contain no meaningful prose content
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
121
|
+
# match text file extensions but contain no meaningful prose content.
|
|
122
|
+
exclude_patterns:
|
|
123
|
+
- "**/Photos Library.photoslibrary/**" # macOS Spotlight index cache (.txt)
|
|
124
|
+
- "**/.claude/debug/**" # Claude Code debug logs
|
|
125
|
+
- "**/.claude/paste-cache/**" # Claude Code paste cache
|
|
126
|
+
- "**/.claude/cache/**" # Claude Code cache
|
|
127
|
+
- "**/.claude/projects/**" # Claude Code session data
|
|
128
|
+
- "**/.claude/plans/**" # Claude Code auto-generated plans
|
|
129
|
+
- "**/.claude/plugins/**" # Claude Code plugin cache
|
|
130
|
+
- "**/.cci/**" # CumulusCI cache
|
|
131
|
+
- "**/.context/**" # IDE context directories
|
|
132
|
+
- "**/.github/**" # GitHub workflow files (FTS-only)
|
|
133
|
+
- "**/.ai-dev/**" # AI dev tool scratch dirs
|
|
129
134
|
|
|
130
135
|
# Source registry seeds — loaded into the sources table on init
|
|
131
136
|
# Connector sources added by: fp connect install <name>
|
|
@@ -49,16 +49,7 @@ C_ERROR = "red"
|
|
|
49
49
|
C_INFO = "cyan"
|
|
50
50
|
C_DIM = "dim"
|
|
51
51
|
|
|
52
|
-
VALID_STATUSES = frozenset(
|
|
53
|
-
{
|
|
54
|
-
"active",
|
|
55
|
-
"paused",
|
|
56
|
-
"completed",
|
|
57
|
-
"abandoned",
|
|
58
|
-
"archived",
|
|
59
|
-
"merged",
|
|
60
|
-
}
|
|
61
|
-
)
|
|
52
|
+
VALID_STATUSES = frozenset({"listed", "unlisted", "removed"})
|
|
62
53
|
|
|
63
54
|
ALLOWED_TABLES = frozenset({"clients", "projects"})
|
|
64
55
|
ALLOWED_COLUMNS = frozenset({"name", "project_name"})
|
|
@@ -113,7 +104,7 @@ def open_database(db_path=None):
|
|
|
113
104
|
|
|
114
105
|
Like ``open_db`` but yields the full ``Database`` wrapper instead of a
|
|
115
106
|
raw ``sqlite3.Connection``. Use this when callers need methods only
|
|
116
|
-
available on the wrapper
|
|
107
|
+
available on the wrapper.
|
|
117
108
|
|
|
118
109
|
Exits with code 1 if the database file does not exist.
|
|
119
110
|
"""
|
|
@@ -128,7 +128,7 @@ def check_file_path(conn: sqlite3.Connection, path: str, json_output: bool, verb
|
|
|
128
128
|
expanded = os.path.expanduser(os.path.normpath(path))
|
|
129
129
|
|
|
130
130
|
row = conn.execute(
|
|
131
|
-
"SELECT id, name, project_id FROM files WHERE path = ? AND status
|
|
131
|
+
"SELECT id, name, project_id FROM files WHERE path = ? AND status = 'listed'",
|
|
132
132
|
(expanded,),
|
|
133
133
|
).fetchone()
|
|
134
134
|
|
|
@@ -200,7 +200,7 @@ def check_folder(conn: sqlite3.Connection, path: str, json_output: bool, verbose
|
|
|
200
200
|
expanded += os.sep
|
|
201
201
|
|
|
202
202
|
rows = conn.execute(
|
|
203
|
-
"SELECT id, name FROM files WHERE path LIKE ? AND status
|
|
203
|
+
"SELECT id, name FROM files WHERE path LIKE ? AND status = 'listed'",
|
|
204
204
|
(expanded + "%",),
|
|
205
205
|
).fetchall()
|
|
206
206
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""fp api — start the HTTP API server."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from footprinter.cli._common import FORMATTER
|
|
6
|
+
|
|
7
|
+
_LOOPBACK_HOSTS = {"127.0.0.1", "localhost", "::1"}
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _start_api(args) -> None:
|
|
11
|
+
if args.host not in _LOOPBACK_HOSTS:
|
|
12
|
+
if not args.allow_insecure_bind:
|
|
13
|
+
print(
|
|
14
|
+
f"error: refusing to bind to non-loopback host {args.host!r}.\n"
|
|
15
|
+
"The Footprinter HTTP API has no authentication; binding outside "
|
|
16
|
+
"loopback exposes indexed data to anyone on the network.\n"
|
|
17
|
+
"Pass --allow-insecure-bind to override.",
|
|
18
|
+
file=sys.stderr,
|
|
19
|
+
)
|
|
20
|
+
raise SystemExit(2)
|
|
21
|
+
print(
|
|
22
|
+
f"WARNING: binding to {args.host} — the HTTP API has no authentication. "
|
|
23
|
+
"Anyone reachable on this network can read indexed files, emails, "
|
|
24
|
+
"chats, and browser history.",
|
|
25
|
+
file=sys.stderr,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
from footprinter.api.server import main
|
|
29
|
+
|
|
30
|
+
main(host=args.host, port=args.port)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def register(subparsers) -> None:
|
|
34
|
+
"""Register the ``api`` subcommand."""
|
|
35
|
+
parser = subparsers.add_parser(
|
|
36
|
+
"api",
|
|
37
|
+
help="Start the HTTP API server",
|
|
38
|
+
description=(
|
|
39
|
+
"Start the Footprinter HTTP API server.\n\n"
|
|
40
|
+
"Provides REST endpoints for programmatic access to indexed data.\n"
|
|
41
|
+
"Auto-generated docs available at /docs (Swagger UI)."
|
|
42
|
+
),
|
|
43
|
+
epilog=(
|
|
44
|
+
"examples:\n"
|
|
45
|
+
" fp api Start on localhost:8000\n"
|
|
46
|
+
" fp api --port 9000 Start on custom port\n"
|
|
47
|
+
" fp api --host 0.0.0.0 --allow-insecure-bind Listen on all interfaces (no auth!)"
|
|
48
|
+
),
|
|
49
|
+
formatter_class=FORMATTER,
|
|
50
|
+
)
|
|
51
|
+
parser.add_argument("--host", default="127.0.0.1", help="Host to bind (default: 127.0.0.1)")
|
|
52
|
+
parser.add_argument("--port", type=int, default=8000, help="Port to bind (default: 8000)")
|
|
53
|
+
parser.add_argument(
|
|
54
|
+
"--allow-insecure-bind",
|
|
55
|
+
action="store_true",
|
|
56
|
+
help=(
|
|
57
|
+
"Allow binding to non-loopback interfaces. The HTTP API has no "
|
|
58
|
+
"authentication; anyone on the network can read indexed data."
|
|
59
|
+
),
|
|
60
|
+
)
|
|
61
|
+
parser.set_defaults(func=_start_api)
|
|
@@ -45,7 +45,7 @@ def _export_query(entity_type: str, status_filter: str | None) -> tuple[str, lis
|
|
|
45
45
|
sql += " WHERE status = ?"
|
|
46
46
|
params.append(status_filter)
|
|
47
47
|
else:
|
|
48
|
-
sql += " WHERE status
|
|
48
|
+
sql += " WHERE status = 'listed'"
|
|
49
49
|
sql += " ORDER BY name"
|
|
50
50
|
else:
|
|
51
51
|
sql = (
|
|
@@ -58,7 +58,7 @@ def _export_query(entity_type: str, status_filter: str | None) -> tuple[str, lis
|
|
|
58
58
|
sql += " WHERE p.status = ?"
|
|
59
59
|
params.append(status_filter)
|
|
60
60
|
else:
|
|
61
|
-
sql += " WHERE p.status
|
|
61
|
+
sql += " WHERE p.status = 'listed'"
|
|
62
62
|
sql += " ORDER BY p.project_name"
|
|
63
63
|
return sql, params
|
|
64
64
|
|
|
@@ -74,10 +74,10 @@ TEMPLATE_ROWS: dict[str, list[dict]] = {
|
|
|
74
74
|
"client_type": "external",
|
|
75
75
|
"slug": "",
|
|
76
76
|
"path_pattern": "~/Work/clients/acme/",
|
|
77
|
-
"status": "
|
|
77
|
+
"status": "listed",
|
|
78
78
|
},
|
|
79
|
-
{"name": "Internal Tools", "client_type": "internal", "slug": "", "path_pattern": "", "status": "
|
|
80
|
-
{"name": "Side Project", "client_type": "personal", "slug": "", "path_pattern": "", "status": "
|
|
79
|
+
{"name": "Internal Tools", "client_type": "internal", "slug": "", "path_pattern": "", "status": "listed"},
|
|
80
|
+
{"name": "Side Project", "client_type": "personal", "slug": "", "path_pattern": "", "status": "listed"},
|
|
81
81
|
],
|
|
82
82
|
"project": [
|
|
83
83
|
{
|
|
@@ -87,7 +87,7 @@ TEMPLATE_ROWS: dict[str, list[dict]] = {
|
|
|
87
87
|
"project_type": "python",
|
|
88
88
|
"description": "A web application",
|
|
89
89
|
"github_url": "",
|
|
90
|
-
"status": "
|
|
90
|
+
"status": "listed",
|
|
91
91
|
},
|
|
92
92
|
{
|
|
93
93
|
"project_name": "Documentation",
|
|
@@ -96,7 +96,7 @@ TEMPLATE_ROWS: dict[str, list[dict]] = {
|
|
|
96
96
|
"project_type": "docs",
|
|
97
97
|
"description": "Internal documentation",
|
|
98
98
|
"github_url": "",
|
|
99
|
-
"status": "
|
|
99
|
+
"status": "listed",
|
|
100
100
|
},
|
|
101
101
|
{
|
|
102
102
|
"project_name": "Mobile App",
|
|
@@ -105,7 +105,7 @@ TEMPLATE_ROWS: dict[str, list[dict]] = {
|
|
|
105
105
|
"project_type": "typescript",
|
|
106
106
|
"description": "Mobile app",
|
|
107
107
|
"github_url": "",
|
|
108
|
-
"status": "
|
|
108
|
+
"status": "listed",
|
|
109
109
|
},
|
|
110
110
|
],
|
|
111
111
|
}
|
|
@@ -113,10 +113,10 @@ TEMPLATE_ROWS: dict[str, list[dict]] = {
|
|
|
113
113
|
VALID_VALUES_NOTES: dict[str, dict[str, str]] = {
|
|
114
114
|
"client": {
|
|
115
115
|
"client_type": "external, internal, personal",
|
|
116
|
-
"status": "
|
|
116
|
+
"status": "listed, unlisted, removed",
|
|
117
117
|
},
|
|
118
118
|
"project": {
|
|
119
|
-
"status": "
|
|
119
|
+
"status": "listed, unlisted, removed",
|
|
120
120
|
},
|
|
121
121
|
}
|
|
122
122
|
|
|
@@ -165,7 +165,7 @@ DATA_SOURCE_SPECS: dict[str, DataSourceSpec] = {
|
|
|
165
165
|
"name": "readme.md",
|
|
166
166
|
"path": "/Users/me/Work/readme.md",
|
|
167
167
|
"source": "local",
|
|
168
|
-
"status": "
|
|
168
|
+
"status": "listed",
|
|
169
169
|
"content_type": "markdown",
|
|
170
170
|
"size_bytes": "1024",
|
|
171
171
|
"modified_at": "2026-01-15T10:00:00Z",
|
|
@@ -190,7 +190,7 @@ DATA_SOURCE_SPECS: dict[str, DataSourceSpec] = {
|
|
|
190
190
|
},
|
|
191
191
|
],
|
|
192
192
|
valid_values={
|
|
193
|
-
"status": "
|
|
193
|
+
"status": "listed, unlisted, removed",
|
|
194
194
|
"mcp_view": "hidden, opaque, visible, inherit",
|
|
195
195
|
"mcp_read": "allow, deny, inherit",
|
|
196
196
|
},
|
|
@@ -219,7 +219,7 @@ DATA_SOURCE_SPECS: dict[str, DataSourceSpec] = {
|
|
|
219
219
|
"relative_path": "Work",
|
|
220
220
|
"name": "Work",
|
|
221
221
|
"source": "local",
|
|
222
|
-
"status": "
|
|
222
|
+
"status": "listed",
|
|
223
223
|
"project_id": "1",
|
|
224
224
|
"client_id": "",
|
|
225
225
|
"mcp_view": "visible",
|
|
@@ -231,7 +231,7 @@ DATA_SOURCE_SPECS: dict[str, DataSourceSpec] = {
|
|
|
231
231
|
"relative_path": "Personal",
|
|
232
232
|
"name": "Personal",
|
|
233
233
|
"source": "local",
|
|
234
|
-
"status": "
|
|
234
|
+
"status": "listed",
|
|
235
235
|
"project_id": "",
|
|
236
236
|
"client_id": "",
|
|
237
237
|
"mcp_view": "inherit",
|
|
@@ -239,7 +239,7 @@ DATA_SOURCE_SPECS: dict[str, DataSourceSpec] = {
|
|
|
239
239
|
},
|
|
240
240
|
],
|
|
241
241
|
valid_values={
|
|
242
|
-
"status": "
|
|
242
|
+
"status": "listed, unlisted, removed",
|
|
243
243
|
"mcp_view": "hidden, opaque, visible, inherit",
|
|
244
244
|
"mcp_read": "allow, deny, inherit",
|
|
245
245
|
},
|
|
@@ -270,7 +270,7 @@ DATA_SOURCE_SPECS: dict[str, DataSourceSpec] = {
|
|
|
270
270
|
"subject": "Project Update",
|
|
271
271
|
"from_address": "sender@example.com",
|
|
272
272
|
"received_at": "2026-02-01T09:00:00Z",
|
|
273
|
-
"status": "
|
|
273
|
+
"status": "listed",
|
|
274
274
|
"project_id": "1",
|
|
275
275
|
"client_id": "1",
|
|
276
276
|
"mcp_view": "visible",
|
|
@@ -283,7 +283,7 @@ DATA_SOURCE_SPECS: dict[str, DataSourceSpec] = {
|
|
|
283
283
|
"subject": "Newsletter",
|
|
284
284
|
"from_address": "news@example.com",
|
|
285
285
|
"received_at": "2026-02-02T09:00:00Z",
|
|
286
|
-
"status": "
|
|
286
|
+
"status": "listed",
|
|
287
287
|
"project_id": "",
|
|
288
288
|
"client_id": "",
|
|
289
289
|
"mcp_view": "inherit",
|
|
@@ -291,7 +291,7 @@ DATA_SOURCE_SPECS: dict[str, DataSourceSpec] = {
|
|
|
291
291
|
},
|
|
292
292
|
],
|
|
293
293
|
valid_values={
|
|
294
|
-
"status": "
|
|
294
|
+
"status": "listed, unlisted, removed",
|
|
295
295
|
"mcp_view": "hidden, opaque, visible, inherit",
|
|
296
296
|
"mcp_read": "allow, deny, inherit",
|
|
297
297
|
},
|
|
@@ -322,7 +322,7 @@ DATA_SOURCE_SPECS: dict[str, DataSourceSpec] = {
|
|
|
322
322
|
"account": "personal",
|
|
323
323
|
"title": "Architecture Chat",
|
|
324
324
|
"message_count": "5",
|
|
325
|
-
"status": "
|
|
325
|
+
"status": "listed",
|
|
326
326
|
"created_at": "2026-01-10T08:00:00Z",
|
|
327
327
|
"updated_at": "2026-01-10T09:00:00Z",
|
|
328
328
|
"project_id": "1",
|
|
@@ -336,7 +336,7 @@ DATA_SOURCE_SPECS: dict[str, DataSourceSpec] = {
|
|
|
336
336
|
"account": "personal",
|
|
337
337
|
"title": "Random Chat",
|
|
338
338
|
"message_count": "3",
|
|
339
|
-
"status": "
|
|
339
|
+
"status": "listed",
|
|
340
340
|
"created_at": "2026-01-11T08:00:00Z",
|
|
341
341
|
"updated_at": "2026-01-11T09:00:00Z",
|
|
342
342
|
"project_id": "",
|
|
@@ -346,7 +346,7 @@ DATA_SOURCE_SPECS: dict[str, DataSourceSpec] = {
|
|
|
346
346
|
},
|
|
347
347
|
],
|
|
348
348
|
valid_values={
|
|
349
|
-
"status": "
|
|
349
|
+
"status": "listed, unlisted, removed",
|
|
350
350
|
"mcp_view": "hidden, opaque, visible, inherit",
|
|
351
351
|
"mcp_read": "allow, deny, inherit",
|
|
352
352
|
},
|
|
@@ -414,7 +414,7 @@ DATA_SOURCE_SPECS: dict[str, DataSourceSpec] = {
|
|
|
414
414
|
"title": "Example",
|
|
415
415
|
"visit_time": "2026-03-01T12:00:00Z",
|
|
416
416
|
"browser": "safari",
|
|
417
|
-
"status": "
|
|
417
|
+
"status": "listed",
|
|
418
418
|
"project_id": "1",
|
|
419
419
|
"client_id": "1",
|
|
420
420
|
"mcp_view": "visible",
|
|
@@ -426,7 +426,7 @@ DATA_SOURCE_SPECS: dict[str, DataSourceSpec] = {
|
|
|
426
426
|
"title": "News",
|
|
427
427
|
"visit_time": "2026-03-02T12:00:00Z",
|
|
428
428
|
"browser": "chrome",
|
|
429
|
-
"status": "
|
|
429
|
+
"status": "listed",
|
|
430
430
|
"project_id": "",
|
|
431
431
|
"client_id": "",
|
|
432
432
|
"mcp_view": "inherit",
|
|
@@ -434,7 +434,7 @@ DATA_SOURCE_SPECS: dict[str, DataSourceSpec] = {
|
|
|
434
434
|
},
|
|
435
435
|
],
|
|
436
436
|
valid_values={
|
|
437
|
-
"status": "
|
|
437
|
+
"status": "listed, unlisted, removed",
|
|
438
438
|
"mcp_view": "hidden, opaque, visible, inherit",
|
|
439
439
|
"mcp_read": "allow, deny, inherit",
|
|
440
440
|
},
|
|
@@ -541,7 +541,7 @@ def _handle_export_data_source(args) -> None:
|
|
|
541
541
|
sql += " WHERE status = ?"
|
|
542
542
|
params.append(status_filter)
|
|
543
543
|
else:
|
|
544
|
-
sql += " WHERE status
|
|
544
|
+
sql += " WHERE status = 'listed'"
|
|
545
545
|
elif status_filter:
|
|
546
546
|
print(
|
|
547
547
|
f"Entity '{noun}' does not have a status column.",
|