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.
Files changed (167) hide show
  1. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/PKG-INFO +15 -8
  2. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/README.md +9 -3
  3. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/access.py +6 -6
  4. footprinter_cli-1.0.2/footprinter/api/__init__.py +4 -0
  5. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/api/entities.py +15 -7
  6. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/api/search.py +2 -1
  7. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/api/semantic.py +2 -1
  8. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/bundled/config.example.yaml +18 -13
  9. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/_common.py +2 -11
  10. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/_policy_helpers.py +2 -2
  11. footprinter_cli-1.0.2/footprinter/cli/api_cmd.py +61 -0
  12. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/data.py +25 -25
  13. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/delete.py +24 -9
  14. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/doctor.py +5 -4
  15. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/ingest.py +25 -13
  16. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/mcp_cmd.py +2 -2
  17. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/mcp_setup.py +29 -9
  18. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/setup.py +75 -57
  19. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/status.py +22 -8
  20. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/uninstall.py +42 -17
  21. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/upsert.py +4 -5
  22. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/vectorize_cmd.py +3 -3
  23. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/view.py +22 -1
  24. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/browser.py +23 -7
  25. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/chats.py +11 -24
  26. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/clients.py +44 -4
  27. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/emails.py +11 -4
  28. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/files.py +23 -19
  29. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/folders.py +118 -44
  30. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/projects.py +47 -59
  31. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/search.py +98 -29
  32. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/status.py +18 -18
  33. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/adapters/local_files.py +8 -2
  34. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/adapters/local_folders.py +26 -2
  35. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/adapters/protocol.py +5 -0
  36. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/cli.py +13 -13
  37. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/db/migration.py +50 -7
  38. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/db/schema.py +61 -24
  39. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/file_indexer.py +13 -3
  40. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/file_scanner.py +15 -5
  41. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/folder_indexer.py +118 -13
  42. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/orchestrator.py +8 -11
  43. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/pipe_runner.py +9 -1
  44. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/processing.py +1 -1
  45. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/status.py +8 -7
  46. footprinter_cli-1.0.2/footprinter/mcp/resources/__init__.py +1 -0
  47. footprinter_cli-1.0.2/footprinter/mcp/resources/context.py +67 -0
  48. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/mcp/server.py +3 -0
  49. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/mcp/tools/navigation.py +18 -3
  50. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/mcp/tools/read.py +2 -0
  51. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/mcp/tools/search.py +8 -0
  52. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/mcp/tools/semantic.py +13 -1
  53. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/semantic/hybrid_search.py +2 -2
  54. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/access_service.py +15 -5
  55. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/client_service.py +14 -5
  56. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/email_service.py +2 -0
  57. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/folder_service.py +16 -2
  58. footprinter_cli-1.0.2/footprinter/services/includes.py +48 -0
  59. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/ingest_service.py +68 -2
  60. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/project_service.py +15 -6
  61. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/search_service.py +18 -0
  62. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/semantic_service.py +24 -4
  63. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/visit_service.py +3 -1
  64. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter_cli.egg-info/PKG-INFO +15 -8
  65. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter_cli.egg-info/SOURCES.txt +2 -1
  66. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/pyproject.toml +6 -5
  67. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_access_control_bypasses.py +2 -2
  68. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_build_status_filter.py +7 -7
  69. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_edit_recalculate.py +1 -1
  70. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_files_surface.py +4 -4
  71. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_resolver.py +23 -9
  72. footprinter_cli-1.0.1/footprinter/api/__init__.py +0 -1
  73. footprinter_cli-1.0.1/footprinter/cli/api_cmd.py +0 -32
  74. footprinter_cli-1.0.1/footprinter/ingest/chat_dedup.py +0 -156
  75. footprinter_cli-1.0.1/footprinter/services/includes.py +0 -19
  76. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/LICENSE +0 -0
  77. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/__init__.py +0 -0
  78. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/api/db.py +0 -0
  79. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/api/server.py +0 -0
  80. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/api/status.py +0 -0
  81. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/bundled/__init__.py +0 -0
  82. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/bundled/patterns/context_patterns.yaml +0 -0
  83. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/bundled/patterns/extensions.yaml +0 -0
  84. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/bundled/patterns/filename_patterns.yaml +0 -0
  85. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/bundled/patterns/mime_mappings.yaml +0 -0
  86. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/bundled/patterns/salesforce_rules.yaml +0 -0
  87. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/bundled/patterns/security_patterns.yaml +0 -0
  88. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/__init__.py +0 -0
  89. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/__main__.py +0 -0
  90. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/_prompt.py +0 -0
  91. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/connect.py +0 -0
  92. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/search.py +0 -0
  93. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/search_cmd.py +0 -0
  94. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/cli/status_cmd.py +0 -0
  95. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/connectors/__init__.py +0 -0
  96. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/connectors/config_utils.py +0 -0
  97. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/__init__.py +0 -0
  98. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/messages.py +0 -0
  99. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/policies.py +0 -0
  100. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/sql_utils.py +0 -0
  101. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/db/uploads.py +0 -0
  102. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/__init__.py +0 -0
  103. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/adapters/__init__.py +0 -0
  104. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/adapters/browser.py +0 -0
  105. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/adapters/chat.py +0 -0
  106. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/adapters/ingest.py +0 -0
  107. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/browser_indexer.py +0 -0
  108. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/chat_indexer.py +0 -0
  109. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/chat_parsers/__init__.py +0 -0
  110. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/chat_parsers/chatgpt_parser.py +0 -0
  111. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/chat_parsers/claude_parser.py +0 -0
  112. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/content_extractors.py +0 -0
  113. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/database.py +0 -0
  114. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/db/__init__.py +0 -0
  115. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/db/connector_schema.py +0 -0
  116. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/db/security.py +0 -0
  117. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/full_content_extractor.py +0 -0
  118. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/registry.py +0 -0
  119. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/ingest/run_record.py +0 -0
  120. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/mcp/__init__.py +0 -0
  121. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/mcp/__main__.py +0 -0
  122. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/mcp/db.py +0 -0
  123. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/mcp/errors.py +0 -0
  124. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/mcp/extraction.py +0 -0
  125. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/mcp/tools/__init__.py +0 -0
  126. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/mcp/tools/status.py +0 -0
  127. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/paths.py +0 -0
  128. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/permissions.py +0 -0
  129. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/semantic/__init__.py +0 -0
  130. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/semantic/chunking.py +0 -0
  131. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/semantic/embeddings.py +0 -0
  132. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/semantic/vector_store.py +0 -0
  133. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/__init__.py +0 -0
  134. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/chat_service.py +0 -0
  135. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/content_service.py +0 -0
  136. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/file_service.py +0 -0
  137. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/roles.py +0 -0
  138. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/services/status_service.py +0 -0
  139. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/source_registry.py +0 -0
  140. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/utils/__init__.py +0 -0
  141. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/utils/hash_utils.py +0 -0
  142. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/utils/logging_config.py +0 -0
  143. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/utils/mime.py +0 -0
  144. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/utils/text.py +0 -0
  145. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/utils/time.py +0 -0
  146. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter/visibility.py +0 -0
  147. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter_cli.egg-info/dependency_links.txt +0 -0
  148. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter_cli.egg-info/entry_points.txt +0 -0
  149. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter_cli.egg-info/requires.txt +0 -0
  150. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/footprinter_cli.egg-info/top_level.txt +0 -0
  151. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/setup.cfg +0 -0
  152. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_access_control_docs.py +0 -0
  153. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_access_recalculate.py +0 -0
  154. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_bundled.py +0 -0
  155. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_e2e_install.py +0 -0
  156. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_e2e_pipeline.py +0 -0
  157. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_examples.py +0 -0
  158. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_files_rename.py +0 -0
  159. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_inherit_resolution.py +0 -0
  160. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_logging.py +0 -0
  161. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_no_project_root.py +0 -0
  162. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_package_init.py +0 -0
  163. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_paths_no_test_marker.py +0 -0
  164. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_pip_install_e2e.py +0 -0
  165. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_prompt_safety.py +0 -0
  166. {footprinter_cli-1.0.1 → footprinter_cli-1.0.2}/tests/test_security_layer.py +0 -0
  167. {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.1
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/swellcitygroup/footprinter
8
- Project-URL: Repository, https://github.com/swellcitygroup/footprinter
9
- Project-URL: Issues, https://github.com/swellcitygroup/footprinter/issues
10
- Project-URL: Documentation, https://github.com/swellcitygroup/footprinter/blob/main/README.md
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
  [![Tests](https://github.com/swellcitygroup/footprinter/actions/workflows/test.yml/badge.svg)](https://github.com/swellcitygroup/footprinter/actions/workflows/test.yml)
56
+ [![PyPI](https://img.shields.io/pypi/v/footprinter-cli)](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) — modern macOS Python ships PEP 668 enabled, which blocks bare `pip install`:
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 or update records and assign relationships |
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` | Soft-delete a record |
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
  [![Tests](https://github.com/swellcitygroup/footprinter/actions/workflows/test.yml/badge.svg)](https://github.com/swellcitygroup/footprinter/actions/workflows/test.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/footprinter-cli)](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) — modern macOS Python ships PEP 668 enabled, which blocks bare `pip install`:
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 or update records and assign relationships |
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` | Soft-delete a record |
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 != 'removed')
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 != 'removed'").fetchall()
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 != 'removed'"
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 != 'removed'"
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 != 'removed'"
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 != 'removed'"
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})
@@ -0,0 +1,4 @@
1
+ """Footprinter HTTP API — FastAPI routers calling the service layer."""
2
+
3
+ MAX_LIMIT = 200
4
+ """Upper bound for `limit` query params on HTTP list/search endpoints."""
@@ -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(conn=Depends(get_conn), limit: int = 50, page: int = 1):
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='hidden' so they appear in the catalog
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
- # - "**/Photos Library.photoslibrary/**" # macOS Spotlight index cache (.txt)
120
- # - "**/.claude/debug/**" # Claude Code debug logs
121
- # - "**/.claude/paste-cache/**" # Claude Code paste cache
122
- # - "**/.claude/cache/**" # Claude Code cache
123
- # - "**/.claude/projects/**" # Claude Code session data
124
- # - "**/.claude/plans/**" # Claude Code auto-generated plans
125
- # - "**/.claude/plugins/**" # Claude Code plugin cache
126
- # - "**/.cci/**" # CumulusCI cache
127
- # - "**/.context/**" # IDE context directories
128
- exclude_patterns: []
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 (e.g. ``ChatDedup``).
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 != 'removed'",
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 != 'removed'",
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 != 'removed'"
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 != 'removed'"
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": "active",
77
+ "status": "listed",
78
78
  },
79
- {"name": "Internal Tools", "client_type": "internal", "slug": "", "path_pattern": "", "status": "active"},
80
- {"name": "Side Project", "client_type": "personal", "slug": "", "path_pattern": "", "status": "active"},
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": "active",
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": "active",
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": "active",
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": "active, hidden, removed",
116
+ "status": "listed, unlisted, removed",
117
117
  },
118
118
  "project": {
119
- "status": "active, paused, completed, abandoned, removed",
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": "active",
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": "active, hidden, removed",
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": "active",
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": "active",
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": "active, hidden, removed",
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": "active",
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": "active",
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": "active, hidden, removed",
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": "active",
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": "active",
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": "active, hidden, removed, merged",
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": "active",
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": "active",
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": "active, hidden, removed",
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 != 'removed'"
544
+ sql += " WHERE status = 'listed'"
545
545
  elif status_filter:
546
546
  print(
547
547
  f"Entity '{noun}' does not have a status column.",