minder-cli 0.6.2__tar.gz → 0.6.3__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 (165) hide show
  1. {minder_cli-0.6.2 → minder_cli-0.6.3}/PKG-INFO +1 -1
  2. {minder_cli-0.6.2 → minder_cli-0.6.3}/pyproject.toml +1 -1
  3. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/bootstrap/transport.py +32 -12
  4. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/repository.py +2 -0
  5. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/skill.py +9 -0
  6. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/interfaces.py +2 -1
  7. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/qdrant/operational_store.py +16 -3
  8. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/relational.py +30 -2
  9. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/repo_state.py +23 -12
  10. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/memory.py +39 -4
  11. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/workflow.py +16 -9
  12. {minder_cli-0.6.2 → minder_cli-0.6.3}/.gitignore +0 -0
  13. {minder_cli-0.6.2 → minder_cli-0.6.3}/LICENSE +0 -0
  14. {minder_cli-0.6.2 → minder_cli-0.6.3}/README-pypi.md +0 -0
  15. {minder_cli-0.6.2 → minder_cli-0.6.3}/README.md +0 -0
  16. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/__init__.py +0 -0
  17. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/api/routers/prompts.py +0 -0
  18. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/application/__init__.py +0 -0
  19. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/application/admin/__init__.py +0 -0
  20. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/application/admin/dto.py +0 -0
  21. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/application/admin/jobs.py +0 -0
  22. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/application/admin/use_cases.py +0 -0
  23. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/auth/__init__.py +0 -0
  24. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/auth/context.py +0 -0
  25. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/auth/middleware.py +0 -0
  26. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/auth/principal.py +0 -0
  27. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/auth/rate_limiter.py +0 -0
  28. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/auth/rbac.py +0 -0
  29. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/auth/service.py +0 -0
  30. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/bootstrap/__init__.py +0 -0
  31. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/bootstrap/agent_seeder.py +0 -0
  32. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/bootstrap/providers.py +0 -0
  33. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/bootstrap/workflow_seeder.py +0 -0
  34. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/cache/__init__.py +0 -0
  35. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/cache/providers.py +0 -0
  36. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/chunking/__init__.py +0 -0
  37. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/chunking/code_splitter.py +0 -0
  38. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/chunking/splitter.py +0 -0
  39. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/cli.py +0 -0
  40. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/config.py +0 -0
  41. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/context_compactor.py +0 -0
  42. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/continuity.py +0 -0
  43. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/dev.py +0 -0
  44. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/embedding/__init__.py +0 -0
  45. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/embedding/base.py +0 -0
  46. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/embedding/local.py +0 -0
  47. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/embedding/openai.py +0 -0
  48. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/__init__.py +0 -0
  49. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/checkpoint.py +0 -0
  50. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/edges.py +0 -0
  51. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/executor.py +0 -0
  52. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/graph.py +0 -0
  53. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/memory_graph.py +0 -0
  54. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/__init__.py +0 -0
  55. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/clarification.py +0 -0
  56. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/evaluator.py +0 -0
  57. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/guard.py +0 -0
  58. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/llm.py +0 -0
  59. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/parallel_retriever.py +0 -0
  60. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/planning.py +0 -0
  61. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/reasoning.py +0 -0
  62. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/reflection.py +0 -0
  63. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/reranker.py +0 -0
  64. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/retriever.py +0 -0
  65. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/verification.py +0 -0
  66. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/workflow_planner.py +0 -0
  67. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/runtime.py +0 -0
  68. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/session_graph.py +0 -0
  69. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/state.py +0 -0
  70. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/supervisor.py +0 -0
  71. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/learning/__init__.py +0 -0
  72. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/learning/error_learner.py +0 -0
  73. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/learning/pattern_extractor.py +0 -0
  74. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/learning/quality_optimizer.py +0 -0
  75. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/learning/skill_synthesizer.py +0 -0
  76. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/llm/__init__.py +0 -0
  77. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/llm/base.py +0 -0
  78. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/llm/factory.py +0 -0
  79. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/llm/llama_cpp_llm.py +0 -0
  80. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/llm/openai.py +0 -0
  81. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/model_bootstrap.py +0 -0
  82. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/__init__.py +0 -0
  83. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/agent.py +0 -0
  84. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/base.py +0 -0
  85. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/checkpoint.py +0 -0
  86. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/client.py +0 -0
  87. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/document.py +0 -0
  88. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/error.py +0 -0
  89. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/graph.py +0 -0
  90. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/history.py +0 -0
  91. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/job.py +0 -0
  92. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/prompt.py +0 -0
  93. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/rule.py +0 -0
  94. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/session.py +0 -0
  95. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/user.py +0 -0
  96. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/workflow.py +0 -0
  97. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/observability/__init__.py +0 -0
  98. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/observability/audit.py +0 -0
  99. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/observability/logging.py +0 -0
  100. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/observability/metrics.py +0 -0
  101. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/observability/tracing.py +0 -0
  102. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/__init__.py +0 -0
  103. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/cli/__init__.py +0 -0
  104. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/cli/commands/agent.py +0 -0
  105. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/cli/commands/auth.py +0 -0
  106. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/cli/commands/mcp.py +0 -0
  107. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/cli/commands/sync.py +0 -0
  108. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/cli/commands/update.py +0 -0
  109. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/cli/main.py +0 -0
  110. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/cli/utils/common.py +0 -0
  111. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/cli/utils/config.py +0 -0
  112. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/cli/utils/git.py +0 -0
  113. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/cli/utils/version.py +0 -0
  114. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/__init__.py +0 -0
  115. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/admin/__init__.py +0 -0
  116. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/admin/agents.py +0 -0
  117. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/admin/api.py +0 -0
  118. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/admin/context.py +0 -0
  119. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/admin/dashboard.py +0 -0
  120. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/admin/jobs.py +0 -0
  121. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/admin/memories.py +0 -0
  122. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/admin/prompts.py +0 -0
  123. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/admin/routes.py +0 -0
  124. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/admin/runtime.py +0 -0
  125. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/admin/search.py +0 -0
  126. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/admin/skills.py +0 -0
  127. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/prompts/__init__.py +0 -0
  128. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/prompts/formatter.py +0 -0
  129. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/resources/__init__.py +0 -0
  130. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/retrieval/__init__.py +0 -0
  131. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/retrieval/hybrid.py +0 -0
  132. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/retrieval/mmr.py +0 -0
  133. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/retrieval/multi_hop.py +0 -0
  134. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/runtime.py +0 -0
  135. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/server.py +0 -0
  136. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/__init__.py +0 -0
  137. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/document.py +0 -0
  138. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/error.py +0 -0
  139. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/feedback.py +0 -0
  140. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/graph.py +0 -0
  141. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/history.py +0 -0
  142. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/qdrant/__init__.py +0 -0
  143. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/qdrant/client.py +0 -0
  144. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/qdrant/crud.py +0 -0
  145. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/qdrant/graph_store.py +0 -0
  146. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/qdrant/vector_store.py +0 -0
  147. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/rule.py +0 -0
  148. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/vector.py +0 -0
  149. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/__init__.py +0 -0
  150. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/agents.py +0 -0
  151. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/auth.py +0 -0
  152. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/graph.py +0 -0
  153. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/ingest.py +0 -0
  154. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/query.py +0 -0
  155. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/registry.py +0 -0
  156. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/repo_scanner.py +0 -0
  157. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/seeds/__init__.py +0 -0
  158. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/seeds/default_agents.py +0 -0
  159. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/session.py +0 -0
  160. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/skills.py +0 -0
  161. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/transport/__init__.py +0 -0
  162. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/transport/base.py +0 -0
  163. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/transport/sse.py +0 -0
  164. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/transport/stdio.py +0 -0
  165. {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: minder-cli
3
- Version: 0.6.2
3
+ Version: 0.6.3
4
4
  Summary: Minder CLI is the command-line interface for the Minder self-hosted MCP platform.
5
5
  Project-URL: Homepage, https://github.com/hiimtrung/minder
6
6
  Project-URL: Repository, https://github.com/hiimtrung/minder
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "minder-cli"
7
- version = "0.6.2"
7
+ version = "0.6.3"
8
8
  description = "Minder CLI is the command-line interface for the Minder self-hosted MCP platform."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.14"
@@ -315,12 +315,13 @@ def build_transport(
315
315
  return await session_tools.minder_session_cleanup(user_id=authenticated_user.id)
316
316
 
317
317
  async def minder_workflow_get(
318
- *, user=None, repo_id: str, repo_path: str
318
+ *, user=None, repo_id: str, repo_path: str, branch: str = "main"
319
319
  ) -> dict[str, Any]: # noqa: ANN001
320
320
  del user
321
321
  return await workflow_tools.minder_workflow_get(
322
322
  repo_id=await _resolve_repo_uuid(repo_id),
323
323
  repo_path=repo_path,
324
+ branch=branch,
324
325
  )
325
326
 
326
327
  async def minder_workflow_step(
@@ -330,6 +331,7 @@ def build_transport(
330
331
  repo_path: str | None = None,
331
332
  session_id: str | None = None,
332
333
  decision: dict[str, Any] | None = None,
334
+ branch: str = "main",
333
335
  ) -> dict[str, Any]: # noqa: ANN001
334
336
  del user
335
337
  return await workflow_tools.minder_workflow_step(
@@ -337,6 +339,7 @@ def build_transport(
337
339
  repo_path=repo_path,
338
340
  session_id=uuid.UUID(session_id) if session_id else None,
339
341
  decision=decision,
342
+ branch=branch,
340
343
  )
341
344
 
342
345
  async def minder_workflow_update(
@@ -347,6 +350,7 @@ def build_transport(
347
350
  completed_step: str,
348
351
  artifact_name: str | None = None,
349
352
  artifact_content: str | None = None,
353
+ branch: str = "main",
350
354
  ) -> dict[str, Any]: # noqa: ANN001
351
355
  del user
352
356
  return await workflow_tools.minder_workflow_update(
@@ -355,88 +359,104 @@ def build_transport(
355
359
  completed_step=completed_step,
356
360
  artifact_name=artifact_name,
357
361
  artifact_content=artifact_content,
362
+ branch=branch,
358
363
  )
359
364
 
360
365
  async def minder_workflow_guard(
361
- *, user=None, repo_id: str, requested_step: str, action: str | None = None
366
+ *, user=None, repo_id: str, requested_step: str, action: str | None = None, branch: str = "main"
362
367
  ) -> dict[str, Any]: # noqa: ANN001
363
368
  del user
364
369
  return await workflow_tools.minder_workflow_guard(
365
370
  repo_id=await _resolve_repo_uuid(repo_id),
366
371
  requested_step=requested_step,
367
372
  action=action,
373
+ branch=branch,
368
374
  )
369
375
 
370
376
  async def minder_memory_store(
371
377
  *,
372
378
  user=None,
379
+ principal: Principal | None = None,
373
380
  title: str,
374
381
  content: str,
375
382
  tags: list[str],
376
383
  language: str, # noqa: ANN001
384
+ scope: str = "private",
377
385
  ) -> dict[str, Any]:
378
- del user
386
+ owner_id = principal.principal_id if principal else (user.id if user else None)
379
387
  return await memory_tools.minder_memory_store(
380
388
  title=title,
381
389
  content=content,
382
390
  tags=tags,
383
391
  language=language,
392
+ owner_id=owner_id,
393
+ scope=scope,
384
394
  )
385
395
 
386
396
  async def minder_memory_recall(
387
397
  *,
388
398
  user=None,
399
+ principal: Principal | None = None,
389
400
  query: str,
390
401
  limit: int = 5,
391
402
  current_step: str | None = None,
392
403
  artifact_type: str | None = None,
393
404
  ) -> list[dict[str, Any]]: # noqa: ANN001
394
- del user
405
+ owner_id = principal.principal_id if principal else (user.id if user else None)
395
406
  return await memory_tools.minder_memory_recall(
396
407
  query,
397
408
  limit=limit,
398
409
  current_step=current_step,
399
410
  artifact_type=artifact_type,
411
+ owner_id=owner_id,
400
412
  )
401
413
 
402
- async def minder_memory_list(*, user=None) -> list[dict[str, Any]]: # noqa: ANN001
403
- del user
404
- return await memory_tools.minder_memory_list()
414
+ async def minder_memory_list(
415
+ *,
416
+ user=None,
417
+ principal: Principal | None = None,
418
+ ) -> list[dict[str, Any]]: # noqa: ANN001
419
+ owner_id = principal.principal_id if principal else (user.id if user else None)
420
+ return await memory_tools.minder_memory_list(owner_id=owner_id)
405
421
 
406
422
  async def minder_memory_update(
407
423
  *,
408
424
  user=None,
425
+ principal: Principal | None = None,
409
426
  memory_id: str,
410
427
  title: str | None = None,
411
428
  content: str | None = None,
412
429
  tags: list[str] | None = None,
413
430
  ) -> dict[str, Any]: # noqa: ANN001
414
- del user
431
+ owner_id = principal.principal_id if principal else (user.id if user else None)
415
432
  return await memory_tools.minder_memory_update(
416
433
  memory_id,
417
434
  title=title,
418
435
  content=content,
419
436
  tags=tags,
437
+ owner_id=owner_id,
420
438
  )
421
439
 
422
440
  async def minder_memory_delete(
423
- *, user=None, skill_id: str
441
+ *, user=None, principal: Principal | None = None, skill_id: str
424
442
  ) -> dict[str, bool]: # noqa: ANN001
425
- del user
426
- return await memory_tools.minder_memory_delete(skill_id)
443
+ owner_id = principal.principal_id if principal else (user.id if user else None)
444
+ return await memory_tools.minder_memory_delete(skill_id, owner_id=owner_id)
427
445
 
428
446
  async def minder_memory_compact(
429
447
  *,
430
448
  user=None,
449
+ principal: Principal | None = None,
431
450
  memory_ids: list[str],
432
451
  similarity_threshold: float = 0.92,
433
452
  dry_run: bool = True,
434
453
  ) -> dict[str, Any]: # noqa: ANN001
435
- del user
454
+ owner_id = principal.principal_id if principal else (user.id if user else None)
436
455
  return await memory_tools.minder_memory_compact(
437
456
  memory_ids=memory_ids,
438
457
  similarity_threshold=similarity_threshold,
439
458
  dry_run=dry_run,
459
+ owner_id=owner_id,
440
460
  )
441
461
 
442
462
  async def minder_skill_store(
@@ -40,6 +40,7 @@ class Repository(Base):
40
40
  class RepositoryWorkflowStateSchema(BaseModelMeta):
41
41
  id: uuid.UUID = Field(default_factory=uuid.uuid4)
42
42
  repo_id: uuid.UUID
43
+ branch: str = "main"
43
44
  session_id: Optional[uuid.UUID] = None
44
45
  current_step: str
45
46
  completed_steps: List[str] = Field(default_factory=list)
@@ -53,6 +54,7 @@ class RepositoryWorkflowState(Base):
53
54
 
54
55
  id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
55
56
  repo_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), index=True)
57
+ branch: Mapped[str] = mapped_column(String, index=True, default="main", server_default="main")
56
58
  session_id: Mapped[Optional[uuid.UUID]] = mapped_column(UUID(as_uuid=True), nullable=True, index=True)
57
59
  current_step: Mapped[str] = mapped_column(String)
58
60
  completed_steps: Mapped[Dict[str, Any]] = mapped_column(JSON, default=list) # stored as JSON list
@@ -21,6 +21,8 @@ class SkillSchema(BaseModelMeta):
21
21
  deprecated: bool = False
22
22
  source_metadata: Optional[Dict[str, Any]] = None
23
23
  excerpt_kind: str = "none"
24
+ owner_id: Optional[uuid.UUID] = None # principal who created this entry (None = team/legacy)
25
+ scope: str = "private" # 'private' = owner-only, 'team' = visible to all principals
24
26
  created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
25
27
  updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
26
28
 
@@ -46,6 +48,13 @@ class Skill(Base):
46
48
  JSON, nullable=True
47
49
  )
48
50
  excerpt_kind: Mapped[str] = mapped_column(String, default="none")
51
+ # Multi-developer isolation: owner_id is the principal who created this entry.
52
+ # None means team/legacy (visible to all). Indexed for efficient filtering.
53
+ owner_id: Mapped[Optional[uuid.UUID]] = mapped_column(
54
+ UUID(as_uuid=True), nullable=True, index=True
55
+ )
56
+ # 'private' = only visible to owner, 'team' = visible to all principals
57
+ scope: Mapped[str] = mapped_column(String, default="private", server_default="private")
49
58
  created_at: Mapped[datetime] = mapped_column(
50
59
  DateTime(timezone=True), server_default=func.now()
51
60
  )
@@ -64,6 +64,7 @@ class ISkillRepository(Protocol):
64
64
  *,
65
65
  is_memory: bool,
66
66
  exclude_deprecated: bool = True,
67
+ owner_id: uuid.UUID | None = None,
67
68
  ) -> list[Any]: ...
68
69
  async def update_skill(self, skill_id: uuid.UUID, **kwargs: Any) -> Any | None: ...
69
70
  async def delete_skill(self, skill_id: uuid.UUID) -> None: ...
@@ -144,7 +145,7 @@ class IRepositoryRepo(Protocol):
144
145
  class IWorkflowStateRepository(Protocol):
145
146
  async def create_workflow_state(self, **kwargs: Any) -> Any: ...
146
147
  async def get_workflow_state_by_id(self, state_id: uuid.UUID) -> Any | None: ...
147
- async def get_workflow_state_by_repo(self, repo_id: uuid.UUID) -> Any | None: ...
148
+ async def get_workflow_state_by_repo(self, repo_id: uuid.UUID, *, branch: str = "main") -> Any | None: ...
148
149
  async def update_workflow_state(
149
150
  self, state_id: uuid.UUID, **kwargs: Any
150
151
  ) -> Any | None: ...
@@ -131,7 +131,7 @@ class QdrantOperationalStore:
131
131
  return await self._col("skills").find_many()
132
132
 
133
133
  async def list_skills_by_kind(
134
- self, *, is_memory: bool, exclude_deprecated: bool = True
134
+ self, *, is_memory: bool, exclude_deprecated: bool = True, owner_id: uuid.UUID | None = None
135
135
  ) -> list[Any]:
136
136
  all_skills = await self.list_skills()
137
137
  _mem_langs = {"markdown", "text", "en", "vi", "", None}
@@ -140,6 +140,14 @@ class QdrantOperationalStore:
140
140
  sm = s._data.get("source_metadata")
141
141
  lang = s._data.get("language")
142
142
  is_mem = sm is None and lang in _mem_langs
143
+
144
+ # Apply owner filtering
145
+ if owner_id is not None:
146
+ s_owner = s._data.get("owner_id")
147
+ s_scope = s._data.get("scope", "team")
148
+ if s_owner is not None and s_owner != str(owner_id) and s_scope != "team":
149
+ continue
150
+
143
151
  if is_memory and is_mem:
144
152
  result.append(s)
145
153
  elif not is_memory and not is_mem:
@@ -326,6 +334,7 @@ class QdrantOperationalStore:
326
334
  for uf in ("repo_id", "session_id"):
327
335
  if uf in kw and isinstance(kw[uf], uuid.UUID):
328
336
  kw[uf] = _uid(kw[uf])
337
+ kw.setdefault("branch", "main")
329
338
  kw.setdefault("completed_steps", [])
330
339
  kw.setdefault("blocked_by", [])
331
340
  kw.setdefault("artifacts", {})
@@ -335,8 +344,12 @@ class QdrantOperationalStore:
335
344
  async def get_workflow_state_by_id(self, state_id: uuid.UUID) -> Any:
336
345
  return await self._col("workflow_states").get(_uid(state_id))
337
346
 
338
- async def get_workflow_state_by_repo(self, repo_id: uuid.UUID) -> Any:
339
- return await self._col("workflow_states").find_one("repo_id", _uid(repo_id))
347
+ async def get_workflow_state_by_repo(self, repo_id: uuid.UUID, *, branch: str = "main") -> Any:
348
+ states = await self._col("workflow_states").find_many({"repo_id": _uid(repo_id)})
349
+ for state in states:
350
+ if state._data.get("branch", "main") == branch:
351
+ return state
352
+ return None
340
353
 
341
354
  async def update_workflow_state(self, state_id: uuid.UUID, **kw: Any) -> Any:
342
355
  if not kw:
@@ -125,6 +125,22 @@ class RelationalStore:
125
125
  sync_conn.execute(
126
126
  text("ALTER TABLE skills ADD COLUMN deprecated BOOLEAN NOT NULL DEFAULT 0")
127
127
  )
128
+ # skills.owner_id + skills.scope (multi-developer isolation)
129
+ if "owner_id" not in existing:
130
+ sync_conn.execute(
131
+ text("ALTER TABLE skills ADD COLUMN owner_id VARCHAR(36) DEFAULT NULL")
132
+ )
133
+ if "scope" not in existing:
134
+ sync_conn.execute(
135
+ text("ALTER TABLE skills ADD COLUMN scope VARCHAR(10) NOT NULL DEFAULT 'team'")
136
+ )
137
+
138
+ if "repository_workflow_states" in inspector.get_table_names():
139
+ existing = {col["name"] for col in inspector.get_columns("repository_workflow_states")}
140
+ if "branch" not in existing:
141
+ sync_conn.execute(
142
+ text("ALTER TABLE repository_workflow_states ADD COLUMN branch VARCHAR(255) NOT NULL DEFAULT 'main'")
143
+ )
128
144
 
129
145
  async def dispose(self) -> None:
130
146
  """Dispose the engine connection pool."""
@@ -266,6 +282,7 @@ class RelationalStore:
266
282
  *,
267
283
  is_memory: bool,
268
284
  exclude_deprecated: bool = True,
285
+ owner_id: uuid.UUID | None = None,
269
286
  ) -> List[Skill]:
270
287
  _memory_langs = ["markdown", "text", "en", "vi", ""]
271
288
  _is_memory_cond = Skill.source_metadata.is_(None) & (
@@ -278,6 +295,16 @@ class RelationalStore:
278
295
  stmt = select(Skill).where(~_is_memory_cond)
279
296
  if exclude_deprecated:
280
297
  stmt = stmt.where(Skill.deprecated.isnot(True))
298
+
299
+ if owner_id is not None:
300
+ # Can see their own private memories + team scope memories + legacy memories
301
+ stmt = stmt.where(
302
+ or_(
303
+ Skill.owner_id == owner_id,
304
+ Skill.scope == "team",
305
+ Skill.owner_id.is_(None)
306
+ )
307
+ )
281
308
  result = await sess.execute(stmt)
282
309
  return list(result.scalars().all())
283
310
 
@@ -775,12 +802,13 @@ class RelationalStore:
775
802
  return result.scalar_one_or_none()
776
803
 
777
804
  async def get_workflow_state_by_repo(
778
- self, repo_id: uuid.UUID
805
+ self, repo_id: uuid.UUID, *, branch: str = "main"
779
806
  ) -> Optional[RepositoryWorkflowState]:
780
807
  async with self._session() as sess:
781
808
  result = await sess.execute(
782
809
  select(RepositoryWorkflowState).where(
783
- RepositoryWorkflowState.repo_id == repo_id
810
+ RepositoryWorkflowState.repo_id == repo_id,
811
+ RepositoryWorkflowState.branch == branch
784
812
  )
785
813
  )
786
814
  return result.scalar_one_or_none()
@@ -9,8 +9,8 @@ class RepoStateStore:
9
9
  def __init__(self, state_dir_name: str = ".minder") -> None:
10
10
  self._state_dir_name = state_dir_name
11
11
 
12
- async def read_all(self, repo_path: str) -> dict[str, Any]:
13
- state_dir = self._ensure_state_dir(repo_path)
12
+ async def read_all(self, repo_path: str, branch: str = "main") -> dict[str, Any]:
13
+ state_dir = self._ensure_state_dir(repo_path, branch)
14
14
  return {
15
15
  "workflow": self._read_json(state_dir / "workflow.json", default={}),
16
16
  "context": self._read_json(state_dir / "context.json", default={}),
@@ -18,22 +18,33 @@ class RepoStateStore:
18
18
  "artifacts": self._read_artifacts(state_dir / "artifacts"),
19
19
  }
20
20
 
21
- async def write_workflow_state(self, repo_path: str, payload: dict[str, Any]) -> None:
22
- self._write_json(self._ensure_state_dir(repo_path) / "workflow.json", payload)
21
+ async def write_workflow_state(self, repo_path: str, payload: dict[str, Any], branch: str = "main") -> None:
22
+ self._write_json(self._ensure_state_dir(repo_path, branch) / "workflow.json", payload)
23
23
 
24
- async def write_context(self, repo_path: str, payload: dict[str, Any]) -> None:
25
- self._write_json(self._ensure_state_dir(repo_path) / "context.json", payload)
24
+ async def write_context(self, repo_path: str, payload: dict[str, Any], branch: str = "main") -> None:
25
+ self._write_json(self._ensure_state_dir(repo_path, branch) / "context.json", payload)
26
26
 
27
- async def write_relationships(self, repo_path: str, payload: dict[str, Any]) -> None:
28
- self._write_json(self._ensure_state_dir(repo_path) / "relationships.json", payload)
27
+ async def write_relationships(self, repo_path: str, payload: dict[str, Any], branch: str = "main") -> None:
28
+ self._write_json(self._ensure_state_dir(repo_path, branch) / "relationships.json", payload)
29
29
 
30
- async def write_artifact(self, repo_path: str, name: str, content: str) -> None:
31
- artifacts_dir = self._ensure_state_dir(repo_path) / "artifacts"
30
+ async def write_artifact(self, repo_path: str, name: str, content: str, branch: str = "main") -> None:
31
+ artifacts_dir = self._ensure_state_dir(repo_path, branch) / "artifacts"
32
32
  artifacts_dir.mkdir(parents=True, exist_ok=True)
33
33
  (artifacts_dir / name).write_text(content, encoding="utf-8")
34
34
 
35
- def _ensure_state_dir(self, repo_path: str) -> Path:
36
- state_dir = Path(repo_path) / self._state_dir_name
35
+ def _ensure_state_dir(self, repo_path: str, branch: str = "main") -> Path:
36
+ base_dir = Path(repo_path) / self._state_dir_name
37
+
38
+ # Phase 3: Auto-generate .gitignore in the base directory to prevent merge conflicts
39
+ if not base_dir.exists():
40
+ base_dir.mkdir(parents=True, exist_ok=True)
41
+ gitignore = base_dir / ".gitignore"
42
+ if not gitignore.exists():
43
+ gitignore.write_text("*\n", encoding="utf-8")
44
+
45
+ # Branch isolation: normalize branch name to be safe for file system
46
+ safe_branch = "".join(c if c.isalnum() or c in "-_" else "_" for c in branch)
47
+ state_dir = base_dir / safe_branch
37
48
  state_dir.mkdir(parents=True, exist_ok=True)
38
49
  return state_dir
39
50
 
@@ -65,6 +65,7 @@ class MemoryTools:
65
65
  limit: int,
66
66
  current_step: str | None,
67
67
  artifact_type: str | None,
68
+ owner_id: uuid.UUID | None = None,
68
69
  ) -> list[dict[str, Any]]:
69
70
  graph = self._get_agentic_graph()
70
71
  result = await graph.run(
@@ -72,6 +73,7 @@ class MemoryTools:
72
73
  "original_query": query,
73
74
  "current_step": current_step,
74
75
  "artifact_type": artifact_type,
76
+ "owner_id": owner_id,
75
77
  "target_count": limit,
76
78
  "min_score": float(self._config.memory.recall_min_score),
77
79
  "all_memories": [],
@@ -95,9 +97,10 @@ class MemoryTools:
95
97
  current_step: str | None,
96
98
  artifact_type: str | None,
97
99
  include_raw_scores: bool = False,
100
+ owner_id: uuid.UUID | None = None,
98
101
  ) -> list[dict[str, Any]]:
99
102
  query_embedding = self._embedder.embed(query)
100
- skills = await self._store.list_skills_by_kind(is_memory=True)
103
+ skills = await self._store.list_skills_by_kind(is_memory=True, owner_id=owner_id)
101
104
  vector_results: list[dict[str, Any]] = []
102
105
  corpus: list[dict[str, Any]] = []
103
106
  for skill in skills:
@@ -162,6 +165,8 @@ class MemoryTools:
162
165
  content: str,
163
166
  tags: list[str],
164
167
  language: str,
168
+ owner_id: uuid.UUID | None = None,
169
+ scope: str = "private",
165
170
  ) -> dict[str, Any]:
166
171
  # Normalize the language to a memory-eligible value so that
167
172
  # is_memory_record() always identifies this entry as a memory.
@@ -183,6 +188,8 @@ class MemoryTools:
183
188
  embedding=self._embedder.embed(f"{title}\n{content}"),
184
189
  usage_count=0,
185
190
  quality_score=0.0,
191
+ owner_id=owner_id,
192
+ scope=scope,
186
193
  )
187
194
 
188
195
  # Record persistent audit event
@@ -209,6 +216,7 @@ class MemoryTools:
209
216
  current_step: str | None = None,
210
217
  artifact_type: str | None = None,
211
218
  skip_synthesis: bool = False,
219
+ owner_id: uuid.UUID | None = None,
212
220
  ) -> list[dict[str, Any]]:
213
221
  if skip_synthesis:
214
222
  limited = await self._recall_candidates(
@@ -216,6 +224,7 @@ class MemoryTools:
216
224
  limit=limit,
217
225
  current_step=current_step,
218
226
  artifact_type=artifact_type,
227
+ owner_id=owner_id,
219
228
  )
220
229
  for item in limited:
221
230
  item.pop("_step_compat", None)
@@ -229,6 +238,7 @@ class MemoryTools:
229
238
  limit=limit,
230
239
  current_step=current_step,
231
240
  artifact_type=artifact_type,
241
+ owner_id=owner_id,
232
242
  )
233
243
  except Exception:
234
244
  pass
@@ -238,6 +248,7 @@ class MemoryTools:
238
248
  limit=limit,
239
249
  current_step=current_step,
240
250
  artifact_type=artifact_type,
251
+ owner_id=owner_id,
241
252
  )
242
253
 
243
254
  try:
@@ -283,8 +294,12 @@ class MemoryTools:
283
294
  item.pop("continuity_reasons", None)
284
295
  return limited
285
296
 
286
- async def minder_memory_list(self) -> list[dict[str, Any]]:
287
- skills = await self._store.list_skills_by_kind(is_memory=True)
297
+ async def minder_memory_list(
298
+ self,
299
+ *,
300
+ owner_id: uuid.UUID | None = None,
301
+ ) -> list[dict[str, Any]]:
302
+ skills = await self._store.list_skills_by_kind(is_memory=True, owner_id=owner_id)
288
303
  return [
289
304
  {
290
305
  "id": str(skill.id),
@@ -302,10 +317,16 @@ class MemoryTools:
302
317
  title: str | None = None,
303
318
  content: str | None = None,
304
319
  tags: list[str] | None = None,
320
+ owner_id: uuid.UUID | None = None,
305
321
  ) -> dict[str, Any]:
306
322
  existing = await self._store.get_skill_by_id(uuid.UUID(memory_id))
307
323
  if existing is None or not is_memory_record(existing):
308
324
  raise ValueError(f"Memory not found: {memory_id}")
325
+
326
+ if owner_id is not None:
327
+ existing_owner = getattr(existing, "owner_id", None)
328
+ if existing_owner is not None and str(existing_owner) != str(owner_id):
329
+ raise ValueError(f"Access denied: you do not own memory {memory_id}")
309
330
 
310
331
  update_data: dict[str, Any] = {}
311
332
  next_title = title if title is not None else str(existing.title)
@@ -343,7 +364,15 @@ class MemoryTools:
343
364
  "updated": True,
344
365
  }
345
366
 
346
- async def minder_memory_delete(self, skill_id: str) -> dict[str, bool]:
367
+ async def minder_memory_delete(self, skill_id: str, *, owner_id: uuid.UUID | None = None) -> dict[str, bool]:
368
+ if owner_id is not None:
369
+ existing = await self._store.get_skill_by_id(uuid.UUID(skill_id))
370
+ if existing is None or not is_memory_record(existing):
371
+ raise ValueError(f"Memory not found: {skill_id}")
372
+ existing_owner = getattr(existing, "owner_id", None)
373
+ if existing_owner is not None and str(existing_owner) != str(owner_id):
374
+ raise ValueError(f"Access denied: you do not own memory {skill_id}")
375
+
347
376
  await self._store.delete_skill(uuid.UUID(skill_id))
348
377
 
349
378
  # Record persistent audit event
@@ -367,6 +396,7 @@ class MemoryTools:
367
396
  memory_ids: list[str],
368
397
  similarity_threshold: float = 0.92,
369
398
  dry_run: bool = True,
399
+ owner_id: uuid.UUID | None = None,
370
400
  ) -> dict[str, Any]:
371
401
  normalized_ids = self._normalize_memory_ids(memory_ids)
372
402
  if len(normalized_ids) < 2:
@@ -377,6 +407,11 @@ class MemoryTools:
377
407
  skill = await self._store.get_skill_by_id(uuid.UUID(memory_id))
378
408
  if skill is None:
379
409
  raise ValueError(f"Memory not found: {memory_id}")
410
+
411
+ if owner_id is not None:
412
+ existing_owner = getattr(skill, "owner_id", None)
413
+ if existing_owner is not None and str(existing_owner) != str(owner_id):
414
+ raise ValueError(f"Access denied: you do not own memory {memory_id}")
380
415
  embedding = self._embedder.embed(
381
416
  self._compaction_text(
382
417
  title=str(skill.title),
@@ -35,15 +35,15 @@ class WorkflowTools:
35
35
  return self._graph
36
36
 
37
37
  async def minder_workflow_get(
38
- self, *, repo_id: uuid.UUID, repo_path: str
38
+ self, *, repo_id: uuid.UUID, repo_path: str, branch: str = "main"
39
39
  ) -> dict[str, Any]:
40
40
  repo = await self._require_repo(repo_id)
41
41
  workflow = await self._require_workflow(repo.workflow_id)
42
- state = await self._store.get_workflow_state_by_repo(repo_id)
42
+ state = await self._require_workflow_state(repo_id, branch=branch)
43
43
  relationships = (
44
44
  dict(repo.relationships) if isinstance(repo.relationships, dict) else {}
45
45
  )
46
- await self._repo_state.write_relationships(repo_path, relationships)
46
+ await self._repo_state.write_relationships(repo_path, relationships, branch=branch)
47
47
  if state is not None:
48
48
  await self._repo_state.write_workflow_state(
49
49
  repo_path,
@@ -53,6 +53,7 @@ class WorkflowTools:
53
53
  "blocked_by": list(state.blocked_by),
54
54
  "next_step": state.next_step,
55
55
  },
56
+ branch=branch,
56
57
  )
57
58
  return {
58
59
  "workflow": {
@@ -70,6 +71,7 @@ class WorkflowTools:
70
71
  repo_path: str | None = None,
71
72
  session_id: uuid.UUID | None = None,
72
73
  decision: dict[str, Any] | None = None,
74
+ branch: str = "main",
73
75
  ) -> dict[str, Any]:
74
76
  if session_id is not None or decision is not None:
75
77
  if session_id is None or decision is None:
@@ -100,7 +102,7 @@ class WorkflowTools:
100
102
  raise ValueError("repo_id and repo_path are required when not resuming")
101
103
  repo = await self._require_repo(repo_id)
102
104
  workflow = await self._require_workflow(repo.workflow_id)
103
- state = await self._require_workflow_state(repo_id)
105
+ state = await self._require_workflow_state(repo_id, branch=branch)
104
106
  envelope = build_instruction_envelope(workflow=workflow, workflow_state=state)
105
107
  await self._repo_state.write_workflow_state(
106
108
  repo_path,
@@ -110,6 +112,7 @@ class WorkflowTools:
110
112
  "blocked_by": list(state.blocked_by),
111
113
  "next_step": state.next_step,
112
114
  },
115
+ branch=branch,
113
116
  )
114
117
  for _k in ("workflow_id", "workflow_version", "policies"):
115
118
  envelope.pop(_k, None)
@@ -127,10 +130,11 @@ class WorkflowTools:
127
130
  completed_step: str,
128
131
  artifact_name: str | None = None,
129
132
  artifact_content: str | None = None,
133
+ branch: str = "main",
130
134
  ) -> dict[str, Any]:
131
135
  repo = await self._require_repo(repo_id)
132
136
  workflow = await self._require_workflow(repo.workflow_id)
133
- state = await self._require_workflow_state(repo_id)
137
+ state = await self._require_workflow_state(repo_id, branch=branch)
134
138
  step_names = [
135
139
  step["name"]
136
140
  for step in workflow.steps
@@ -144,7 +148,7 @@ class WorkflowTools:
144
148
  if artifact_name and artifact_content is not None:
145
149
  artifacts[artifact_name] = artifact_content
146
150
  await self._repo_state.write_artifact(
147
- repo_path, artifact_name, artifact_content
151
+ repo_path, artifact_name, artifact_content, branch=branch
148
152
  )
149
153
  updated = await self._store.update_workflow_state(
150
154
  state.id,
@@ -163,10 +167,12 @@ class WorkflowTools:
163
167
  "blocked_by": list(updated.blocked_by),
164
168
  "next_step": updated.next_step,
165
169
  },
170
+ branch=branch,
166
171
  )
167
172
  await self._repo_state.write_relationships(
168
173
  repo_path,
169
174
  dict(repo.relationships) if isinstance(repo.relationships, dict) else {},
175
+ branch=branch,
170
176
  )
171
177
  return {
172
178
  "current_step": updated.current_step,
@@ -180,10 +186,11 @@ class WorkflowTools:
180
186
  repo_id: uuid.UUID,
181
187
  requested_step: str,
182
188
  action: str | None = None,
189
+ branch: str = "main",
183
190
  ) -> dict[str, Any]:
184
191
  repo = await self._require_repo(repo_id)
185
192
  workflow = await self._require_workflow(repo.workflow_id)
186
- state = await self._require_workflow_state(repo_id)
193
+ state = await self._require_workflow_state(repo_id, branch=branch)
187
194
  step_names = [
188
195
  step["name"]
189
196
  for step in workflow.steps
@@ -234,8 +241,8 @@ class WorkflowTools:
234
241
  raise ValueError(f"Workflow not found: {workflow_id}")
235
242
  return workflow
236
243
 
237
- async def _require_workflow_state(self, repo_id: uuid.UUID): # noqa: ANN202
238
- state = await self._store.get_workflow_state_by_repo(repo_id)
244
+ async def _require_workflow_state(self, repo_id: uuid.UUID, *, branch: str = "main"): # noqa: ANN202
245
+ state = await self._store.get_workflow_state_by_repo(repo_id, branch=branch)
239
246
  if state is None:
240
247
  raise ValueError(f"Workflow state not found for repo: {repo_id}")
241
248
  return state
File without changes
File without changes
File without changes
File without changes