multi-agent-platform 0.1.0__tar.gz → 0.2.0__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 (300) hide show
  1. {multi_agent_platform-0.1.0/multi_agent_platform.egg-info → multi_agent_platform-0.2.0}/PKG-INFO +22 -3
  2. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/README.md +21 -2
  3. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/commands/project.py +65 -0
  4. multi_agent_platform-0.2.0/cli/commands/sync.py +169 -0
  5. multi_agent_platform-0.2.0/cli/local_cache.py +255 -0
  6. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/main.py +2 -0
  7. multi_agent_platform-0.2.0/cli/project_export.py +356 -0
  8. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0/multi_agent_platform.egg-info}/PKG-INFO +22 -3
  9. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/multi_agent_platform.egg-info/SOURCES.txt +5 -0
  10. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/pyproject.toml +1 -1
  11. multi_agent_platform-0.2.0/tests/test_local_cache.py +409 -0
  12. multi_agent_platform-0.2.0/tests/test_project_export.py +311 -0
  13. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/LICENSE +0 -0
  14. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/__init__.py +0 -0
  15. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/action_item_escalation.py +0 -0
  16. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/agent_client.py +0 -0
  17. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/bridge_state.py +0 -0
  18. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/commands/__init__.py +0 -0
  19. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/commands/action.py +0 -0
  20. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/commands/agent.py +0 -0
  21. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/commands/audit.py +0 -0
  22. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/commands/docs.py +0 -0
  23. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/commands/experiment.py +0 -0
  24. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/commands/feedback.py +0 -0
  25. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/commands/notification.py +0 -0
  26. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/commands/persona.py +0 -0
  27. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/commands/runtime.py +0 -0
  28. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/commands/topic.py +0 -0
  29. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/e2e_collab.py +0 -0
  30. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/git_checkpoint.py +0 -0
  31. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/host_worker_types.py +0 -0
  32. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/map_command_client.py +0 -0
  33. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/participant_worker.py +0 -0
  34. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/reviewer_worker.py +0 -0
  35. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/runtime/__init__.py +0 -0
  36. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/runtime/run_lock.py +0 -0
  37. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/runtime_chat.py +0 -0
  38. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/session_wake_log.py +0 -0
  39. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/simple_waker.py +0 -0
  40. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/table_render.py +0 -0
  41. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/wake_backend.py +0 -0
  42. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/cli/worker_cycle_log.py +0 -0
  43. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/multi_agent_platform.egg-info/dependency_links.txt +0 -0
  44. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/multi_agent_platform.egg-info/entry_points.txt +0 -0
  45. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/multi_agent_platform.egg-info/requires.txt +0 -0
  46. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/multi_agent_platform.egg-info/top_level.txt +0 -0
  47. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/sdk/python/map_client/__init__.py +0 -0
  48. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/sdk/python/map_client/bootstrap.py +0 -0
  49. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/sdk/python/map_client/client.py +0 -0
  50. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/sdk/python/map_client/config.py +0 -0
  51. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/sdk/python/map_client/errors.py +0 -0
  52. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/sdk/python/map_client/exceptions.py +0 -0
  53. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/sdk/python/map_client/plan_evidence.py +0 -0
  54. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/sdk/python/map_client/project_config.py +0 -0
  55. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/sdk/python/map_client/result_template.py +0 -0
  56. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/sdk/python/map_client/testing.py +0 -0
  57. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/sdk/python/map_mcp/__init__.py +0 -0
  58. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/sdk/python/map_mcp/_utils.py +0 -0
  59. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/sdk/python/map_mcp/auth.py +0 -0
  60. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/sdk/python/map_mcp/config.py +0 -0
  61. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/sdk/python/map_mcp/context.py +0 -0
  62. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/sdk/python/map_mcp/main.py +0 -0
  63. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/sdk/python/map_mcp/server.py +0 -0
  64. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/sdk/python/map_mcp/session.py +0 -0
  65. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/sdk/python/map_sdk/__init__.py +0 -0
  66. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/sdk/python/map_sdk/evidence.py +0 -0
  67. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/sdk/python/map_types/__init__.py +0 -0
  68. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/sdk/python/map_types/enums.py +0 -0
  69. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/sdk/python/map_types/schemas.py +0 -0
  70. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/__init__.py +0 -0
  71. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/__version__.py +0 -0
  72. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/api/__init__.py +0 -0
  73. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/api/action_items.py +0 -0
  74. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/api/agents.py +0 -0
  75. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/api/audit.py +0 -0
  76. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/api/background_tasks.py +0 -0
  77. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/api/common.py +0 -0
  78. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/api/deps.py +0 -0
  79. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/api/experiments.py +0 -0
  80. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/api/feedback.py +0 -0
  81. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/api/notifications.py +0 -0
  82. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/api/projects.py +0 -0
  83. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/api/router.py +0 -0
  84. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/api/status.py +0 -0
  85. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/api/topics.py +0 -0
  86. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/api/webhooks.py +0 -0
  87. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/auth/__init__.py +0 -0
  88. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/auth/experiment_access.py +0 -0
  89. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/config.py +0 -0
  90. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/db/__init__.py +0 -0
  91. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/db/base.py +0 -0
  92. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/db/deadlock_retry.py +0 -0
  93. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/db/session.py +0 -0
  94. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/domain/__init__.py +0 -0
  95. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/domain/encrypted_types.py +0 -0
  96. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/domain/models.py +0 -0
  97. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/domain/schemas.py +0 -0
  98. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/domain/state_machine.py +0 -0
  99. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/domain/topic_ack_constants.py +0 -0
  100. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/main.py +0 -0
  101. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/scripts/__init__.py +0 -0
  102. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/scripts/migrate_notification_unique.py +0 -0
  103. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/scripts/purge_audit_pollution.py +0 -0
  104. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/__init__.py +0 -0
  105. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/_lookups.py +0 -0
  106. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/acceptance_service.py +0 -0
  107. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/action_item_migration_service.py +0 -0
  108. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/action_item_service.py +0 -0
  109. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/agent_work_service.py +0 -0
  110. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/archive_lint_service.py +0 -0
  111. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/audit_service.py +0 -0
  112. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/auth.py +0 -0
  113. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/comment_service.py +0 -0
  114. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/errors.py +0 -0
  115. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/escalation_resolver.py +0 -0
  116. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/evidence_service.py +0 -0
  117. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/experiment_capabilities_service.py +0 -0
  118. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/inbound_event_service.py +0 -0
  119. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/lock_service.py +0 -0
  120. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/log_service.py +0 -0
  121. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/mention_service.py +0 -0
  122. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/notification_service.py +0 -0
  123. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/notification_stream.py +0 -0
  124. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/permissions.py +0 -0
  125. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/persona_activity_service.py +0 -0
  126. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/phase_owner_resolver.py +0 -0
  127. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/phase_service.py +0 -0
  128. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/plan_marker_service.py +0 -0
  129. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/plan_service.py +0 -0
  130. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/platform_feedback_service.py +0 -0
  131. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/project_service.py +0 -0
  132. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/project_status_service.py +0 -0
  133. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/review_service.py +0 -0
  134. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/secret_encryption.py +0 -0
  135. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/similarity_service.py +0 -0
  136. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/sse_event_schemas.py +0 -0
  137. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/status_service.py +0 -0
  138. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/template_service.py +0 -0
  139. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/text_utils.py +0 -0
  140. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/thread_activity.py +0 -0
  141. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/todo_persona_filter.py +0 -0
  142. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/todo_service.py +0 -0
  143. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/topic_ack_service.py +0 -0
  144. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/topic_action_item_ops.py +0 -0
  145. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/topic_comment_kind.py +0 -0
  146. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/topic_comment_service.py +0 -0
  147. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/topic_helpers.py +0 -0
  148. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/topic_lifecycle_service.py +0 -0
  149. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/topic_progress_service.py +0 -0
  150. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/topic_resolve_service.py +0 -0
  151. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/topic_service.py +0 -0
  152. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/topic_work_item_service.py +0 -0
  153. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/server/services/webhook_service.py +0 -0
  154. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/setup.cfg +0 -0
  155. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_18a64691_linkage.py +0 -0
  156. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_a2_action_item_cascade.py +0 -0
  157. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_accept_result_verdict.py +0 -0
  158. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_action_items.py +0 -0
  159. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_agent_client.py +0 -0
  160. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_agent_work.py +0 -0
  161. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_agents_list.py +0 -0
  162. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_arch_map_sdk_evidence.py +0 -0
  163. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_archive_lint_service.py +0 -0
  164. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_audit_admin_cli.py +0 -0
  165. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_audit_admin_filter.py +0 -0
  166. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_auth.py +0 -0
  167. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_auth_experiment_access.py +0 -0
  168. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_authz_cross_persona_capability.py +0 -0
  169. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_authz_pr3_creator_gating_extended.py +0 -0
  170. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_b_acceptance.py +0 -0
  171. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_b_action_item_stale.py +0 -0
  172. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_b_action_item_wake_endpoints.py +0 -0
  173. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_b_action_item_wake_stale.py +0 -0
  174. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_b_action_item_wake_stale_notifications.py +0 -0
  175. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_b_waker_should_wake_action_item.py +0 -0
  176. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_bridge_state.py +0 -0
  177. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_cleanup_audit_log_no_commit.py +0 -0
  178. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_cleanup_exp_b_paging_seven_endpoints.py +0 -0
  179. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_cleanup_exp_b_paging_unified_limit.py +0 -0
  180. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_clear_action_template.py +0 -0
  181. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_cli.py +0 -0
  182. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_cli_error_envelope.py +0 -0
  183. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_cli_evidence_contract.py +0 -0
  184. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_cli_format_priority.py +0 -0
  185. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_cli_n2_hard_cutover.py +0 -0
  186. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_cli_schema_discovery.py +0 -0
  187. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_cli_status.py +0 -0
  188. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_cli_template_contract.py +0 -0
  189. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_cli_work_deprecation.py +0 -0
  190. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_cross_persona_call_audit.py +0 -0
  191. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_deadlock_retry.py +0 -0
  192. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_docs_inventory.py +0 -0
  193. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_eng_fast_gate_whitelist_complete.py +0 -0
  194. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_eng_mypy_strict_agent_work_service.py +0 -0
  195. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_eng_mypy_strict_agents.py +0 -0
  196. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_eng_mypy_strict_experiment_capabilities_service.py +0 -0
  197. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_eng_mypy_strict_experiments_projects.py +0 -0
  198. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_eng_mypy_strict_mention_service.py +0 -0
  199. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_eng_mypy_strict_notification_service.py +0 -0
  200. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_eng_mypy_strict_phase_service.py +0 -0
  201. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_eng_mypy_strict_project_service.py +0 -0
  202. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_eng_mypy_strict_todo_service.py +0 -0
  203. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_eng_mypy_strict_topic_comment_service.py +0 -0
  204. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_eng_mypy_strict_topic_service.py +0 -0
  205. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_eng_mypy_strict_topic_work_item_service.py +0 -0
  206. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_eng_mypy_strict_topics.py +0 -0
  207. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_eng_typing_extensions_clean.py +0 -0
  208. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_eng_uv_lock.py +0 -0
  209. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_eng_version_single_source.py +0 -0
  210. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_error_codes_cli.py +0 -0
  211. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_error_envelope_and_file_options.py +0 -0
  212. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_escalation_resolver.py +0 -0
  213. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_escalation_target_endpoint.py +0 -0
  214. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_evidence_service.py +0 -0
  215. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_experiment_capabilities.py +0 -0
  216. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_experiment_capabilities_whitelist.py +0 -0
  217. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_experiment_lock_fuzz.py +0 -0
  218. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_experiment_lock_notifications.py +0 -0
  219. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_experiment_lock_unit.py +0 -0
  220. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_experiments.py +0 -0
  221. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_feedback.py +0 -0
  222. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_feedback_admin_cli.py +0 -0
  223. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_field_rename_alias.py +0 -0
  224. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_git_checkpoint.py +0 -0
  225. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_inbound_event_rejection.py +0 -0
  226. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_log_create_response.py +0 -0
  227. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_m2_flow.py +0 -0
  228. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_m3_flow.py +0 -0
  229. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_map_sdk_skeleton.py +0 -0
  230. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_mcp.py +0 -0
  231. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_mcp_http.py +0 -0
  232. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_mention_code_skip_unit.py +0 -0
  233. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_mentions.py +0 -0
  234. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_notification_bulk_filter.py +0 -0
  235. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_notification_service_v09.py +0 -0
  236. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_notification_stream.py +0 -0
  237. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_notification_upsert_pr2.py +0 -0
  238. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_notifications.py +0 -0
  239. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_participant_worker_claude_agent.py +0 -0
  240. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_perf_count_sql_fixture.py +0 -0
  241. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_perf_experiment_bundle_verdict_n_plus_one.py +0 -0
  242. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_perf_experiment_summary_bulk.py +0 -0
  243. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_perf_topic_comments_bulk.py +0 -0
  244. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_perf_work_hotpath_bulk.py +0 -0
  245. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_persona_activity.py +0 -0
  246. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_persona_compare_partition.py +0 -0
  247. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_phase_owner_orm_validation.py +0 -0
  248. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_phase_owner_resolver.py +0 -0
  249. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_plan_evidence_parser.py +0 -0
  250. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_plan_marker_service.py +0 -0
  251. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_plan_revise_auto_archive.py +0 -0
  252. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_plan_validate_cli.py +0 -0
  253. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_project_config.py +0 -0
  254. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_project_status.py +0 -0
  255. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_projects.py +0 -0
  256. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_rebutted_plan_continues_e2e.py +0 -0
  257. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_register_agent_body_model.py +0 -0
  258. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_reject_result_misuse.py +0 -0
  259. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_resolve_item_sdk_contract.py +0 -0
  260. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_resolve_item_status.py +0 -0
  261. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_result_template_parser.py +0 -0
  262. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_review_already_archived.py +0 -0
  263. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_review_archived_metadata.py +0 -0
  264. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_review_eligibility.py +0 -0
  265. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_review_item_mutation_audit.py +0 -0
  266. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_review_item_state_closed_migration.py +0 -0
  267. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_review_list_archived_filter.py +0 -0
  268. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_review_substitute_and_withdraw.py +0 -0
  269. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_review_verdict_alias.py +0 -0
  270. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_reviewer_worker_claude_agent.py +0 -0
  271. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_runtime_chat.py +0 -0
  272. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_same_second_host_reply.py +0 -0
  273. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_same_second_trio.py +0 -0
  274. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_sdk.py +0 -0
  275. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_sdk_exceptions.py +0 -0
  276. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_server_lock_service.py +0 -0
  277. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_session_wake_log.py +0 -0
  278. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_similarity_api_contract.py +0 -0
  279. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_similarity_service.py +0 -0
  280. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_simple_waker.py +0 -0
  281. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_simple_waker_pr3_dedup.py +0 -0
  282. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_simple_waker_reviewer_filter.py +0 -0
  283. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_sse_acceptance.py +0 -0
  284. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_sse_isolation.py +0 -0
  285. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_sse_schema_overlap.py +0 -0
  286. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_state_machine.py +0 -0
  287. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_state_machine_recovery_hint.py +0 -0
  288. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_systemd_install.py +0 -0
  289. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_template_api_contract.py +0 -0
  290. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_template_service.py +0 -0
  291. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_todos.py +0 -0
  292. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_topic_notifications.py +0 -0
  293. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_topic_progress.py +0 -0
  294. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_topic_read_cursor.py +0 -0
  295. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_topic_work_items.py +0 -0
  296. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_topics.py +0 -0
  297. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_waker_phase1_acceptance.py +0 -0
  298. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_webhook_secret_encryption.py +0 -0
  299. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_webhooks_audit.py +0 -0
  300. {multi_agent_platform-0.1.0 → multi_agent_platform-0.2.0}/tests/test_work_summary.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multi-agent-platform
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Multi-Agent experiment collaboration platform
5
5
  License-Expression: MIT
6
6
  Project-URL: Homepage, https://github.com/quantaeye/multi-agent-platform
@@ -45,8 +45,20 @@ Dynamic: license-file
45
45
 
46
46
  # Multi-Agent Platform (MAP)
47
47
 
48
+ [![PyPI version](https://img.shields.io/pypi/v/multi-agent-platform.svg)](https://pypi.org/project/multi-agent-platform/)
49
+ [![Python 3.11+](https://img.shields.io/pypi/pyversions/multi-agent-platform.svg)](https://pypi.org/project/multi-agent-platform/)
50
+ [![License: MIT](https://img.shields.io/pypi/l/multi-agent-platform.svg)](https://github.com/quantaeye/multi-agent-platform/blob/main/LICENSE)
51
+
48
52
  多 Agent 实验协作平台:以话题为中心,管理实验计划、评审讨论、执行日志与项目状态。
49
53
 
54
+ ## 安装
55
+
56
+ ```bash
57
+ pip install multi-agent-platform
58
+ ```
59
+
60
+ 安装后即可使用 `map` CLI 和 `map-server` 命令。详见 [Quick Start 指南](docs/QUICKSTART.md)。
61
+
50
62
  **新用户?** 一键启动:`./scripts/quickstart.sh`,或阅读 [Quick Start 指南](docs/QUICKSTART.md)。
51
63
 
52
64
  ## 文档
@@ -138,7 +150,10 @@ map --persona host todos
138
150
  ## 快速开始
139
151
 
140
152
  ```bash
141
- # 安装依赖(含开发工具)
153
+ # 从 PyPI 安装(用户)
154
+ pip install multi-agent-platform
155
+
156
+ # 或从源码开发安装(贡献者)
142
157
  pip install -e ".[dev]"
143
158
 
144
159
  # 运行数据库迁移
@@ -273,7 +288,7 @@ python -c "from map_client import MAPClient; print(MAPClient.from_env().get_me()
273
288
  ## MCP(IDE Agent)
274
289
 
275
290
  ```bash
276
- pip install -e ".[mcp]"
291
+ pip install "multi-agent-platform[mcp]"
277
292
  export MAP_TOKEN=<your-token>
278
293
 
279
294
  # stdio — Cursor 本地子进程(默认)
@@ -296,3 +311,7 @@ map-mcp --transport streamable-http --host 0.0.0.0 --port 8080
296
311
  ## 后续
297
312
 
298
313
  v0.3–v0.6 与 v0.7 P3(CLI archive)已落地;当前主线为 **agent-runtime**(simple-waker 默认 + PersonaAgentClient;legacy runtime-waker 启动路径已退役,模块保留为 re-export 兼容层)。待推进项见 [docs/status-md-v10.md](docs/status-md-v10.md) 与 [架构文档](docs/ARCHITECTURE.md)。
314
+
315
+ ## License
316
+
317
+ [MIT](LICENSE)
@@ -1,7 +1,19 @@
1
1
  # Multi-Agent Platform (MAP)
2
2
 
3
+ [![PyPI version](https://img.shields.io/pypi/v/multi-agent-platform.svg)](https://pypi.org/project/multi-agent-platform/)
4
+ [![Python 3.11+](https://img.shields.io/pypi/pyversions/multi-agent-platform.svg)](https://pypi.org/project/multi-agent-platform/)
5
+ [![License: MIT](https://img.shields.io/pypi/l/multi-agent-platform.svg)](https://github.com/quantaeye/multi-agent-platform/blob/main/LICENSE)
6
+
3
7
  多 Agent 实验协作平台:以话题为中心,管理实验计划、评审讨论、执行日志与项目状态。
4
8
 
9
+ ## 安装
10
+
11
+ ```bash
12
+ pip install multi-agent-platform
13
+ ```
14
+
15
+ 安装后即可使用 `map` CLI 和 `map-server` 命令。详见 [Quick Start 指南](docs/QUICKSTART.md)。
16
+
5
17
  **新用户?** 一键启动:`./scripts/quickstart.sh`,或阅读 [Quick Start 指南](docs/QUICKSTART.md)。
6
18
 
7
19
  ## 文档
@@ -93,7 +105,10 @@ map --persona host todos
93
105
  ## 快速开始
94
106
 
95
107
  ```bash
96
- # 安装依赖(含开发工具)
108
+ # 从 PyPI 安装(用户)
109
+ pip install multi-agent-platform
110
+
111
+ # 或从源码开发安装(贡献者)
97
112
  pip install -e ".[dev]"
98
113
 
99
114
  # 运行数据库迁移
@@ -228,7 +243,7 @@ python -c "from map_client import MAPClient; print(MAPClient.from_env().get_me()
228
243
  ## MCP(IDE Agent)
229
244
 
230
245
  ```bash
231
- pip install -e ".[mcp]"
246
+ pip install "multi-agent-platform[mcp]"
232
247
  export MAP_TOKEN=<your-token>
233
248
 
234
249
  # stdio — Cursor 本地子进程(默认)
@@ -251,3 +266,7 @@ map-mcp --transport streamable-http --host 0.0.0.0 --port 8080
251
266
  ## 后续
252
267
 
253
268
  v0.3–v0.6 与 v0.7 P3(CLI archive)已落地;当前主线为 **agent-runtime**(simple-waker 默认 + PersonaAgentClient;legacy runtime-waker 启动路径已退役,模块保留为 re-export 兼容层)。待推进项见 [docs/status-md-v10.md](docs/status-md-v10.md) 与 [架构文档](docs/ARCHITECTURE.md)。
269
+
270
+ ## License
271
+
272
+ [MIT](LICENSE)
@@ -85,3 +85,68 @@ def project_status_show(
85
85
  lambda c: c.get_project_status_version(_resolve_project(c, project, project_key), version)
86
86
  )
87
87
 
88
+
89
+ @project_app.command("export")
90
+ def project_export(
91
+ output_dir: Path = typer.Option(
92
+ None,
93
+ "--output-dir",
94
+ "-o",
95
+ help="Output directory (default: .map/history/)",
96
+ ),
97
+ include_archived: bool = typer.Option(
98
+ True,
99
+ "--include-archived/--no-archived",
100
+ help="Include archived topics and experiments (default: yes)",
101
+ ),
102
+ project: uuid.UUID | None = typer.Option(None, "--project"),
103
+ project_key: str | None = typer.Option(None, "--project-key"),
104
+ ) -> None:
105
+ """Export project history (topics, experiments, decisions) to local Markdown.
106
+
107
+ The export is a read-only snapshot suitable for committing to Git,
108
+ so that topic discussions and experiment records travel with the code.
109
+
110
+ \b
111
+ Layout:
112
+ <output_dir>/
113
+ INDEX.md # Browseable index
114
+ topics/<slug>.md # One file per topic (with comments + decision)
115
+ experiments/<slug>.md # One file per experiment (with plans + reviews + logs)
116
+
117
+ \b
118
+ Examples:
119
+ map project export # Export to .map/history/
120
+ map project export -o ./docs/history # Custom output directory
121
+ map project export --no-archived # Skip archived items
122
+ """
123
+ from cli.main import _resolve_project, _run # lazy: avoid cycle
124
+ from cli.project_export import export_project_history
125
+
126
+ if output_dir is None:
127
+ from cli.main import find_map_dir, _cli_options
128
+ map_dir = find_map_dir(_cli_options.get("project_root"))
129
+ if map_dir is not None:
130
+ output_dir = map_dir / "history"
131
+ else:
132
+ output_dir = Path(".map") / "history"
133
+
134
+ def action(c: MAPClient):
135
+ pid = _resolve_project(c, project, project_key)
136
+ result_dir = export_project_history(
137
+ c,
138
+ pid,
139
+ output_dir,
140
+ include_archived=include_archived,
141
+ )
142
+ # Count files for feedback
143
+ topic_files = list((result_dir / "topics").glob("*.md"))
144
+ exp_files = list((result_dir / "experiments").glob("*.md"))
145
+ typer.echo(
146
+ f"Exported {len(topic_files)} topic(s) and {len(exp_files)} experiment(s) "
147
+ f"to {result_dir}/"
148
+ )
149
+ return None
150
+
151
+ _run(action)
152
+
@@ -0,0 +1,169 @@
1
+ """``map sync ...`` sub-app — local cache sync commands.
2
+
3
+ Commands:
4
+ map sync pull — Pull project data from remote server to local cache
5
+ map sync status — Show sync status (last pull time, cached counts)
6
+ map sync topics — List cached topics (offline, from local cache)
7
+ map sync topic — Show a cached topic by ID (offline, from local cache)
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ import uuid
13
+ from pathlib import Path
14
+
15
+ import typer
16
+ from map_client.client import MAPClient
17
+
18
+ sync_app = typer.Typer(help="Local cache sync commands (offline browsing)")
19
+
20
+
21
+ def _require_cache_session():
22
+ """Open the local cache DB, exiting with a helpful hint if .map/ is missing."""
23
+ from cli.main import find_map_dir, _cli_options
24
+ from cli.local_cache import init_cache, get_cache_path
25
+
26
+ map_dir = find_map_dir(_cli_options.get("project_root"))
27
+ if map_dir is None:
28
+ typer.echo("Error: .map/ directory not found. Run `map bootstrap` first.", err=True)
29
+ raise typer.Exit(1)
30
+ db_path = get_cache_path(map_dir)
31
+ return map_dir, db_path, init_cache(db_path)
32
+
33
+
34
+ @sync_app.command("pull")
35
+ def sync_pull(
36
+ project: uuid.UUID | None = typer.Option(None, "--project"),
37
+ project_key: str | None = typer.Option(None, "--project-key"),
38
+ include_archived: bool = typer.Option(
39
+ True,
40
+ "--include-archived/--no-archived",
41
+ help="Include archived items (default: yes)",
42
+ ),
43
+ ) -> None:
44
+ """Pull project data from remote server into local cache (.map/cache.db).
45
+
46
+ After pulling, you can browse topics and experiments offline using
47
+ `map sync topics` and `map sync topic --id <uuid>`.
48
+ """
49
+ from cli.main import _resolve_project, _run
50
+ from cli.local_cache import pull_project_to_cache
51
+
52
+ map_dir, db_path, session = _require_cache_session()
53
+
54
+ def action(c: MAPClient):
55
+ pid = _resolve_project(c, project, project_key)
56
+ # Resolve project_key for sync_meta bookkeeping
57
+ from map_client.config import load_config
58
+
59
+ try:
60
+ pk = project_key or load_config().get("project_key") or ""
61
+ except Exception:
62
+ pk = project_key or ""
63
+
64
+ result = pull_project_to_cache(
65
+ session, c, pid, pk, include_archived=include_archived,
66
+ )
67
+ typer.echo(
68
+ f"Pulled {result['topics']} topic(s) and {result['experiments']} experiment(s) "
69
+ f"to {db_path}"
70
+ )
71
+ return None
72
+
73
+ try:
74
+ _run(action)
75
+ finally:
76
+ session.close()
77
+
78
+
79
+ @sync_app.command("status")
80
+ def sync_status(
81
+ project: uuid.UUID | None = typer.Option(None, "--project"),
82
+ ) -> None:
83
+ """Show local cache sync status."""
84
+ from cli.local_cache import get_sync_status
85
+
86
+ _, db_path, session = _require_cache_session()
87
+ if not db_path.exists():
88
+ typer.echo("No local cache found. Run `map sync pull` first.")
89
+ return
90
+
91
+ try:
92
+ statuses = get_sync_status(session, project)
93
+ if not statuses:
94
+ typer.echo("No sync records found. Run `map sync pull` first.")
95
+ return
96
+ for s in statuses:
97
+ typer.echo(f"Project: {s['project_key']} ({s['project_id']})")
98
+ typer.echo(f" Last pull: {s['last_pull_at']}")
99
+ typer.echo(f" Topics: {s['topic_count']}, Experiments: {s['experiment_count']}")
100
+ finally:
101
+ session.close()
102
+
103
+
104
+ @sync_app.command("topics")
105
+ def sync_topics(
106
+ project: uuid.UUID | None = typer.Option(None, "--project"),
107
+ project_key: str | None = typer.Option(None, "--project-key"),
108
+ ) -> None:
109
+ """List cached topics from local cache (offline)."""
110
+ from cli.local_cache import get_sync_status, list_cached_topics
111
+
112
+ _, db_path, session = _require_cache_session()
113
+ if not db_path.exists():
114
+ typer.echo("No local cache found. Run `map sync pull` first.")
115
+ return
116
+
117
+ try:
118
+ # If project_id not given, try to find it from sync_meta
119
+ if project is None:
120
+ statuses = get_sync_status(session)
121
+ if not statuses:
122
+ typer.echo("No cached projects. Run `map sync pull` first.")
123
+ return
124
+ # Use first project (or match by project_key)
125
+ if project_key:
126
+ for s in statuses:
127
+ if s["project_key"] == project_key:
128
+ project = uuid.UUID(s["project_id"])
129
+ break
130
+ if project is None and len(statuses) == 1:
131
+ project = uuid.UUID(statuses[0]["project_id"])
132
+ if project is None:
133
+ typer.echo("Multiple projects cached. Specify --project or --project-key.")
134
+ for s in statuses:
135
+ typer.echo(f" {s['project_key']} ({s['project_id']})")
136
+ return
137
+
138
+ topics = list_cached_topics(session, project)
139
+ if not topics:
140
+ typer.echo("No cached topics. Run `map sync pull` first.")
141
+ return
142
+ typer.echo(f"{'ID':<36} {'Status':<12} {'Title'}")
143
+ typer.echo("-" * 80)
144
+ for t in topics:
145
+ typer.echo(f"{t['id']:<36} {t['status']:<12} {t['title']}")
146
+ finally:
147
+ session.close()
148
+
149
+
150
+ @sync_app.command("topic")
151
+ def sync_topic(
152
+ topic_id: uuid.UUID = typer.Option(..., "--id", help="Topic UUID"),
153
+ ) -> None:
154
+ """Show a cached topic from local cache (offline)."""
155
+ from cli.local_cache import get_cached_topic
156
+
157
+ _, db_path, session = _require_cache_session()
158
+ if not db_path.exists():
159
+ typer.echo("No local cache found. Run `map sync pull` first.")
160
+ return
161
+
162
+ try:
163
+ topic = get_cached_topic(session, topic_id)
164
+ if topic is None:
165
+ typer.echo(f"Topic {topic_id} not found in local cache.")
166
+ return
167
+ typer.echo(json.dumps(topic["data"], indent=2, default=str, ensure_ascii=False))
168
+ finally:
169
+ session.close()
@@ -0,0 +1,255 @@
1
+ """Local SQLite cache for MAP project data — Plan A (read-only sync).
2
+
3
+ This module provides a lightweight local cache that stores JSON snapshots
4
+ of topics, experiments, and decisions pulled from a remote MAP server.
5
+ The cache lives at ``.map/cache.db`` and enables offline browsing of
6
+ project history.
7
+
8
+ Architecture::
9
+
10
+ Remote MAP Server Local .map/cache.db
11
+ ┌──────────────────┐ ┌────────────────────┐
12
+ │ PostgreSQL / │ ── pull ──> │ sync_meta │
13
+ │ SQLite │ (JSON) │ cached_topics │
14
+ │ │ │ cached_experiments │
15
+ └──────────────────┘ └────────────────────┘
16
+
17
+ Design choices:
18
+ - JSON blob storage (not full ORM) — keeps the cache schema-agnostic
19
+ and immune to server-side model changes
20
+ - Upsert by UUID — repeated pulls update in place
21
+ - ``sync_meta`` tracks last-pull timestamp per project for delta sync
22
+ """
23
+ from __future__ import annotations
24
+
25
+ import json
26
+ import uuid
27
+ from datetime import datetime, timezone
28
+ from pathlib import Path
29
+ from typing import Any
30
+
31
+ from sqlalchemy import (
32
+ Column,
33
+ DateTime,
34
+ String,
35
+ Text,
36
+ create_engine,
37
+ text,
38
+ )
39
+ from sqlalchemy.orm import DeclarativeBase, Session, sessionmaker
40
+
41
+
42
+ class Base(DeclarativeBase):
43
+ pass
44
+
45
+
46
+ class SyncMeta(Base):
47
+ """Tracks last-sync timestamp per project."""
48
+
49
+ __tablename__ = "sync_meta"
50
+
51
+ project_id = Column(String(36), primary_key=True) # UUID as string
52
+ project_key = Column(String(64), nullable=False)
53
+ last_pull_at = Column(DateTime(timezone=True), nullable=False)
54
+ topic_count = Column(String(10), default="0")
55
+ experiment_count = Column(String(10), default="0")
56
+
57
+
58
+ class CachedTopic(Base):
59
+ """A topic snapshot stored as JSON blob."""
60
+
61
+ __tablename__ = "cached_topics"
62
+
63
+ id = Column(String(36), primary_key=True) # UUID as string
64
+ project_id = Column(String(36), nullable=False, index=True)
65
+ title = Column(String(512), nullable=False)
66
+ status = Column(String(32), nullable=False)
67
+ data_json = Column(Text, nullable=False) # Full TopicRead as JSON
68
+ pulled_at = Column(DateTime(timezone=True), nullable=False)
69
+
70
+
71
+ class CachedExperiment(Base):
72
+ """An experiment snapshot stored as JSON blob."""
73
+
74
+ __tablename__ = "cached_experiments"
75
+
76
+ id = Column(String(36), primary_key=True) # UUID as string
77
+ project_id = Column(String(36), nullable=False, index=True)
78
+ title = Column(String(512), nullable=False)
79
+ phase = Column(String(32), nullable=False)
80
+ data_json = Column(Text, nullable=False) # Full ExperimentBundleRead as JSON
81
+ pulled_at = Column(DateTime(timezone=True), nullable=False)
82
+
83
+
84
+ def get_cache_path(map_dir: Path) -> Path:
85
+ """Return the cache database path inside ``.map/``."""
86
+ return map_dir / "cache.db"
87
+
88
+
89
+ def init_cache(db_path: Path) -> Session:
90
+ """Initialize (or open) the local cache database and return a session."""
91
+ engine = create_engine(f"sqlite:///{db_path}", connect_args={"check_same_thread": False})
92
+ Base.metadata.create_all(engine)
93
+ SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False)
94
+ return SessionLocal()
95
+
96
+
97
+ def pull_project_to_cache(
98
+ session: Session,
99
+ client: Any, # MAPClient
100
+ project_id: uuid.UUID,
101
+ project_key: str,
102
+ *,
103
+ include_archived: bool = True,
104
+ ) -> dict[str, int]:
105
+ """Pull all topics and experiments from remote server into local cache.
106
+
107
+ Returns a dict with ``topics``, ``experiments`` counts.
108
+ """
109
+ now = datetime.now(timezone.utc)
110
+ pid_str = str(project_id)
111
+
112
+ # --- Pull topics ---
113
+ topic_count = 0
114
+ page = 1
115
+ while True:
116
+ batch = client.list_topics(
117
+ project_id,
118
+ page=page,
119
+ page_size=100,
120
+ include_archived=include_archived,
121
+ )
122
+ if not batch:
123
+ break
124
+ for summary in batch:
125
+ topic = client.get_topic(summary.id)
126
+ topic_json = topic.model_dump(mode="json") if hasattr(topic, "model_dump") else _to_dict(topic)
127
+ # Upsert
128
+ existing = session.get(CachedTopic, str(topic.id))
129
+ if existing:
130
+ existing.title = topic.title
131
+ existing.status = str(topic.status.value if hasattr(topic.status, "value") else topic.status)
132
+ existing.data_json = json.dumps(topic_json, default=str, ensure_ascii=False)
133
+ existing.pulled_at = now
134
+ else:
135
+ session.add(CachedTopic(
136
+ id=str(topic.id),
137
+ project_id=pid_str,
138
+ title=topic.title,
139
+ status=str(topic.status.value if hasattr(topic.status, "value") else topic.status),
140
+ data_json=json.dumps(topic_json, default=str, ensure_ascii=False),
141
+ pulled_at=now,
142
+ ))
143
+ topic_count += 1
144
+ if len(batch) < 100:
145
+ break
146
+ page += 1
147
+
148
+ # --- Pull experiments ---
149
+ experiment_count = 0
150
+ page = 1
151
+ while True:
152
+ batch = client.list_experiments(
153
+ project_id,
154
+ page=page,
155
+ page_size=100,
156
+ include_archived=include_archived,
157
+ )
158
+ if not batch:
159
+ break
160
+ for summary in batch:
161
+ bundle = client.get_experiment_bundle(summary.id)
162
+ bundle_json = bundle.model_dump(mode="json") if hasattr(bundle, "model_dump") else _to_dict(bundle)
163
+ exp = bundle.experiment if hasattr(bundle, "experiment") else bundle
164
+ phase_val = str(exp.phase.value if hasattr(exp.phase, "value") else exp.phase)
165
+ # Upsert
166
+ existing = session.get(CachedExperiment, str(exp.id))
167
+ if existing:
168
+ existing.title = exp.title
169
+ existing.phase = phase_val
170
+ existing.data_json = json.dumps(bundle_json, default=str, ensure_ascii=False)
171
+ existing.pulled_at = now
172
+ else:
173
+ session.add(CachedExperiment(
174
+ id=str(exp.id),
175
+ project_id=pid_str,
176
+ title=exp.title,
177
+ phase=phase_val,
178
+ data_json=json.dumps(bundle_json, default=str, ensure_ascii=False),
179
+ pulled_at=now,
180
+ ))
181
+ experiment_count += 1
182
+ if len(batch) < 100:
183
+ break
184
+ page += 1
185
+
186
+ # --- Update sync_meta ---
187
+ meta = session.get(SyncMeta, pid_str)
188
+ if meta:
189
+ meta.last_pull_at = now
190
+ meta.topic_count = str(topic_count)
191
+ meta.experiment_count = str(experiment_count)
192
+ meta.project_key = project_key
193
+ else:
194
+ session.add(SyncMeta(
195
+ project_id=pid_str,
196
+ project_key=project_key,
197
+ last_pull_at=now,
198
+ topic_count=str(topic_count),
199
+ experiment_count=str(experiment_count),
200
+ ))
201
+
202
+ session.commit()
203
+ return {"topics": topic_count, "experiments": experiment_count}
204
+
205
+
206
+ def get_sync_status(session: Session, project_id: uuid.UUID | None = None) -> list[dict[str, Any]]:
207
+ """Return sync status for all projects (or a specific one)."""
208
+ query = session.query(SyncMeta)
209
+ if project_id is not None:
210
+ query = query.filter(SyncMeta.project_id == str(project_id))
211
+ results = []
212
+ for meta in query.all():
213
+ results.append({
214
+ "project_id": meta.project_id,
215
+ "project_key": meta.project_key,
216
+ "last_pull_at": meta.last_pull_at.isoformat() if meta.last_pull_at else None,
217
+ "topic_count": int(meta.topic_count or "0"),
218
+ "experiment_count": int(meta.experiment_count or "0"),
219
+ })
220
+ return results
221
+
222
+
223
+ def list_cached_topics(session: Session, project_id: uuid.UUID) -> list[dict[str, Any]]:
224
+ """List cached topics (lightweight metadata, no JSON blob)."""
225
+ rows = session.query(CachedTopic).filter(CachedTopic.project_id == str(project_id)).all()
226
+ return [
227
+ {
228
+ "id": r.id,
229
+ "title": r.title,
230
+ "status": r.status,
231
+ "pulled_at": r.pulled_at.isoformat() if r.pulled_at else None,
232
+ }
233
+ for r in rows
234
+ ]
235
+
236
+
237
+ def get_cached_topic(session: Session, topic_id: uuid.UUID) -> dict[str, Any] | None:
238
+ """Get a single cached topic with full JSON data."""
239
+ row = session.get(CachedTopic, str(topic_id))
240
+ if row is None:
241
+ return None
242
+ return {
243
+ "id": row.id,
244
+ "title": row.title,
245
+ "status": row.status,
246
+ "data": json.loads(row.data_json) if row.data_json else None,
247
+ "pulled_at": row.pulled_at.isoformat() if row.pulled_at else None,
248
+ }
249
+
250
+
251
+ def _to_dict(obj: Any) -> dict[str, Any]:
252
+ """Fallback serialization for non-Pydantic objects."""
253
+ if hasattr(obj, "__dict__"):
254
+ return {k: v for k, v in obj.__dict__.items() if not k.startswith("_")}
255
+ return {"value": str(obj)}
@@ -39,6 +39,7 @@ from cli.commands.notification import inbound_event_app, notification_app
39
39
  from cli.commands.persona import persona_app
40
40
  from cli.commands.project import project_app
41
41
  from cli.commands.runtime import runtime_app
42
+ from cli.commands.sync import sync_app
42
43
  from cli.commands.topic import mention_app, todo_app, topic_app
43
44
  from cli.e2e_collab import e2e_app
44
45
  from server.domain.models import AgentRole
@@ -63,6 +64,7 @@ app.add_typer(action_app, name="action")
63
64
  app.add_typer(feedback_app, name="feedback")
64
65
  app.add_typer(docs_app, name="docs")
65
66
  app.add_typer(e2e_app, name="e2e")
67
+ app.add_typer(sync_app, name="sync")
66
68
 
67
69
  _transport: httpx.BaseTransport | None = None
68
70
  _cli_options: dict[str, Any] = {"persona": None, "project_root": None, "format": "yaml"}