footprinter-cli 1.0.4__tar.gz → 1.0.5__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 (179) hide show
  1. {footprinter_cli-1.0.4/footprinter_cli.egg-info → footprinter_cli-1.0.5}/PKG-INFO +8 -10
  2. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/README.md +7 -9
  3. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/access_stamper.py +17 -14
  4. footprinter_cli-1.0.5/footprinter/api/__main__.py +5 -0
  5. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/api/entities.py +0 -2
  6. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/api/server.py +43 -1
  7. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/bundled/config.example.yaml +0 -12
  8. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/__init__.py +20 -15
  9. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/_common.py +94 -34
  10. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/_policy_helpers.py +151 -101
  11. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/_vectorize_stage.py +10 -3
  12. footprinter_cli-1.0.5/footprinter/cli/add.py +813 -0
  13. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/delete.py +4 -4
  14. footprinter_cli-1.0.5/footprinter/cli/diagnostics.py +148 -0
  15. footprinter_cli-1.0.5/footprinter/cli/doctor.py +410 -0
  16. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/ingest.py +21 -324
  17. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/mcp_setup.py +11 -107
  18. footprinter_cli-1.0.5/footprinter/cli/permission_cmd.py +577 -0
  19. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/setup.py +53 -392
  20. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/status.py +34 -3
  21. footprinter_cli-1.0.5/footprinter/cli/update.py +884 -0
  22. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/view.py +221 -7
  23. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/browser.py +32 -12
  24. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/chats.py +29 -8
  25. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/clients.py +35 -26
  26. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/emails.py +14 -8
  27. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/files.py +34 -62
  28. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/folders.py +40 -15
  29. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/messages.py +18 -9
  30. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/policies.py +3 -3
  31. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/projects.py +74 -129
  32. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/search.py +44 -31
  33. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/status.py +39 -9
  34. footprinter_cli-1.0.5/footprinter/ingest/adapters/chat.py +120 -0
  35. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/adapters/local_files.py +1 -1
  36. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/adapters/local_folders.py +6 -6
  37. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/adapters/protocol.py +1 -1
  38. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/chat_indexer.py +2 -2
  39. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/chat_parsers/__init__.py +2 -1
  40. footprinter_cli-1.0.5/footprinter/ingest/chat_parsers/claude_code_parser.py +165 -0
  41. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/db/schema.py +212 -78
  42. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/file_indexer.py +15 -13
  43. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/file_scanner.py +4 -4
  44. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/folder_indexer.py +3 -3
  45. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/orchestrator.py +2 -7
  46. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/processing.py +30 -9
  47. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/scan_summary.py +1 -1
  48. footprinter_cli-1.0.5/footprinter/ingest/status.py +133 -0
  49. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/vector_ops.py +14 -14
  50. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/resources/discoverability.py +1 -1
  51. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/tools/navigation.py +0 -8
  52. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/semantic/embeddings.py +1 -1
  53. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/semantic/vector_store.py +2 -2
  54. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/access_service.py +14 -14
  55. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/chat_service.py +4 -0
  56. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/client_service.py +4 -9
  57. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/ingest_service.py +1 -1
  58. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/project_service.py +14 -32
  59. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/semantic_service.py +10 -10
  60. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/visit_service.py +5 -1
  61. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/visibility.py +40 -40
  62. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5/footprinter_cli.egg-info}/PKG-INFO +8 -10
  63. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter_cli.egg-info/SOURCES.txt +8 -5
  64. footprinter_cli-1.0.5/footprinter_cli.egg-info/entry_points.txt +4 -0
  65. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/pyproject.toml +3 -1
  66. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_access_control_bypasses.py +7 -7
  67. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_access_control_docs.py +2 -2
  68. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_access_recalculate.py +146 -146
  69. footprinter_cli-1.0.5/tests/test_access_source_provenance.py +263 -0
  70. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_e2e_install.py +48 -65
  71. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_edit_recalculate.py +16 -16
  72. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_files_surface.py +3 -3
  73. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_inherit_resolution.py +13 -13
  74. footprinter_cli-1.0.5/tests/test_no_issue_ids.py +53 -0
  75. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_pip_install_e2e.py +4 -6
  76. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_resolver.py +17 -10
  77. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_security_layer.py +87 -87
  78. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_security_permissions.py +25 -25
  79. footprinter_cli-1.0.4/footprinter/cli/api_cmd.py +0 -61
  80. footprinter_cli-1.0.4/footprinter/cli/data.py +0 -879
  81. footprinter_cli-1.0.4/footprinter/cli/doctor.py +0 -251
  82. footprinter_cli-1.0.4/footprinter/cli/mcp_cmd.py +0 -469
  83. footprinter_cli-1.0.4/footprinter/cli/upsert.py +0 -1087
  84. footprinter_cli-1.0.4/footprinter/cli/vectorize.py +0 -215
  85. footprinter_cli-1.0.4/footprinter/ingest/adapters/chat.py +0 -57
  86. footprinter_cli-1.0.4/footprinter/ingest/status.py +0 -347
  87. footprinter_cli-1.0.4/footprinter_cli.egg-info/entry_points.txt +0 -2
  88. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/LICENSE +0 -0
  89. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/__init__.py +0 -0
  90. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/api/__init__.py +0 -0
  91. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/api/db.py +0 -0
  92. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/api/search.py +0 -0
  93. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/api/semantic.py +0 -0
  94. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/api/status.py +0 -0
  95. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/bundled/__init__.py +0 -0
  96. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/bundled/patterns/context_patterns.yaml +0 -0
  97. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/bundled/patterns/extensions.yaml +0 -0
  98. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/bundled/patterns/filename_patterns.yaml +0 -0
  99. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/bundled/patterns/mime_mappings.yaml +0 -0
  100. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/bundled/patterns/salesforce_rules.yaml +0 -0
  101. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/bundled/patterns/security_patterns.yaml +0 -0
  102. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/__main__.py +0 -0
  103. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/_prompt.py +0 -0
  104. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/connect.py +0 -0
  105. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/search.py +0 -0
  106. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/uninstall.py +0 -0
  107. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/connectors/__init__.py +0 -0
  108. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/connectors/config_utils.py +0 -0
  109. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/__init__.py +0 -0
  110. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/protocols.py +0 -0
  111. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/sql_utils.py +0 -0
  112. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/uploads.py +0 -0
  113. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db_base.py +0 -0
  114. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/__init__.py +0 -0
  115. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/adapters/__init__.py +0 -0
  116. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/adapters/browser.py +0 -0
  117. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/adapters/ingest.py +0 -0
  118. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/browser_indexer.py +0 -0
  119. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/chat_parsers/chatgpt_parser.py +0 -0
  120. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/chat_parsers/claude_parser.py +0 -0
  121. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/content_extractors.py +0 -0
  122. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/database.py +0 -0
  123. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/db/__init__.py +0 -0
  124. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/db/connector_schema.py +0 -0
  125. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/full_content_extractor.py +0 -0
  126. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/pipe_runner.py +0 -0
  127. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/registry.py +0 -0
  128. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/run_record.py +0 -0
  129. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/__init__.py +0 -0
  130. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/__main__.py +0 -0
  131. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/db.py +0 -0
  132. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/errors.py +0 -0
  133. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/resources/__init__.py +0 -0
  134. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/resources/context.py +0 -0
  135. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/server.py +0 -0
  136. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/tools/__init__.py +0 -0
  137. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/tools/read.py +0 -0
  138. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/tools/search.py +0 -0
  139. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/tools/semantic.py +0 -0
  140. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/tools/status.py +0 -0
  141. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/paths.py +0 -0
  142. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/permissions.py +0 -0
  143. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/semantic/__init__.py +0 -0
  144. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/semantic/chunking.py +0 -0
  145. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/semantic/hybrid_search.py +0 -0
  146. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/__init__.py +0 -0
  147. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/content_service.py +0 -0
  148. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/email_service.py +0 -0
  149. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/file_service.py +0 -0
  150. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/folder_service.py +0 -0
  151. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/includes.py +0 -0
  152. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/roles.py +0 -0
  153. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/search_service.py +0 -0
  154. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/status_service.py +0 -0
  155. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/source_registry.py +0 -0
  156. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/utils/__init__.py +0 -0
  157. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/utils/exceptions.py +0 -0
  158. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/utils/extraction.py +0 -0
  159. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/utils/hash_utils.py +0 -0
  160. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/utils/logging_config.py +0 -0
  161. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/utils/mime.py +0 -0
  162. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/utils/paths.py +0 -0
  163. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/utils/text.py +0 -0
  164. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/utils/time.py +0 -0
  165. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter_cli.egg-info/dependency_links.txt +0 -0
  166. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter_cli.egg-info/requires.txt +0 -0
  167. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter_cli.egg-info/top_level.txt +0 -0
  168. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/setup.cfg +0 -0
  169. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_build_status_filter.py +0 -0
  170. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_bundled.py +0 -0
  171. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_db_base.py +0 -0
  172. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_e2e_pipeline.py +0 -0
  173. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_examples.py +0 -0
  174. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_files_rename.py +0 -0
  175. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_logging.py +0 -0
  176. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_no_project_root.py +0 -0
  177. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_package_init.py +0 -0
  178. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_paths_no_test_marker.py +0 -0
  179. {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_prompt_safety.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: footprinter-cli
3
- Version: 1.0.4
3
+ Version: 1.0.5
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
@@ -75,7 +75,7 @@ Or install with **pipx** directly:
75
75
  pipx install footprinter-cli
76
76
  ```
77
77
 
78
- Either method gives you the `fp` command with the indexing pipeline, CLI, MCP server, and HTTP API. Optional extras add more:
78
+ Either method installs three commands: `fp` (the CLI and indexing pipeline), `fp-mcp` (the MCP server for AI agents), and `fp-api` (the HTTP API). Optional extras add more:
79
79
 
80
80
  | Extra | What it adds |
81
81
  |-------|-------------|
@@ -160,14 +160,12 @@ All commands use the `fp` entry point.
160
160
  | `fp status` | System health and data counts |
161
161
  | `fp search` | Search across all indexed sources |
162
162
  | `fp connect` | Manage optional integrations |
163
- | `fp mcp` | MCP server and access policies |
164
- | `fp api` | Start the HTTP API server |
163
+ | `fp permission` | Manage access policies (visibility, permissions) |
165
164
  | `fp view` | Browse indexed data (files, folders, projects, clients, chats, emails, visits) |
166
- | `fp upsert` | Create/update records, assign relationships, or soft-delete via `--status removed` |
167
- | `fp data` | Export data, generate templates, or import metadata corrections |
165
+ | `fp add` | Create new entity records or import from CSV |
166
+ | `fp update` | Update existing records by ID — status, assignments, metadata |
168
167
  | `fp delete` | Hard-delete a super entity (irreversible) |
169
- | `fp vectorize` | Manage per-record vectorization control |
170
- | `fp doctor` | Post-install health check (Python version, install location, FDA, MCP wiring) |
168
+ | `fp doctor` | Post-install health check (Python version, platform, FDA, MCP wiring) |
171
169
  | `fp uninstall` | Remove Footprinter — MCP entry, user data, package |
172
170
 
173
171
  Run `fp <command> --help` for full usage.
@@ -176,7 +174,7 @@ Run `fp <command> --help` for full usage.
176
174
 
177
175
  Single-process CLI with optional MCP server. SQLite database. No containers, no cloud, no accounts.
178
176
 
179
- Sources are scanned into SQLite with bidirectional links connecting local files to remote backups via content hash matching. Embeddings are generated at ingest time for semantic search. The MCP server exposes indexed data with two-layer access control (visibility + permissions) — you decide what agents can see.
177
+ Sources are scanned into SQLite with bidirectional links connecting local files to remote backups via content hash matching. Embeddings are generated at ingest time for semantic search. The MCP server exposes indexed data with two-layer access control (visibility + access) — you decide what agents can see.
180
178
 
181
179
  ## Documentation
182
180
 
@@ -194,7 +192,7 @@ Bug fixes, documentation, and tests welcome. For new features or architectural c
194
192
 
195
193
  ```bash
196
194
  git clone https://github.com/harringjohn/footprinter-cli.git
197
- cd footprinter
195
+ cd footprinter-cli
198
196
  python3 -m venv venv
199
197
  ./venv/bin/pip install -e ".[dev]"
200
198
  ```
@@ -21,7 +21,7 @@ Or install with **pipx** directly:
21
21
  pipx install footprinter-cli
22
22
  ```
23
23
 
24
- Either method gives you the `fp` command with the indexing pipeline, CLI, MCP server, and HTTP API. Optional extras add more:
24
+ Either method installs three commands: `fp` (the CLI and indexing pipeline), `fp-mcp` (the MCP server for AI agents), and `fp-api` (the HTTP API). Optional extras add more:
25
25
 
26
26
  | Extra | What it adds |
27
27
  |-------|-------------|
@@ -106,14 +106,12 @@ All commands use the `fp` entry point.
106
106
  | `fp status` | System health and data counts |
107
107
  | `fp search` | Search across all indexed sources |
108
108
  | `fp connect` | Manage optional integrations |
109
- | `fp mcp` | MCP server and access policies |
110
- | `fp api` | Start the HTTP API server |
109
+ | `fp permission` | Manage access policies (visibility, permissions) |
111
110
  | `fp view` | Browse indexed data (files, folders, projects, clients, chats, emails, visits) |
112
- | `fp upsert` | Create/update records, assign relationships, or soft-delete via `--status removed` |
113
- | `fp data` | Export data, generate templates, or import metadata corrections |
111
+ | `fp add` | Create new entity records or import from CSV |
112
+ | `fp update` | Update existing records by ID — status, assignments, metadata |
114
113
  | `fp delete` | Hard-delete a super entity (irreversible) |
115
- | `fp vectorize` | Manage per-record vectorization control |
116
- | `fp doctor` | Post-install health check (Python version, install location, FDA, MCP wiring) |
114
+ | `fp doctor` | Post-install health check (Python version, platform, FDA, MCP wiring) |
117
115
  | `fp uninstall` | Remove Footprinter — MCP entry, user data, package |
118
116
 
119
117
  Run `fp <command> --help` for full usage.
@@ -122,7 +120,7 @@ Run `fp <command> --help` for full usage.
122
120
 
123
121
  Single-process CLI with optional MCP server. SQLite database. No containers, no cloud, no accounts.
124
122
 
125
- Sources are scanned into SQLite with bidirectional links connecting local files to remote backups via content hash matching. Embeddings are generated at ingest time for semantic search. The MCP server exposes indexed data with two-layer access control (visibility + permissions) — you decide what agents can see.
123
+ Sources are scanned into SQLite with bidirectional links connecting local files to remote backups via content hash matching. Embeddings are generated at ingest time for semantic search. The MCP server exposes indexed data with two-layer access control (visibility + access) — you decide what agents can see.
126
124
 
127
125
  ## Documentation
128
126
 
@@ -140,7 +138,7 @@ Bug fixes, documentation, and tests welcome. For new features or architectural c
140
138
 
141
139
  ```bash
142
140
  git clone https://github.com/harringjohn/footprinter-cli.git
143
- cd footprinter
141
+ cd footprinter-cli
144
142
  python3 -m venv venv
145
143
  ./venv/bin/pip install -e ".[dev]"
146
144
  ```
@@ -2,7 +2,7 @@
2
2
 
3
3
  Maps a policy scope (e.g. "global", "project:3", "folder:~/Work/") to affected
4
4
  entity rows, calls the existing batch resolve functions, and writes resolved
5
- values back to mcp_view / mcp_read columns.
5
+ values back to visibility / access columns.
6
6
  """
7
7
 
8
8
  from __future__ import annotations
@@ -45,8 +45,8 @@ def _is_inherit_source(source: str) -> bool:
45
45
  # ---------------------------------------------------------------------------
46
46
  # Each entry describes an entity type's table and capabilities.
47
47
  # table: SQL table name
48
- # has_visibility: has mcp_view column
49
- # has_permissions: has mcp_read column
48
+ # has_visibility: has visibility column
49
+ # has_permissions: has access column
50
50
  # has_status: has status column (filter WHERE status = 'listed')
51
51
  # has_project_id: has project_id FK
52
52
  # has_client_id: has client_id FK
@@ -102,7 +102,7 @@ ENTITY_META: dict[str, dict[str, Any]] = {
102
102
  "has_project_id": False,
103
103
  "has_client_id": True,
104
104
  "has_account": False,
105
- "path_column": "root_path",
105
+ "path_column": None,
106
106
  },
107
107
  "client": {
108
108
  "table": "clients",
@@ -305,31 +305,34 @@ def _get_ids_for_scope(conn: sqlite3.Connection, scope: str) -> dict[str, list[i
305
305
 
306
306
 
307
307
  def _write_back_visibility(conn: sqlite3.Connection, entity_type: str, results: dict[int, tuple]) -> None:
308
- """Batch UPDATE mcp_view from resolve results.
308
+ """Batch UPDATE visibility and visibility_source from resolve results.
309
309
 
310
310
  Entities whose visibility comes from the global policy or the hardcoded
311
- baseline are written as ``'inherit'`` the MCP layer resolves them at
312
- query time. Entities with a specific policy get the resolved value.
311
+ baseline are written as ``'inherit'`` with ``visibility_source = NULL``.
312
+ Entities with a specific policy get the resolved value and source scope.
313
313
  """
314
314
  table = ENTITY_META[entity_type]["table"]
315
315
  conn.executemany(
316
- f"UPDATE {table} SET mcp_view = ? WHERE id = ?",
317
- [("inherit" if _is_inherit_source(source) else state, eid) for eid, (state, source) in results.items()],
316
+ f"UPDATE {table} SET visibility = ?, visibility_source = ? WHERE id = ?",
317
+ [
318
+ ("inherit", None, eid) if _is_inherit_source(source) else (state, source, eid)
319
+ for eid, (state, source) in results.items()
320
+ ],
318
321
  )
319
322
 
320
323
 
321
324
  def _write_back_permissions(conn: sqlite3.Connection, entity_type: str, results: dict[int, tuple]) -> None:
322
- """Batch UPDATE mcp_read from resolve results.
325
+ """Batch UPDATE access and access_source from resolve results.
323
326
 
324
327
  Entities whose permission comes from the global policy or the hardcoded
325
- baseline are written as ``'inherit'`` the MCP layer resolves them at
326
- query time. Entities with a specific policy get the resolved value.
328
+ baseline are written as ``'inherit'`` with ``access_source = NULL``.
329
+ Entities with a specific policy get the resolved value and source scope.
327
330
  """
328
331
  table = ENTITY_META[entity_type]["table"]
329
332
  conn.executemany(
330
- f"UPDATE {table} SET mcp_read = ? WHERE id = ?",
333
+ f"UPDATE {table} SET access = ?, access_source = ? WHERE id = ?",
331
334
  [
332
- ("inherit" if _is_inherit_source(source) else ("allow" if allowed else "deny"), eid)
335
+ ("inherit", None, eid) if _is_inherit_source(source) else ("allow" if allowed else "deny", source, eid)
333
336
  for eid, (allowed, source) in results.items()
334
337
  ],
335
338
  )
@@ -0,0 +1,5 @@
1
+ """Allow running as: python -m footprinter.api"""
2
+
3
+ from footprinter.api.server import cli
4
+
5
+ cli()
@@ -137,7 +137,6 @@ def list_projects(
137
137
  include: Optional[str] = Query(None, description="Comma-separated includes"),
138
138
  status: Optional[str] = Query(None, description="Comma-separated status filter"),
139
139
  client: Optional[str] = None,
140
- project_type: Optional[str] = None,
141
140
  limit: int = Query(50, ge=1, le=MAX_LIMIT),
142
141
  page: int = 1,
143
142
  ):
@@ -149,7 +148,6 @@ def list_projects(
149
148
  include=include_list,
150
149
  status=status_list,
151
150
  client=client,
152
- project_type=project_type,
153
151
  limit=limit,
154
152
  page=page,
155
153
  )
@@ -1,10 +1,17 @@
1
1
  """Footprinter HTTP API — FastAPI app factory and server entry point."""
2
2
 
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import sys
7
+
3
8
  from fastapi import FastAPI, Request
4
9
  from fastapi.responses import JSONResponse
5
10
 
6
11
  from footprinter.utils.exceptions import DatabaseNotInitializedError
7
12
 
13
+ _LOOPBACK_HOSTS = {"127.0.0.1", "localhost", "::1"}
14
+
8
15
 
9
16
  def create_app() -> "FastAPI":
10
17
  """Create and configure the FastAPI application.
@@ -62,5 +69,40 @@ def main(host: str = "127.0.0.1", port: int = 8000) -> None:
62
69
  uvicorn.run(app, host=host, port=port)
63
70
 
64
71
 
72
+ def cli(argv: list[str] | None = None) -> None:
73
+ """Console_script entry point — parse CLI args, validate host, start server."""
74
+ parser = argparse.ArgumentParser(
75
+ prog="fp-api",
76
+ description="Start the Footprinter HTTP API server.",
77
+ )
78
+ parser.add_argument("--host", default="127.0.0.1", help="Host to bind (default: 127.0.0.1)")
79
+ parser.add_argument("--port", type=int, default=8000, help="Port to bind (default: 8000)")
80
+ parser.add_argument(
81
+ "--allow-insecure-bind",
82
+ action="store_true",
83
+ help="Allow binding to non-loopback interfaces (no authentication!).",
84
+ )
85
+ args = parser.parse_args(argv)
86
+
87
+ if args.host not in _LOOPBACK_HOSTS:
88
+ if not args.allow_insecure_bind:
89
+ print(
90
+ f"error: refusing to bind to non-loopback host {args.host!r}.\n"
91
+ "The Footprinter HTTP API has no authentication; binding outside "
92
+ "loopback exposes indexed data to anyone on the network.\n"
93
+ "Pass --allow-insecure-bind to override.",
94
+ file=sys.stderr,
95
+ )
96
+ raise SystemExit(2)
97
+ print(
98
+ f"WARNING: binding to {args.host} — the HTTP API has no authentication. "
99
+ "Anyone reachable on this network can read indexed files, emails, "
100
+ "chats, and browser history.",
101
+ file=sys.stderr,
102
+ )
103
+
104
+ main(host=args.host, port=args.port)
105
+
106
+
65
107
  if __name__ == "__main__":
66
- main()
108
+ cli()
@@ -81,9 +81,6 @@ indexing:
81
81
  max_file_size_mb: 50 # MB; 0 = no size limit. 50 is generous for prose/docs.
82
82
  lookback_days: 14 # Browser history window (days back to index)
83
83
  content_snippets: false # Extract file/email content previews for keyword search
84
- # `fp ingest --preview` (FPR-1723) — pre-scan summary tuning
85
- preview_top_n: 10 # rows shown for top files / top directories
86
- preview_size_threshold_mb: 50 # files at or above this size are flagged as outliers
87
84
 
88
85
  # Semantic search — stores content as embeddings in a local ChromaDB database
89
86
  # Enables finding files and chats by meaning, not just keywords
@@ -163,12 +160,3 @@ source_seeds:
163
160
  account: null
164
161
  label: "Chat"
165
162
  icon: message
166
-
167
- # Display labels for the group/project hierarchy
168
- # Customize these to match your organization's terminology
169
- domain:
170
- labels:
171
- group_singular: "Client"
172
- group_plural: "Clients"
173
- project_singular: "Project"
174
- project_plural: "Projects"
@@ -39,7 +39,6 @@ def main(argv=None) -> None:
39
39
  epilog=(
40
40
  "getting started:\n"
41
41
  " fp setup Run the configuration wizard\n"
42
- " fp setup --check Validate existing configuration\n"
43
42
  " fp connect list Show available data source connectors\n"
44
43
  "\n"
45
44
  "data commands:\n"
@@ -57,13 +56,23 @@ def main(argv=None) -> None:
57
56
  " fp view emails List indexed emails\n"
58
57
  " fp view visits List browser history\n"
59
58
  "\n"
60
- "servers:\n"
61
- " fp mcp Start the MCP server\n"
62
- " fp api Start the HTTP API server\n"
63
- " fp mcp check ~/Work/file Check access resolution for a path\n"
59
+ "manage records:\n"
60
+ " fp add client --name Acme Create a new entity record\n"
61
+ " fp update client 5 --name X Update an existing record by ID\n"
62
+ " fp update file 42 --project-id 3 Assign a file to a project\n"
63
+ " fp update files data.csv Bulk update from CSV\n"
64
+ "\n"
65
+ "access control:\n"
66
+ " fp permission list Show all configured access policies\n"
67
+ " fp permission set Set visibility and/or access for a scope\n"
68
+ " fp permission check Check access resolution for a target\n"
69
+ " fp permission reset Remove policy (fall back to inheritance)\n"
70
+ " fp permission recalculate Re-resolve access stamps from the policy chain\n"
64
71
  "\n"
65
72
  "diagnostics:\n"
66
73
  " fp doctor Check installation health\n"
74
+ " fp doctor search Rebuild FTS search indexes\n"
75
+ " fp doctor semantic Rebuild vector store\n"
67
76
  "\n"
68
77
  "cleanup:\n"
69
78
  " fp uninstall Remove Footprinter (MCP entry, data, package)\n"
@@ -81,35 +90,31 @@ def main(argv=None) -> None:
81
90
  subparsers.required = False
82
91
 
83
92
  from footprinter.cli import (
84
- api_cmd,
93
+ add,
85
94
  connect,
86
- data,
87
95
  delete,
88
96
  doctor,
89
97
  ingest,
90
- mcp_cmd,
98
+ permission_cmd,
91
99
  search,
92
100
  setup,
93
101
  status,
94
102
  uninstall,
95
- upsert,
96
- vectorize,
103
+ update,
97
104
  view,
98
105
  )
99
106
 
100
107
  for mod in [
108
+ add,
101
109
  ingest,
102
- mcp_cmd,
103
- api_cmd,
110
+ permission_cmd,
104
111
  status,
105
112
  search,
106
113
  setup,
107
114
  connect,
108
115
  view,
109
- upsert,
110
- data,
116
+ update,
111
117
  delete,
112
- vectorize,
113
118
  uninstall,
114
119
  doctor,
115
120
  ]:
@@ -14,6 +14,8 @@ from typing import Optional, Union
14
14
 
15
15
  from rich.console import Console
16
16
 
17
+ from footprinter.db.clients import VALID_STATUSES as VALID_CLIENT_STATUSES
18
+ from footprinter.db.projects import VALID_STATUSES as VALID_PROJECT_STATUSES
17
19
  from footprinter.services import access_service as _access
18
20
  from footprinter.services.access_service import (
19
21
  resolve_inherit_permission,
@@ -51,8 +53,13 @@ C_DIM = "dim"
51
53
 
52
54
  VALID_STATUSES = frozenset({"listed", "unlisted", "removed"})
53
55
 
56
+ VALID_STATUSES_BY_ENTITY: dict[str, frozenset[str]] = {
57
+ "client": VALID_CLIENT_STATUSES,
58
+ "project": VALID_PROJECT_STATUSES,
59
+ }
60
+
54
61
  ALLOWED_TABLES = frozenset({"clients", "projects"})
55
- ALLOWED_COLUMNS = frozenset({"name", "project_name"})
62
+ ALLOWED_COLUMNS = frozenset({"name"})
56
63
 
57
64
 
58
65
  # ---------------------------------------------------------------------------
@@ -157,6 +164,16 @@ def add_csv_flag(parser) -> None:
157
164
  )
158
165
 
159
166
 
167
+ def add_template_flag(parser) -> None:
168
+ """Add a ``--template`` flag to an argparse parser."""
169
+ parser.add_argument(
170
+ "--template",
171
+ action="store_true",
172
+ default=False,
173
+ help="Output an import-compatible CSV template with example data",
174
+ )
175
+
176
+
160
177
  # ---------------------------------------------------------------------------
161
178
  # Output helpers
162
179
  # ---------------------------------------------------------------------------
@@ -263,46 +280,80 @@ def enrich_verbose_access(
263
280
  *,
264
281
  id_key: str = "id",
265
282
  ) -> None:
266
- """Annotate rows in-place with access, access_source, visibility.
283
+ """Annotate rows in-place with resolved access fields.
267
284
 
268
- Uses ``resolve_inherit_visibility`` / ``resolve_inherit_permission``
269
- so that ``inherit`` values resolve to the global policy. Since
270
- ``open_db()`` calls ``load_globals`` before yielding, ``inherit``
271
- values resolve to the actual global policy in normal CLI usage.
272
- The baseline fallback remains as a defence-in-depth measure.
273
-
274
- Three cases based on the ``mcp_read`` key in each row dict:
275
-
276
- * **Key absent** (folders, visits): access = "—", source = "—"
277
- * **Key is None** (truly missing): fails closed, source = "default"
278
- * **Key is "inherit"**: resolved via global policy (source = "global")
279
- or baseline (source = "baseline") depending on whether ``load_globals``
280
- has been called
281
- * **Key has a real value**: access from value, source = "cached"
285
+ Pops raw ``visibility`` and ``access`` stamped columns, resolves
286
+ ``inherit`` values, and writes a six-field access block:
287
+ ``visibility_raw``, ``access_raw``, ``visibility``, ``access``,
288
+ ``access_source``, ``visibility_source``. Internal provenance columns
289
+ (``visibility_source``, ``access_source``) are consumed then removed.
282
290
 
283
291
  No-op if *rows* is empty.
284
292
  """
285
293
  if not rows:
286
294
  return
287
295
  for r in rows:
288
- if "mcp_read" not in r:
289
- r["access"] = "—"
290
- r["access_source"] = ""
291
- elif r["mcp_read"] not in (None, "inherit"):
292
- r["access"] = "allow" if r["mcp_read"] == "allow" else "deny"
293
- r["access_source"] = "cached"
296
+ raw_vis = r.pop("visibility", None)
297
+ access_present = "access" in r
298
+ raw_access = r.pop("access", None)
299
+ read_source = r.pop("access_source", None)
300
+ view_source = r.pop("visibility_source", None)
301
+
302
+ if not access_present:
303
+ resolved_access = "—"
304
+ access_source = "—"
305
+ elif raw_access not in (None, "inherit"):
306
+ resolved_access = "allow" if raw_access == "allow" else "deny"
307
+ access_source = read_source if read_source else "cached"
294
308
  else:
295
- resolved = resolve_inherit_permission(r["mcp_read"])
296
- r["access"] = resolved
297
- if r["mcp_read"] == "inherit":
298
- r["access_source"] = "global" if _access.is_global_policy_loaded() else "baseline"
309
+ resolved_access = resolve_inherit_permission(raw_access)
310
+ if raw_access == "inherit":
311
+ access_source = "global" if _access.is_global_policy_loaded() else "baseline"
299
312
  else:
300
- r["access_source"] = "default"
301
- r["visibility"] = resolve_inherit_visibility(r.get("mcp_view"))
313
+ access_source = "default"
314
+
315
+ if raw_vis not in (None, "inherit"):
316
+ visibility_source = view_source if view_source else "cached"
317
+ elif raw_vis == "inherit":
318
+ visibility_source = "global" if _access.is_global_policy_loaded() else "baseline"
319
+ else:
320
+ visibility_source = "default"
321
+
322
+ r["visibility_raw"] = raw_vis
323
+ r["access_raw"] = raw_access
324
+ r["visibility"] = resolve_inherit_visibility(raw_vis)
325
+ r["access"] = resolved_access
326
+ r["access_source"] = access_source
327
+ r["visibility_source"] = visibility_source
302
328
 
303
329
 
304
330
  def verbose_access_cells(row: dict) -> list[str]:
305
- """Return [access_cell, visibility_cell] with Rich color markup."""
331
+ """Return [vis_raw, access_raw, visibility, access, source, vis_source] with Rich color markup."""
332
+ vis_colors = {"full": "green", "opaque": "yellow", "hidden": "red"}
333
+
334
+ raw_vis = row.get("visibility_raw")
335
+ if raw_vis is None:
336
+ vis_raw_cell = "[dim]—[/dim]"
337
+ elif raw_vis == "inherit":
338
+ vis_raw_cell = "[dim]inherit[/dim]"
339
+ else:
340
+ vc = vis_colors.get(raw_vis, "white")
341
+ vis_raw_cell = f"[{vc}]{raw_vis}[/{vc}]"
342
+
343
+ raw_access = row.get("access_raw")
344
+ if raw_access is None:
345
+ access_raw_cell = "[dim]—[/dim]"
346
+ elif raw_access == "inherit":
347
+ access_raw_cell = "[dim]inherit[/dim]"
348
+ elif raw_access == "allow":
349
+ access_raw_cell = "[green]allow[/green]"
350
+ else:
351
+ access_raw_cell = "[red]deny[/red]"
352
+
353
+ visibility = row.get("visibility", "opaque")
354
+ vis_color = vis_colors.get(visibility, "white")
355
+ vis_cell = f"[{vis_color}]{visibility}[/{vis_color}]"
356
+
306
357
  access = row.get("access", "deny")
307
358
  if access == "—":
308
359
  access_cell = "[dim]—[/dim]"
@@ -311,12 +362,21 @@ def verbose_access_cells(row: dict) -> list[str]:
311
362
  else:
312
363
  access_cell = "[red]deny[/red]"
313
364
 
314
- visibility = row.get("visibility", "opaque")
315
- vis_colors = {"visible": "green", "opaque": "yellow", "hidden": "red"}
316
- vis_color = vis_colors.get(visibility, "white")
317
- vis_cell = f"[{vis_color}]{visibility}[/{vis_color}]"
365
+ source = row.get("access_source")
366
+ if source is None or source == "":
367
+ source_cell = "[dim]—[/dim]"
368
+ else:
369
+ source_cell = source
370
+
371
+ vis_source = row.get("visibility_source")
372
+ if vis_source is None or vis_source == "—":
373
+ vis_source_cell = "[dim]—[/dim]"
374
+ elif vis_source == source:
375
+ vis_source_cell = "[dim]≡[/dim]"
376
+ else:
377
+ vis_source_cell = vis_source
318
378
 
319
- return [access_cell, vis_cell]
379
+ return [vis_raw_cell, access_raw_cell, vis_cell, access_cell, source_cell, vis_source_cell]
320
380
 
321
381
 
322
382
  def format_size(size_bytes: int) -> str: