minder-cli 0.4.1__tar.gz → 0.4.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. {minder_cli-0.4.1 → minder_cli-0.4.2}/PKG-INFO +1 -1
  2. {minder_cli-0.4.1 → minder_cli-0.4.2}/pyproject.toml +1 -1
  3. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/cli/commands/agent.py +108 -43
  4. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/cli/commands/mcp.py +77 -8
  5. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/cli/main.py +84 -16
  6. {minder_cli-0.4.1 → minder_cli-0.4.2}/.gitignore +0 -0
  7. {minder_cli-0.4.1 → minder_cli-0.4.2}/LICENSE +0 -0
  8. {minder_cli-0.4.1 → minder_cli-0.4.2}/README-pypi.md +0 -0
  9. {minder_cli-0.4.1 → minder_cli-0.4.2}/README.md +0 -0
  10. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/__init__.py +0 -0
  11. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/api/routers/prompts.py +0 -0
  12. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/application/__init__.py +0 -0
  13. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/application/admin/__init__.py +0 -0
  14. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/application/admin/dto.py +0 -0
  15. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/application/admin/jobs.py +0 -0
  16. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/application/admin/use_cases.py +0 -0
  17. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/auth/__init__.py +0 -0
  18. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/auth/context.py +0 -0
  19. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/auth/middleware.py +0 -0
  20. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/auth/principal.py +0 -0
  21. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/auth/rate_limiter.py +0 -0
  22. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/auth/rbac.py +0 -0
  23. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/auth/service.py +0 -0
  24. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/bootstrap/__init__.py +0 -0
  25. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/bootstrap/providers.py +0 -0
  26. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/bootstrap/transport.py +0 -0
  27. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/cache/__init__.py +0 -0
  28. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/cache/providers.py +0 -0
  29. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/chunking/__init__.py +0 -0
  30. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/chunking/code_splitter.py +0 -0
  31. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/chunking/splitter.py +0 -0
  32. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/cli.py +0 -0
  33. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/config.py +0 -0
  34. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/continuity.py +0 -0
  35. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/dev.py +0 -0
  36. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/embedding/__init__.py +0 -0
  37. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/embedding/base.py +0 -0
  38. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/embedding/local.py +0 -0
  39. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/embedding/openai.py +0 -0
  40. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/graph/__init__.py +0 -0
  41. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/graph/edges.py +0 -0
  42. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/graph/executor.py +0 -0
  43. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/graph/graph.py +0 -0
  44. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/graph/nodes/__init__.py +0 -0
  45. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/graph/nodes/evaluator.py +0 -0
  46. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/graph/nodes/guard.py +0 -0
  47. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/graph/nodes/llm.py +0 -0
  48. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/graph/nodes/planning.py +0 -0
  49. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/graph/nodes/reasoning.py +0 -0
  50. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/graph/nodes/reranker.py +0 -0
  51. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/graph/nodes/retriever.py +0 -0
  52. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/graph/nodes/verification.py +0 -0
  53. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/graph/nodes/workflow_planner.py +0 -0
  54. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/graph/runtime.py +0 -0
  55. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/graph/state.py +0 -0
  56. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/llm/__init__.py +0 -0
  57. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/llm/base.py +0 -0
  58. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/llm/factory.py +0 -0
  59. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/llm/litert.py +0 -0
  60. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/llm/openai.py +0 -0
  61. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/models/__init__.py +0 -0
  62. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/models/base.py +0 -0
  63. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/models/client.py +0 -0
  64. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/models/document.py +0 -0
  65. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/models/error.py +0 -0
  66. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/models/graph.py +0 -0
  67. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/models/history.py +0 -0
  68. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/models/job.py +0 -0
  69. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/models/prompt.py +0 -0
  70. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/models/repository.py +0 -0
  71. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/models/rule.py +0 -0
  72. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/models/session.py +0 -0
  73. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/models/skill.py +0 -0
  74. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/models/user.py +0 -0
  75. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/models/workflow.py +0 -0
  76. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/observability/__init__.py +0 -0
  77. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/observability/audit.py +0 -0
  78. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/observability/logging.py +0 -0
  79. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/observability/metrics.py +0 -0
  80. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/observability/tracing.py +0 -0
  81. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/__init__.py +0 -0
  82. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/cli/__init__.py +0 -0
  83. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/cli/commands/auth.py +0 -0
  84. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/cli/commands/ide.py +0 -0
  85. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/cli/commands/sync.py +0 -0
  86. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/cli/commands/update.py +0 -0
  87. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/cli/utils/common.py +0 -0
  88. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/cli/utils/config.py +0 -0
  89. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/cli/utils/git.py +0 -0
  90. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/cli/utils/version.py +0 -0
  91. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/http/__init__.py +0 -0
  92. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/http/admin/__init__.py +0 -0
  93. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/http/admin/api.py +0 -0
  94. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/http/admin/context.py +0 -0
  95. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/http/admin/dashboard.py +0 -0
  96. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/http/admin/jobs.py +0 -0
  97. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/http/admin/memories.py +0 -0
  98. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/http/admin/prompts.py +0 -0
  99. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/http/admin/routes.py +0 -0
  100. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/http/admin/runtime.py +0 -0
  101. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/http/admin/search.py +0 -0
  102. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/presentation/http/admin/skills.py +0 -0
  103. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/prompts/__init__.py +0 -0
  104. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/prompts/formatter.py +0 -0
  105. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/resources/__init__.py +0 -0
  106. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/retrieval/__init__.py +0 -0
  107. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/retrieval/hybrid.py +0 -0
  108. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/retrieval/mmr.py +0 -0
  109. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/retrieval/multi_hop.py +0 -0
  110. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/runtime.py +0 -0
  111. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/server.py +0 -0
  112. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/store/__init__.py +0 -0
  113. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/store/document.py +0 -0
  114. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/store/error.py +0 -0
  115. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/store/feedback.py +0 -0
  116. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/store/graph.py +0 -0
  117. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/store/history.py +0 -0
  118. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/store/interfaces.py +0 -0
  119. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/store/milvus/__init__.py +0 -0
  120. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/store/milvus/client.py +0 -0
  121. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/store/milvus/collections.py +0 -0
  122. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/store/milvus/vector_store.py +0 -0
  123. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/store/mongodb/__init__.py +0 -0
  124. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/store/mongodb/client.py +0 -0
  125. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/store/mongodb/indexes.py +0 -0
  126. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/store/mongodb/operational_store.py +0 -0
  127. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/store/relational.py +0 -0
  128. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/store/repo_state.py +0 -0
  129. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/store/rule.py +0 -0
  130. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/store/vector.py +0 -0
  131. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/tools/__init__.py +0 -0
  132. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/tools/auth.py +0 -0
  133. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/tools/graph.py +0 -0
  134. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/tools/ingest.py +0 -0
  135. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/tools/memory.py +0 -0
  136. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/tools/query.py +0 -0
  137. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/tools/registry.py +0 -0
  138. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/tools/repo_scanner.py +0 -0
  139. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/tools/search.py +0 -0
  140. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/tools/session.py +0 -0
  141. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/tools/skills.py +0 -0
  142. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/tools/workflow.py +0 -0
  143. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/transport/__init__.py +0 -0
  144. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/transport/base.py +0 -0
  145. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/transport/sse.py +0 -0
  146. {minder_cli-0.4.1 → minder_cli-0.4.2}/src/minder/transport/stdio.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: minder-cli
3
- Version: 0.4.1
3
+ Version: 0.4.2
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.4.1"
7
+ version = "0.4.2"
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"
@@ -7,6 +7,7 @@ from ..utils.common import (
7
7
  upsert_managed_block,
8
8
  remove_managed_block,
9
9
  wrap_managed_block,
10
+ marker_pair,
10
11
  )
11
12
  from ..utils.version import installed_package_version
12
13
 
@@ -15,6 +16,11 @@ _ANTIGRAVITY_FRONT_MATTER = """---
15
16
  description: Minder is your agentic engineering copilot for repo-aware development, workflow governance, and persistent session continuity.
16
17
  ---"""
17
18
 
19
+ _CLAUDE_CODE_FRONT_MATTER = """---
20
+ name: minder
21
+ description: Minder is your agentic engineering copilot for repo-aware development, workflow governance, and persistent session continuity.
22
+ ---"""
23
+
18
24
  MINDER_AGENT_PROMPT = """# Minder Agent Orchestration Rules
19
25
 
20
26
  You are an expert AI software engineer equipped with **Minder**, an agentic development infrastructure. Your goal is to provide deep, grounded assistance by orchestrating Minder's tools effectively.
@@ -93,27 +99,48 @@ When implementing a feature or fix:
93
99
 
94
100
  def _agent_instruction_path(target: str, cwd: Path) -> Path | None:
95
101
  if target == "vscode":
96
- return cwd / ".github" / "copilot-instructions.md"
102
+ return Path.home() / ".copilot" / "agents" / "minder.agent.md"
97
103
  if target == "cursor":
98
- return cwd / ".cursor" / "rules" / "minder.mdc"
104
+ return cwd / "AGENTS.md"
99
105
  if target == "claude-code":
100
- return cwd / "CLAUDE.md"
106
+ return Path.home() / ".claude" / "agents" / "minder.md"
101
107
  if target == "antigravity":
102
- return cwd / ".agents" / "workflows" / "minder.md"
108
+ return cwd / ".gemini" / "antigravity" / "global_workflows" / "minder.md"
103
109
  if target == "codex":
104
110
  return Path.home() / ".codex" / "AGENTS.md"
105
111
  return None
106
112
 
107
113
 
108
- def _upsert_antigravity_workflow(path: Path, body: str) -> None:
114
+ def _get_installed_version(path: Path) -> str | None:
115
+ """Extract the minder version embedded in an installed agent managed block."""
116
+ if not path.is_file():
117
+ return None
118
+ content = path.read_text(encoding="utf-8")
119
+ start, _ = marker_pair(path, _AGENT_INSTRUCTIONS_KEY)
120
+ if start not in content:
121
+ return None
122
+ _, after = content.split(start, 1)
123
+ first_line = after.lstrip("\n").split("\n")[0].strip()
124
+ prefix = "<!-- minder-agent-version:"
125
+ suffix = "-->"
126
+ if first_line.startswith(prefix) and first_line.endswith(suffix):
127
+ return first_line[len(prefix):-len(suffix)].strip()
128
+ return None
129
+
130
+
131
+ def _agent_body_with_version(body: str, version: str) -> str:
132
+ return f"<!-- minder-agent-version: {version} -->\n{body}"
133
+
134
+
135
+ def _upsert_with_front_matter(path: Path, front_matter: str, body: str) -> None:
109
136
  # Migrate away legacy layout where managed marker was the first line.
110
137
  remove_managed_block(path, _AGENT_INSTRUCTIONS_KEY)
111
138
  existing = path.read_text(encoding="utf-8") if path.is_file() else ""
112
139
  tail = existing
113
- if tail.startswith(_ANTIGRAVITY_FRONT_MATTER):
114
- tail = tail[len(_ANTIGRAVITY_FRONT_MATTER):].lstrip("\n")
140
+ if tail.startswith(front_matter):
141
+ tail = tail[len(front_matter):].lstrip("\n")
115
142
  managed = wrap_managed_block(path, _AGENT_INSTRUCTIONS_KEY, body).rstrip("\n")
116
- updated = f"{_ANTIGRAVITY_FRONT_MATTER}\n\n{managed}"
143
+ updated = f"{front_matter}\n\n{managed}"
117
144
  tail = tail.strip("\n")
118
145
  if tail:
119
146
  updated = f"{updated}\n\n{tail}"
@@ -121,67 +148,105 @@ def _upsert_antigravity_workflow(path: Path, body: str) -> None:
121
148
  path.write_text(updated + "\n", encoding="utf-8")
122
149
 
123
150
 
124
- def _cleanup_antigravity_front_matter(path: Path) -> None:
151
+ def _cleanup_front_matter(path: Path, front_matter: str) -> None:
125
152
  if not path.is_file():
126
153
  return
127
154
  content = path.read_text(encoding="utf-8")
128
- if not content.startswith(_ANTIGRAVITY_FRONT_MATTER):
155
+ if not content.startswith(front_matter):
129
156
  return
130
- tail = content[len(_ANTIGRAVITY_FRONT_MATTER):].lstrip("\n")
157
+ tail = content[len(front_matter):].lstrip("\n")
131
158
  if tail:
132
159
  path.write_text(tail, encoding="utf-8")
133
160
  else:
134
161
  path.unlink(missing_ok=True)
135
162
 
163
+
164
+ def _display_path(path: Path, cwd: Path) -> str:
165
+ try:
166
+ return str(path.relative_to(cwd))
167
+ except ValueError:
168
+ return str(path)
169
+
136
170
  def install_agent_command(args: argparse.Namespace) -> int:
137
171
  cwd = Path(args.cwd).resolve()
138
172
  targets = args.target or ["all"]
139
173
  if "all" in targets:
140
174
  targets = ["vscode", "cursor", "claude-code", "antigravity", "codex"]
141
-
175
+
142
176
  version = installed_package_version() or "unknown"
143
- installed_paths = []
177
+ installed_list: list[tuple[Path, str | None]] = []
178
+ skipped_list: list[tuple[Path, str]] = []
179
+
144
180
  for target in targets:
145
181
  path = _agent_instruction_path(target, cwd)
146
- if path:
147
- path.parent.mkdir(parents=True, exist_ok=True)
148
- body = MINDER_AGENT_PROMPT
149
- if target == "antigravity":
150
- _upsert_antigravity_workflow(path, body)
151
- installed_paths.append(path)
152
- continue
153
- if target == "codex":
154
- # Prefix with version as requested
155
- body = f"# Minder Agent (v{version})\n\n" + body
156
-
157
- upsert_managed_block(
158
- path,
159
- _AGENT_INSTRUCTIONS_KEY,
160
- body,
161
- )
162
- installed_paths.append(path)
163
-
164
- if not installed_paths:
182
+ if not path:
183
+ continue
184
+
185
+ existing_version = _get_installed_version(path)
186
+ if existing_version == version:
187
+ skipped_list.append((path, version))
188
+ continue
189
+
190
+ path.parent.mkdir(parents=True, exist_ok=True)
191
+ body = _agent_body_with_version(MINDER_AGENT_PROMPT, version)
192
+
193
+ if target == "antigravity":
194
+ _upsert_with_front_matter(path, _ANTIGRAVITY_FRONT_MATTER, body)
195
+ elif target == "claude-code":
196
+ _upsert_with_front_matter(path, _CLAUDE_CODE_FRONT_MATTER, body)
197
+ else:
198
+ upsert_managed_block(path, _AGENT_INSTRUCTIONS_KEY, body)
199
+
200
+ installed_list.append((path, existing_version))
201
+
202
+ if not installed_list and not skipped_list:
165
203
  print("No valid targets found for agent installation.")
166
204
  return 1
167
-
168
- print(f"Installed sophisticated Minder Agent rules in {len(installed_paths)} location(s).")
169
- for p in installed_paths:
170
- print(f" - {p.relative_to(cwd) if p.is_relative_to(cwd) else p}")
205
+
206
+ for path, old_ver in installed_list:
207
+ display = _display_path(path, cwd)
208
+ if old_ver:
209
+ print(f" Updated {display} (v{old_ver} → v{version})")
210
+ else:
211
+ print(f" Installed {display} (v{version})")
212
+
213
+ for path, ver in skipped_list:
214
+ print(f" Already up to date: {_display_path(path, cwd)} (v{ver})")
215
+
216
+ if installed_list:
217
+ print(f"Minder Agent rules installed/updated in {len(installed_list)} location(s).")
218
+ else:
219
+ print(f"All {len(skipped_list)} agent file(s) already up to date (v{version}).")
220
+
171
221
  return 0
172
222
 
223
+
173
224
  def uninstall_agent_command(args: argparse.Namespace) -> int:
174
225
  cwd = Path(args.cwd).resolve()
175
226
  targets = args.target or ["all"]
176
227
  if "all" in targets:
177
228
  targets = ["vscode", "cursor", "claude-code", "antigravity", "codex"]
178
-
229
+
230
+ removed: list[Path] = []
179
231
  for target in targets:
180
232
  path = _agent_instruction_path(target, cwd)
181
- if path:
182
- remove_managed_block(path, _AGENT_INSTRUCTIONS_KEY)
183
- if target == "antigravity":
184
- _cleanup_antigravity_front_matter(path)
185
-
186
- print(f"Removed Minder Agent rules from {cwd}")
233
+ if not path:
234
+ continue
235
+ removed_block = remove_managed_block(path, _AGENT_INSTRUCTIONS_KEY)
236
+ if target == "antigravity":
237
+ _cleanup_front_matter(path, _ANTIGRAVITY_FRONT_MATTER)
238
+ elif target == "claude-code":
239
+ _cleanup_front_matter(path, _CLAUDE_CODE_FRONT_MATTER)
240
+ if removed_block:
241
+ removed.append(path)
242
+
243
+ if removed:
244
+ print(f"Removed Minder Agent rules from {len(removed)} location(s).")
245
+ for p in removed:
246
+ print(f" - {_display_path(p, cwd)}")
247
+ else:
248
+ print("No Minder Agent rules found to remove.")
187
249
  return 0
250
+
251
+
252
+ remove_agent_command = uninstall_agent_command
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import argparse
4
4
  import platform
5
+ import re
5
6
  from pathlib import Path
6
7
  from typing import Any
7
8
 
@@ -15,6 +16,11 @@ from ..utils.common import (
15
16
  from ..utils.config import require_client_settings
16
17
 
17
18
  _LOCAL_MCP_TARGETS = ("vscode", "cursor", "claude-code", "antigravity")
19
+ _ALL_MCP_TARGETS = (*_LOCAL_MCP_TARGETS, "codex")
20
+
21
+ # Matches [mcp_servers.minder] and all its key/value lines up to the next
22
+ # section header or end of file. [^\[] also matches newlines in Python.
23
+ _CODEX_SECTION_RE = re.compile(r"^\[mcp_servers\.minder\][^\[]*", re.MULTILINE)
18
24
 
19
25
 
20
26
  def _global_target_path(target: str) -> Path:
@@ -39,9 +45,49 @@ def _global_target_path(target: str) -> Path:
39
45
  return Path.home() / ".config" / "Claude" / "claude_desktop_config.json"
40
46
  if target == "antigravity":
41
47
  return Path.home() / ".gemini" / "antigravity" / "mcp_config.json"
48
+ if target == "codex":
49
+ return Path.home() / ".codex" / "config.toml"
42
50
  raise ValueError(f"Unknown global target: {target}")
43
51
 
44
52
 
53
+ def _codex_mcp_section(url: str, client_key: str) -> str:
54
+ return (
55
+ "[mcp_servers.minder]\n"
56
+ f'url = "{url}"\n'
57
+ f'http_headers = {{ "X-Minder-Client-Key" = "{client_key}" }}\n'
58
+ )
59
+
60
+
61
+ def _install_codex_mcp(url: str, client_key: str) -> None:
62
+ path = _global_target_path("codex")
63
+ new_section = _codex_mcp_section(url, client_key)
64
+ existing = path.read_text(encoding="utf-8") if path.is_file() else ""
65
+ if _CODEX_SECTION_RE.search(existing):
66
+ updated = _CODEX_SECTION_RE.sub(new_section, existing)
67
+ else:
68
+ updated = existing.rstrip("\n")
69
+ if updated:
70
+ updated += "\n\n"
71
+ updated += new_section
72
+ path.parent.mkdir(parents=True, exist_ok=True)
73
+ path.write_text(updated, encoding="utf-8")
74
+
75
+
76
+ def _uninstall_codex_mcp() -> bool:
77
+ path = _global_target_path("codex")
78
+ if not path.is_file():
79
+ return False
80
+ existing = path.read_text(encoding="utf-8")
81
+ if not _CODEX_SECTION_RE.search(existing):
82
+ return False
83
+ updated = _CODEX_SECTION_RE.sub("", existing).strip("\n")
84
+ if updated:
85
+ path.write_text(updated + "\n", encoding="utf-8")
86
+ else:
87
+ path.unlink(missing_ok=True)
88
+ return True
89
+
90
+
45
91
  def local_target_path(target: str, cwd: Path) -> Path:
46
92
  if target == "vscode":
47
93
  return cwd / ".vscode" / "mcp.json"
@@ -101,14 +147,25 @@ def _target_entry(
101
147
  def install_mcp_command(args: argparse.Namespace) -> int:
102
148
  config_path = Path(args.config_path).expanduser().resolve()
103
149
  settings = require_client_settings(config_path)
104
-
150
+
105
151
  targets = args.target or ["all"]
106
152
  if "all" in targets:
107
- targets = list(_LOCAL_MCP_TARGETS)
108
-
153
+ targets = list(_ALL_MCP_TARGETS)
154
+
109
155
  cwd = Path(args.cwd).resolve()
110
-
156
+
111
157
  for target in targets:
158
+ if target == "codex":
159
+ try:
160
+ _install_codex_mcp(
161
+ sse_url(settings.get("server_url") or ""),
162
+ settings["client_api_key"],
163
+ )
164
+ print(f"Installed Minder MCP config for codex at {_global_target_path('codex')}")
165
+ except Exception as e:
166
+ print(f"Failed to install MCP for codex: {e}")
167
+ continue
168
+
112
169
  try:
113
170
  if args.global_install:
114
171
  path = _global_target_path(target)
@@ -138,11 +195,20 @@ def install_mcp_command(args: argparse.Namespace) -> int:
138
195
  def uninstall_mcp_command(args: argparse.Namespace) -> int:
139
196
  targets = args.target or ["all"]
140
197
  if "all" in targets:
141
- targets = list(_LOCAL_MCP_TARGETS)
142
-
198
+ targets = list(_ALL_MCP_TARGETS)
199
+
143
200
  cwd = Path(args.cwd).resolve()
144
-
201
+
145
202
  for target in targets:
203
+ if target == "codex":
204
+ try:
205
+ removed = _uninstall_codex_mcp()
206
+ if removed:
207
+ print(f"Removed Minder MCP config for codex from {_global_target_path('codex')}")
208
+ except Exception as e:
209
+ print(f"Failed to uninstall MCP for codex: {e}")
210
+ continue
211
+
146
212
  try:
147
213
  if args.global_install:
148
214
  path = _global_target_path(target)
@@ -165,5 +231,8 @@ def uninstall_mcp_command(args: argparse.Namespace) -> int:
165
231
  print(f"Removed Minder MCP config for {target} from {path}")
166
232
  except Exception as e:
167
233
  print(f"Failed to uninstall MCP for {target}: {e}")
168
-
234
+
169
235
  return 0
236
+
237
+
238
+ remove_mcp_command = uninstall_mcp_command
@@ -5,9 +5,9 @@ import argparse
5
5
  from .utils.version import installed_package_version
6
6
  from .utils.common import client_config_path
7
7
  from .commands.auth import login_command
8
- from .commands.mcp import install_mcp_command, uninstall_mcp_command
8
+ from .commands.mcp import install_mcp_command, uninstall_mcp_command, remove_mcp_command, _global_target_path
9
9
  from .commands.ide import install_ide_command, uninstall_ide_command
10
- from .commands.agent import install_agent_command, uninstall_agent_command
10
+ from .commands.agent import install_agent_command, uninstall_agent_command, remove_agent_command
11
11
  from .commands.update import version_command, check_update_command, update_command
12
12
  from .commands.sync import sync_command
13
13
 
@@ -41,10 +41,29 @@ def build_parser() -> argparse.ArgumentParser:
41
41
  install = subparsers.add_parser("install", help="Install Minder integration (MCP/IDE).")
42
42
  install_subs = install.add_subparsers(dest="subcommand", required=True)
43
43
 
44
- mcp_in = install_subs.add_parser("mcp", help="Install MCP server config.")
45
- mcp_in.add_argument("--target", action="append", help="Target IDE (vscode, cursor, claude-code, all).")
46
- mcp_in.add_argument("--global", dest="global_install", action="store_true", help="Install globally.")
47
- mcp_in.add_argument("--cwd", default=".", help="Workspace directory.")
44
+ _cwd_placeholder = "<repo>"
45
+ _mcp_epilog = (
46
+ "targets:\n"
47
+ f" vscode per-repo: {_cwd_placeholder}/.vscode/mcp.json\n"
48
+ f" --global: {_global_target_path('vscode')}\n"
49
+ f" cursor per-repo: {_cwd_placeholder}/.cursor/mcp.json\n"
50
+ f" --global: {_global_target_path('cursor')}\n"
51
+ f" claude-code per-repo: {_cwd_placeholder}/.claude/mcp.json\n"
52
+ f" --global: {_global_target_path('claude-code')}\n"
53
+ f" antigravity always: {_global_target_path('antigravity')} [--global has no effect]\n"
54
+ f" codex always: {_global_target_path('codex')} [--global has no effect]\n"
55
+ " all all targets above (default)\n"
56
+ )
57
+
58
+ mcp_in = install_subs.add_parser(
59
+ "mcp",
60
+ help="Install MCP server config.",
61
+ formatter_class=argparse.RawDescriptionHelpFormatter,
62
+ epilog=_mcp_epilog,
63
+ )
64
+ mcp_in.add_argument("--target", action="append", metavar="TARGET", help="Target to install (see targets below).")
65
+ mcp_in.add_argument("--global", dest="global_install", action="store_true", help="Write to the IDE's global config instead of the repo-local file.")
66
+ mcp_in.add_argument("--cwd", default=".", help="Workspace directory (used for per-repo targets).")
48
67
  mcp_in.add_argument("--config-path", default=str(client_config_path()), help="Path to client config.")
49
68
  mcp_in.set_defaults(func=install_mcp_command)
50
69
 
@@ -54,18 +73,37 @@ def build_parser() -> argparse.ArgumentParser:
54
73
  ide_in.add_argument("--config-path", default=str(client_config_path()), help="Path to client config.")
55
74
  ide_in.set_defaults(func=install_ide_command)
56
75
 
57
- agent_in = install_subs.add_parser("agent", help="Install sophisticated Minder Agent rules.")
58
- agent_in.add_argument("--target", action="append", help="Target IDE.")
59
- agent_in.add_argument("--cwd", default=".", help="Workspace directory.")
76
+ _agent_epilog = """\
77
+ targets (scope):
78
+ vscode ~/.copilot/agents/minder.agent.md [global all repos]
79
+ claude-code ~/.claude/agents/minder.md [global – all repos]
80
+ codex ~/.codex/AGENTS.md [global – all repos]
81
+ cursor <repo>/AGENTS.md [per-repo]
82
+ antigravity <repo>/.gemini/antigravity/global_workflows/ [per-repo]
83
+ all all targets above (default)
84
+ """
85
+ agent_in = install_subs.add_parser(
86
+ "agent",
87
+ help="Install Minder Agent rules.",
88
+ formatter_class=argparse.RawDescriptionHelpFormatter,
89
+ epilog=_agent_epilog,
90
+ )
91
+ agent_in.add_argument("--target", action="append", metavar="TARGET", help="Target to install (see targets below).")
92
+ agent_in.add_argument("--cwd", default=".", help="Workspace directory (used for per-repo targets).")
60
93
  agent_in.set_defaults(func=install_agent_command)
61
94
 
62
95
  uninstall = subparsers.add_parser("uninstall", help="Remove Minder integration.")
63
96
  uninstall_subs = uninstall.add_subparsers(dest="subcommand", required=True)
64
97
 
65
- mcp_un = uninstall_subs.add_parser("mcp", help="Remove MCP server config.")
66
- mcp_un.add_argument("--target", action="append", help="Target IDE.")
67
- mcp_un.add_argument("--global", dest="global_install", action="store_true", help="Remove from global config.")
68
- mcp_un.add_argument("--cwd", default=".", help="Workspace directory.")
98
+ mcp_un = uninstall_subs.add_parser(
99
+ "mcp",
100
+ help="Remove MCP server config.",
101
+ formatter_class=argparse.RawDescriptionHelpFormatter,
102
+ epilog=_mcp_epilog,
103
+ )
104
+ mcp_un.add_argument("--target", action="append", metavar="TARGET", help="Target to remove (see targets below).")
105
+ mcp_un.add_argument("--global", dest="global_install", action="store_true", help="Remove from the IDE's global config instead of the repo-local file.")
106
+ mcp_un.add_argument("--cwd", default=".", help="Workspace directory (used for per-repo targets).")
69
107
  mcp_un.set_defaults(func=uninstall_mcp_command)
70
108
 
71
109
  ide_un = uninstall_subs.add_parser("ide", help="Remove IDE bootstrap assets.")
@@ -73,11 +111,41 @@ def build_parser() -> argparse.ArgumentParser:
73
111
  ide_un.add_argument("--cwd", default=".", help="Workspace directory.")
74
112
  ide_un.set_defaults(func=uninstall_ide_command)
75
113
 
76
- agent_un = uninstall_subs.add_parser("agent", help="Remove Minder Agent rules.")
77
- agent_un.add_argument("--target", action="append", help="Target IDE.")
78
- agent_un.add_argument("--cwd", default=".", help="Workspace directory.")
114
+ agent_un = uninstall_subs.add_parser(
115
+ "agent",
116
+ help="Remove Minder Agent rules.",
117
+ formatter_class=argparse.RawDescriptionHelpFormatter,
118
+ epilog=_agent_epilog,
119
+ )
120
+ agent_un.add_argument("--target", action="append", metavar="TARGET", help="Target to remove (see targets below).")
121
+ agent_un.add_argument("--cwd", default=".", help="Workspace directory (used for per-repo targets).")
79
122
  agent_un.set_defaults(func=uninstall_agent_command)
80
123
 
124
+ # --- Remove (alias for uninstall) ---
125
+ remove = subparsers.add_parser("remove", help="Remove Minder integration (alias for uninstall).")
126
+ remove_subs = remove.add_subparsers(dest="subcommand", required=True)
127
+
128
+ remove_mcp = remove_subs.add_parser(
129
+ "mcp",
130
+ help="Remove MCP server config.",
131
+ formatter_class=argparse.RawDescriptionHelpFormatter,
132
+ epilog=_mcp_epilog,
133
+ )
134
+ remove_mcp.add_argument("--target", action="append", metavar="TARGET", help="Target to remove (see targets below).")
135
+ remove_mcp.add_argument("--global", dest="global_install", action="store_true", help="Remove from the IDE's global config instead of the repo-local file.")
136
+ remove_mcp.add_argument("--cwd", default=".", help="Workspace directory (used for per-repo targets).")
137
+ remove_mcp.set_defaults(func=remove_mcp_command)
138
+
139
+ remove_agent = remove_subs.add_parser(
140
+ "agent",
141
+ help="Remove Minder Agent rules.",
142
+ formatter_class=argparse.RawDescriptionHelpFormatter,
143
+ epilog=_agent_epilog,
144
+ )
145
+ remove_agent.add_argument("--target", action="append", metavar="TARGET", help="Target to remove (see targets below).")
146
+ remove_agent.add_argument("--cwd", default=".", help="Workspace directory (used for per-repo targets).")
147
+ remove_agent.set_defaults(func=remove_agent_command)
148
+
81
149
  # --- Sync ---
82
150
  sync = subparsers.add_parser("sync", help="Sync repository state with Minder server.")
83
151
  sync.add_argument("--repo-id", help="Repository UUID.")
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes