agent-brain-cli 8.0.0__tar.gz → 9.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 (37) hide show
  1. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/PKG-INFO +2 -2
  2. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/agent_brain_cli/__init__.py +1 -1
  3. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/agent_brain_cli/cli.py +2 -0
  4. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/agent_brain_cli/commands/__init__.py +2 -0
  5. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/agent_brain_cli/commands/config.py +8 -5
  6. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/agent_brain_cli/commands/init.py +8 -4
  7. agent_brain_cli-9.2.0/agent_brain_cli/commands/install_agent.py +340 -0
  8. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/agent_brain_cli/commands/start.py +5 -2
  9. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/agent_brain_cli/commands/stop.py +5 -2
  10. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/agent_brain_cli/commands/uninstall.py +1 -1
  11. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/agent_brain_cli/config.py +27 -15
  12. agent_brain_cli-9.2.0/agent_brain_cli/migration.py +117 -0
  13. agent_brain_cli-9.2.0/agent_brain_cli/runtime/__init__.py +59 -0
  14. agent_brain_cli-9.2.0/agent_brain_cli/runtime/claude_converter.py +155 -0
  15. agent_brain_cli-9.2.0/agent_brain_cli/runtime/codex_converter.py +215 -0
  16. agent_brain_cli-9.2.0/agent_brain_cli/runtime/converter_base.py +73 -0
  17. agent_brain_cli-9.2.0/agent_brain_cli/runtime/gemini_converter.py +119 -0
  18. agent_brain_cli-9.2.0/agent_brain_cli/runtime/opencode_converter.py +146 -0
  19. agent_brain_cli-9.2.0/agent_brain_cli/runtime/parser.py +330 -0
  20. agent_brain_cli-9.2.0/agent_brain_cli/runtime/skill_runtime_converter.py +234 -0
  21. agent_brain_cli-9.2.0/agent_brain_cli/runtime/tool_maps.py +82 -0
  22. agent_brain_cli-9.2.0/agent_brain_cli/runtime/types.py +122 -0
  23. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/pyproject.toml +2 -2
  24. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/README.md +0 -0
  25. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/agent_brain_cli/client/__init__.py +0 -0
  26. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/agent_brain_cli/client/api_client.py +0 -0
  27. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/agent_brain_cli/commands/cache.py +0 -0
  28. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/agent_brain_cli/commands/folders.py +0 -0
  29. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/agent_brain_cli/commands/index.py +0 -0
  30. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/agent_brain_cli/commands/inject.py +0 -0
  31. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/agent_brain_cli/commands/jobs.py +0 -0
  32. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/agent_brain_cli/commands/list_cmd.py +0 -0
  33. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/agent_brain_cli/commands/query.py +0 -0
  34. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/agent_brain_cli/commands/reset.py +0 -0
  35. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/agent_brain_cli/commands/status.py +0 -0
  36. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/agent_brain_cli/commands/types.py +0 -0
  37. {agent_brain_cli-8.0.0 → agent_brain_cli-9.2.0}/agent_brain_cli/xdg_paths.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: agent-brain-cli
3
- Version: 8.0.0
3
+ Version: 9.2.0
4
4
  Summary: Agent Brain CLI - Command-line interface for managing AI agent memory and knowledge retrieval
5
5
  Home-page: https://github.com/SpillwaveSolutions/agent-brain
6
6
  License: MIT
@@ -15,7 +15,7 @@ Classifier: Programming Language :: Python :: 3
15
15
  Classifier: Programming Language :: Python :: 3.10
16
16
  Classifier: Programming Language :: Python :: 3.11
17
17
  Classifier: Programming Language :: Python :: 3.12
18
- Requires-Dist: agent-brain-rag (>=8.0.0,<9.0.0)
18
+ Requires-Dist: agent-brain-rag (>=9.2.0,<10.0.0)
19
19
  Requires-Dist: click (>=8.1.0,<9.0.0)
20
20
  Requires-Dist: httpx (>=0.28.0,<0.29.0)
21
21
  Requires-Dist: pydantic (>=2.10.0,<3.0.0)
@@ -1,3 +1,3 @@
1
1
  """Doc-Serve CLI - Command-line interface for managing Doc-Serve server."""
2
2
 
3
- __version__ = "8.0.0"
3
+ __version__ = "9.2.0"
@@ -14,6 +14,7 @@ from .commands import (
14
14
  index_command,
15
15
  init_command,
16
16
  inject_command,
17
+ install_agent_command,
17
18
  jobs_command,
18
19
  list_command,
19
20
  query_command,
@@ -101,6 +102,7 @@ cli.add_command(folders_group, name="folders")
101
102
  cli.add_command(types_group, name="types")
102
103
  cli.add_command(cache_group, name="cache")
103
104
  cli.add_command(uninstall_command, name="uninstall")
105
+ cli.add_command(install_agent_command, name="install-agent")
104
106
 
105
107
 
106
108
  if __name__ == "__main__":
@@ -6,6 +6,7 @@ from .folders import folders_group
6
6
  from .index import index_command
7
7
  from .init import init_command
8
8
  from .inject import inject_command
9
+ from .install_agent import install_agent_command
9
10
  from .jobs import jobs_command
10
11
  from .list_cmd import list_command
11
12
  from .query import query_command
@@ -23,6 +24,7 @@ __all__ = [
23
24
  "index_command",
24
25
  "inject_command",
25
26
  "init_command",
27
+ "install_agent_command",
26
28
  "jobs_command",
27
29
  "list_command",
28
30
  "query_command",
@@ -22,7 +22,7 @@ def _find_config_file() -> Path | None:
22
22
  1. AGENT_BRAIN_CONFIG environment variable
23
23
  2. State directory config.yaml (if AGENT_BRAIN_STATE_DIR set)
24
24
  3. Current directory config.yaml
25
- 4. Walk up from CWD looking for .claude/agent-brain/config.yaml
25
+ 4. Walk up from CWD: .agent-brain/config.yaml (or legacy path)
26
26
  5. XDG config ~/.config/agent-brain/config.yaml (preferred)
27
27
  6. Legacy ~/.agent-brain/config.yaml (deprecated, prints warning)
28
28
 
@@ -48,13 +48,16 @@ def _find_config_file() -> Path | None:
48
48
  if cwd_config.exists():
49
49
  return cwd_config
50
50
 
51
- # 4. Walk up from CWD
51
+ # 4. Walk up from CWD looking for .agent-brain/ or legacy .claude/agent-brain/
52
52
  current = Path.cwd()
53
53
  root = Path(current.anchor)
54
54
  while current != root:
55
- claude_config = current / ".claude" / "agent-brain" / "config.yaml"
56
- if claude_config.exists():
57
- return claude_config
55
+ new_config = current / ".agent-brain" / "config.yaml"
56
+ if new_config.exists():
57
+ return new_config
58
+ legacy_config = current / ".claude" / "agent-brain" / "config.yaml"
59
+ if legacy_config.exists():
60
+ return legacy_config
58
61
  current = current.parent
59
62
 
60
63
  # 5. XDG config (checked before legacy per XDG standard)
@@ -7,6 +7,7 @@ import click
7
7
  from rich.console import Console
8
8
  from rich.panel import Panel
9
9
 
10
+ from agent_brain_cli.migration import migrate_state_dir
10
11
  from agent_brain_cli.xdg_paths import migrate_legacy_paths
11
12
 
12
13
  console = Console()
@@ -33,7 +34,7 @@ DEFAULT_CONFIG = {
33
34
  ],
34
35
  }
35
36
 
36
- STATE_DIR_NAME = ".claude/agent-brain"
37
+ STATE_DIR_NAME = ".agent-brain"
37
38
 
38
39
 
39
40
  def resolve_project_root(start_path: Path | None = None) -> Path:
@@ -72,6 +73,8 @@ def resolve_project_root(start_path: Path | None = None) -> Path:
72
73
  # Walk up looking for markers
73
74
  current = start
74
75
  while current != current.parent:
76
+ if (current / ".agent-brain").is_dir():
77
+ return current
75
78
  if (current / ".claude").is_dir():
76
79
  return current
77
80
  if (current / "pyproject.toml").is_file():
@@ -110,7 +113,7 @@ def resolve_project_root(start_path: Path | None = None) -> Path:
110
113
  "--state-dir",
111
114
  "-s",
112
115
  type=click.Path(file_okay=False, resolve_path=True),
113
- help="Custom state directory for index data (default: .claude/agent-brain)",
116
+ help="Custom state directory for index data (default: .agent-brain)",
114
117
  )
115
118
  def init_command(
116
119
  path: str | None,
@@ -122,7 +125,7 @@ def init_command(
122
125
  ) -> None:
123
126
  """Initialize a new Agent Brain project.
124
127
 
125
- Creates the .claude/agent-brain/ directory structure and writes
128
+ Creates the .agent-brain/ directory structure and writes
126
129
  a default config.json file.
127
130
 
128
131
  \b
@@ -147,7 +150,8 @@ def init_command(
147
150
  if state_dir:
148
151
  resolved_state_dir = Path(state_dir).resolve()
149
152
  else:
150
- resolved_state_dir = project_root / STATE_DIR_NAME
153
+ # Auto-migrate from legacy .claude/agent-brain if needed
154
+ resolved_state_dir = migrate_state_dir(project_root)
151
155
  config_path = resolved_state_dir / "config.json"
152
156
 
153
157
  # Check for existing configuration
@@ -0,0 +1,340 @@
1
+ """Install-agent command for installing runtime-specific plugin files."""
2
+
3
+ import json
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+ import click
8
+ from rich.console import Console
9
+ from rich.panel import Panel
10
+
11
+ from agent_brain_cli.runtime.claude_converter import ClaudeConverter
12
+ from agent_brain_cli.runtime.codex_converter import CodexConverter
13
+ from agent_brain_cli.runtime.gemini_converter import GeminiConverter
14
+ from agent_brain_cli.runtime.opencode_converter import OpenCodeConverter
15
+ from agent_brain_cli.runtime.parser import parse_plugin_dir
16
+ from agent_brain_cli.runtime.skill_runtime_converter import SkillRuntimeConverter
17
+ from agent_brain_cli.runtime.types import Scope
18
+
19
+ console = Console()
20
+
21
+ # Default install directories per runtime and scope
22
+ INSTALL_DIRS: dict[str, dict[str, str]] = {
23
+ "claude": {
24
+ "project": ".claude/plugins/agent-brain",
25
+ "global": "~/.claude/plugins/agent-brain",
26
+ },
27
+ "opencode": {
28
+ "project": ".opencode/plugins/agent-brain",
29
+ "global": "~/.config/opencode/plugins/agent-brain",
30
+ },
31
+ "gemini": {
32
+ "project": ".gemini/plugins/agent-brain",
33
+ "global": "~/.config/gemini/plugins/agent-brain",
34
+ },
35
+ "codex": {
36
+ "project": ".codex/skills/agent-brain",
37
+ "global": "~/.codex/skills/agent-brain",
38
+ },
39
+ }
40
+
41
+ # Runtimes that require --dir (no default directory)
42
+ DIR_REQUIRED_RUNTIMES = {"skill-runtime"}
43
+
44
+ ConverterType = type[
45
+ ClaudeConverter
46
+ | OpenCodeConverter
47
+ | GeminiConverter
48
+ | SkillRuntimeConverter
49
+ | CodexConverter
50
+ ]
51
+
52
+ CONVERTERS: dict[str, ConverterType] = {
53
+ "claude": ClaudeConverter,
54
+ "opencode": OpenCodeConverter,
55
+ "gemini": GeminiConverter,
56
+ "skill-runtime": SkillRuntimeConverter,
57
+ "codex": CodexConverter,
58
+ }
59
+
60
+
61
+ def _find_plugin_dir() -> Path | None:
62
+ """Find the canonical plugin directory.
63
+
64
+ Searches for agent-brain-plugin in common locations.
65
+ """
66
+ # Check relative to this package (development layout)
67
+ pkg_dir = Path(__file__).parent.parent.parent.parent
68
+ candidate = pkg_dir / "agent-brain-plugin"
69
+ if candidate.is_dir() and (candidate / "commands").is_dir():
70
+ return candidate
71
+
72
+ # Check installed location
73
+ installed = Path.home() / ".claude" / "plugins" / "agent-brain"
74
+ if installed.is_dir() and (installed / "commands").is_dir():
75
+ return installed
76
+
77
+ return None
78
+
79
+
80
+ def _resolve_target_dir(
81
+ runtime: str,
82
+ scope: str,
83
+ project_root: Path | None = None,
84
+ custom_dir: str | None = None,
85
+ ) -> Path:
86
+ """Resolve the target installation directory."""
87
+ if custom_dir:
88
+ return Path(custom_dir).expanduser().resolve()
89
+ dir_template = INSTALL_DIRS[runtime][scope]
90
+ if scope == "global":
91
+ return Path(dir_template).expanduser()
92
+ if project_root is None:
93
+ project_root = Path.cwd()
94
+ return project_root / dir_template
95
+
96
+
97
+ RUNTIME_CHOICES = ["claude", "opencode", "gemini", "skill-runtime", "codex"]
98
+
99
+
100
+ @click.command("install-agent")
101
+ @click.option(
102
+ "--agent",
103
+ "-a",
104
+ required=True,
105
+ type=click.Choice(RUNTIME_CHOICES),
106
+ help="Target runtime to install for",
107
+ )
108
+ @click.option(
109
+ "--project",
110
+ "scope",
111
+ flag_value="project",
112
+ default=True,
113
+ help="Install to project directory (default)",
114
+ )
115
+ @click.option(
116
+ "--global",
117
+ "scope",
118
+ flag_value="global",
119
+ help="Install to user-level directory",
120
+ )
121
+ @click.option(
122
+ "--plugin-dir",
123
+ type=click.Path(exists=True, file_okay=False, resolve_path=True),
124
+ help="Custom canonical plugin source directory",
125
+ )
126
+ @click.option(
127
+ "--dir",
128
+ "target_dir_option",
129
+ type=click.Path(resolve_path=True),
130
+ help="Target skill directory (required for skill-runtime)",
131
+ )
132
+ @click.option(
133
+ "--dry-run",
134
+ is_flag=True,
135
+ help="List files that would be created without writing",
136
+ )
137
+ @click.option(
138
+ "--json",
139
+ "json_output",
140
+ is_flag=True,
141
+ help="Output as JSON",
142
+ )
143
+ @click.option(
144
+ "--path",
145
+ "-p",
146
+ type=click.Path(exists=True, file_okay=False, resolve_path=True),
147
+ help="Project path for --project scope (default: cwd)",
148
+ )
149
+ def install_agent_command(
150
+ agent: str,
151
+ scope: str,
152
+ plugin_dir: str | None,
153
+ target_dir_option: str | None,
154
+ dry_run: bool,
155
+ json_output: bool,
156
+ path: str | None,
157
+ ) -> None:
158
+ """Install Agent Brain plugin for a specific runtime.
159
+
160
+ Converts the canonical plugin format into the target runtime's
161
+ native format and installs it.
162
+
163
+ \b
164
+ Examples:
165
+ agent-brain install-agent --agent claude --project
166
+ agent-brain install-agent --agent opencode --global
167
+ agent-brain install-agent --agent gemini --dry-run
168
+ agent-brain install-agent --agent skill-runtime --dir ./my-skills
169
+ agent-brain install-agent --agent codex
170
+ """
171
+ try:
172
+ # Validate --dir requirement for skill-runtime
173
+ if agent in DIR_REQUIRED_RUNTIMES and not target_dir_option:
174
+ msg = (
175
+ f"--dir is required for --agent {agent}. "
176
+ "Specify the target skill directory."
177
+ )
178
+ if json_output:
179
+ click.echo(json.dumps({"error": msg}))
180
+ else:
181
+ console.print(f"[red]Error:[/] {msg}")
182
+ raise SystemExit(1)
183
+
184
+ # Resolve plugin source directory
185
+ source: Path
186
+ if plugin_dir:
187
+ source = Path(plugin_dir)
188
+ else:
189
+ found = _find_plugin_dir()
190
+ if found is None:
191
+ msg = (
192
+ "Could not find canonical plugin directory. "
193
+ "Use --plugin-dir to specify location."
194
+ )
195
+ if json_output:
196
+ click.echo(json.dumps({"error": msg}))
197
+ else:
198
+ console.print(f"[red]Error:[/] {msg}")
199
+ raise SystemExit(1)
200
+ source = found
201
+
202
+ # Parse the plugin
203
+ bundle = parse_plugin_dir(source)
204
+
205
+ if not json_output and not dry_run:
206
+ console.print(
207
+ f"[dim]Parsed {len(bundle.commands)} commands, "
208
+ f"{len(bundle.agents)} agents, "
209
+ f"{len(bundle.skills)} skills, "
210
+ f"{len(bundle.templates)} templates, "
211
+ f"{len(bundle.scripts)} scripts[/]"
212
+ )
213
+
214
+ # Resolve target directory
215
+ project_root = Path(path) if path else None
216
+ target = _resolve_target_dir(agent, scope, project_root, target_dir_option)
217
+
218
+ # Create converter
219
+ converter_cls = CONVERTERS[agent]
220
+ converter = converter_cls()
221
+ scope_enum = Scope.GLOBAL if scope == "global" else Scope.PROJECT
222
+
223
+ if dry_run:
224
+ _handle_dry_run(
225
+ converter,
226
+ bundle,
227
+ target,
228
+ scope_enum,
229
+ agent,
230
+ scope,
231
+ json_output,
232
+ )
233
+ return
234
+
235
+ # Actually install
236
+ if isinstance(converter, CodexConverter):
237
+ codex_root = Path(path) if path else Path.cwd()
238
+ files = converter.install(
239
+ bundle, target, scope_enum, project_root=codex_root
240
+ )
241
+ else:
242
+ files = converter.install(bundle, target, scope_enum)
243
+
244
+ if json_output:
245
+ result: dict[str, Any] = {
246
+ "status": "installed",
247
+ "agent": agent,
248
+ "scope": scope,
249
+ "target_dir": str(target),
250
+ "files_created": len(files),
251
+ "source_dir": str(source),
252
+ }
253
+ click.echo(json.dumps(result, indent=2))
254
+ else:
255
+ console.print(
256
+ Panel(
257
+ f"[green]Plugin installed successfully![/]\n\n"
258
+ f"[bold]Runtime:[/] {agent}\n"
259
+ f"[bold]Scope:[/] {scope}\n"
260
+ f"[bold]Target:[/] {target}\n"
261
+ f"[bold]Files:[/] {len(files)}",
262
+ title="Agent Brain Installed",
263
+ border_style="green",
264
+ )
265
+ )
266
+
267
+ except SystemExit:
268
+ raise
269
+ except Exception as exc:
270
+ if json_output:
271
+ click.echo(json.dumps({"error": str(exc)}))
272
+ else:
273
+ console.print(f"[red]Error:[/] {exc}")
274
+ raise SystemExit(1) from exc
275
+
276
+
277
+ def _handle_dry_run(
278
+ converter: (
279
+ ClaudeConverter
280
+ | OpenCodeConverter
281
+ | GeminiConverter
282
+ | SkillRuntimeConverter
283
+ | CodexConverter
284
+ ),
285
+ bundle: Any,
286
+ target: Path,
287
+ scope_enum: Scope,
288
+ agent: str,
289
+ scope: str,
290
+ json_output: bool,
291
+ ) -> None:
292
+ """Handle dry-run mode: simulate install in temp dir."""
293
+ import tempfile
294
+
295
+ with tempfile.TemporaryDirectory() as tmp:
296
+ tmp_target = Path(tmp)
297
+ # For Codex, pass tmp as project_root so AGENTS.md lands in tmpdir
298
+ if isinstance(converter, CodexConverter):
299
+ files = converter.install(
300
+ bundle, tmp_target, scope_enum, project_root=Path(tmp)
301
+ )
302
+ else:
303
+ files = converter.install(bundle, tmp_target, scope_enum)
304
+ # Remap paths to real target
305
+ planned: list[Path] = []
306
+ for f in files:
307
+ try:
308
+ planned.append(target / f.relative_to(tmp_target))
309
+ except ValueError:
310
+ # AGENTS.md may be at project_root, not under target
311
+ planned.append(f)
312
+
313
+ if json_output:
314
+ click.echo(
315
+ json.dumps(
316
+ {
317
+ "dry_run": True,
318
+ "agent": agent,
319
+ "scope": scope,
320
+ "target_dir": str(target),
321
+ "files": [str(f) for f in planned],
322
+ "file_count": len(planned),
323
+ },
324
+ indent=2,
325
+ )
326
+ )
327
+ else:
328
+ console.print(
329
+ Panel(
330
+ f"[yellow]Dry run[/] — no files written\n\n"
331
+ f"[bold]Runtime:[/] {agent}\n"
332
+ f"[bold]Scope:[/] {scope}\n"
333
+ f"[bold]Target:[/] {target}\n"
334
+ f"[bold]Files:[/] {len(planned)}",
335
+ title="Install Preview",
336
+ border_style="yellow",
337
+ )
338
+ )
339
+ for f in planned:
340
+ console.print(f" [dim]{f}[/]")
@@ -15,11 +15,12 @@ import click
15
15
  from rich.console import Console
16
16
  from rich.panel import Panel
17
17
 
18
+ from agent_brain_cli.migration import resolve_state_dir_with_fallback
18
19
  from agent_brain_cli.xdg_paths import get_xdg_state_dir, migrate_legacy_paths
19
20
 
20
21
  console = Console()
21
22
 
22
- STATE_DIR_NAME = ".claude/agent-brain"
23
+ STATE_DIR_NAME = ".agent-brain"
23
24
  LOCK_FILE = "agent-brain.lock"
24
25
  PID_FILE = "agent-brain.pid"
25
26
  RUNTIME_FILE = "runtime.json"
@@ -46,6 +47,8 @@ def resolve_project_root(start_path: Path | None = None) -> Path:
46
47
  # Walk up looking for markers
47
48
  current = start
48
49
  while current != current.parent:
50
+ if (current / ".agent-brain").is_dir():
51
+ return current
49
52
  if (current / ".claude").is_dir():
50
53
  return current
51
54
  if (current / "pyproject.toml").is_file():
@@ -234,7 +237,7 @@ def start_command(
234
237
  else:
235
238
  project_root = resolve_project_root()
236
239
 
237
- state_dir = project_root / STATE_DIR_NAME
240
+ state_dir = resolve_state_dir_with_fallback(project_root)
238
241
 
239
242
  # Check if initialized
240
243
  if not state_dir.exists():
@@ -12,11 +12,12 @@ from urllib.request import Request, urlopen
12
12
  import click
13
13
  from rich.console import Console
14
14
 
15
+ from agent_brain_cli.migration import resolve_state_dir_with_fallback
15
16
  from agent_brain_cli.xdg_paths import get_registry_path
16
17
 
17
18
  console = Console()
18
19
 
19
- STATE_DIR_NAME = ".claude/agent-brain"
20
+ STATE_DIR_NAME = ".agent-brain"
20
21
  LOCK_FILE = "agent-brain.lock"
21
22
  PID_FILE = "agent-brain.pid"
22
23
  RUNTIME_FILE = "runtime.json"
@@ -43,6 +44,8 @@ def resolve_project_root(start_path: Path | None = None) -> Path:
43
44
  # Walk up looking for markers
44
45
  current = start
45
46
  while current != current.parent:
47
+ if (current / ".agent-brain").is_dir():
48
+ return current
46
49
  if (current / ".claude").is_dir():
47
50
  return current
48
51
  if (current / "pyproject.toml").is_file():
@@ -181,7 +184,7 @@ def stop_command(
181
184
  else:
182
185
  project_root = resolve_project_root()
183
186
 
184
- state_dir = project_root / STATE_DIR_NAME
187
+ state_dir = resolve_state_dir_with_fallback(project_root)
185
188
 
186
189
  # Check if state directory exists
187
190
  if not state_dir.exists():
@@ -102,7 +102,7 @@ def uninstall_command(yes: bool, json_output: bool) -> None:
102
102
  - ~/.local/state/agent-brain/ (XDG state directory)
103
103
  - ~/.agent-brain/ (legacy directory, if present)
104
104
 
105
- Does NOT remove project-level .claude/agent-brain/ directories.
105
+ Does NOT remove project-level .agent-brain/ directories.
106
106
 
107
107
  \b
108
108
  Examples:
@@ -18,7 +18,8 @@ from agent_brain_cli.xdg_paths import get_xdg_config_dir
18
18
  logger = logging.getLogger(__name__)
19
19
 
20
20
  # Default state directory name within project root
21
- STATE_DIR_NAME = ".claude/agent-brain"
21
+ STATE_DIR_NAME = ".agent-brain"
22
+ LEGACY_STATE_DIR_NAME = ".claude/agent-brain"
22
23
 
23
24
 
24
25
  class ServerConfig(BaseModel):
@@ -47,7 +48,7 @@ class ProjectConfig(BaseModel):
47
48
 
48
49
  state_dir: str | None = Field(
49
50
  default=None,
50
- description="Custom state directory path (default: .claude/agent-brain)",
51
+ description="Custom state directory path (default: .agent-brain)",
51
52
  )
52
53
  project_root: str | None = Field(
53
54
  default=None,
@@ -120,7 +121,7 @@ def _find_config_file(start_path: Path | None = None) -> Path | None:
120
121
  Search order:
121
122
  1. AGENT_BRAIN_CONFIG environment variable
122
123
  2. Current directory: agent-brain.yaml or config.yaml
123
- 3. Project .claude/agent-brain/config.yaml
124
+ 3. Project .agent-brain/config.yaml (or legacy .claude/agent-brain/)
124
125
  4. User home: ~/.agent-brain/config.yaml
125
126
  5. User home: ~/.config/agent-brain/config.yaml (XDG)
126
127
 
@@ -148,14 +149,18 @@ def _find_config_file(start_path: Path | None = None) -> Path | None:
148
149
  logger.debug(f"Using config from current directory: {cwd_config}")
149
150
  return cwd_config
150
151
 
151
- # 3. Project .claude/agent-brain directory
152
- # Walk up looking for .claude directory
152
+ # 3. Project .agent-brain directory (or legacy .claude/agent-brain)
153
+ # Walk up looking for state directory
153
154
  current = start
154
155
  while current != current.parent:
155
- claude_config = current / ".claude" / "agent-brain" / "config.yaml"
156
- if claude_config.exists():
157
- logger.debug(f"Using config from project: {claude_config}")
158
- return claude_config
156
+ new_config = current / ".agent-brain" / "config.yaml"
157
+ if new_config.exists():
158
+ logger.debug(f"Using config from project: {new_config}")
159
+ return new_config
160
+ legacy_config = current / ".claude" / "agent-brain" / "config.yaml"
161
+ if legacy_config.exists():
162
+ logger.debug(f"Using config from project: {legacy_config}")
163
+ return legacy_config
159
164
  current = current.parent
160
165
 
161
166
  # 4. XDG config (checked before legacy per XDG standard)
@@ -281,6 +286,8 @@ def _find_project_root(start_path: Path | None = None) -> Path:
281
286
  # Walk up looking for markers
282
287
  current = start
283
288
  while current != current.parent:
289
+ if (current / ".agent-brain").is_dir():
290
+ return current
284
291
  if (current / ".claude").is_dir():
285
292
  return current
286
293
  if (current / "pyproject.toml").is_file():
@@ -297,10 +304,10 @@ def get_state_dir(
297
304
  """Get the resolved state directory path.
298
305
 
299
306
  Resolution order:
300
- 1. Detect project root and check for .claude/agent-brain/
307
+ 1. Detect project root and check for .agent-brain/ (or legacy .claude/agent-brain/)
301
308
  2. config.project.state_dir from config file
302
309
  3. AGENT_BRAIN_STATE_DIR environment variable (explicit override)
303
- 4. Default: {project_root}/.claude/agent-brain
310
+ 4. Default: {project_root}/.agent-brain
304
311
 
305
312
  Args:
306
313
  config: Optional pre-loaded config.
@@ -313,9 +320,14 @@ def get_state_dir(
313
320
  if project_root is None:
314
321
  project_root = _find_project_root()
315
322
 
316
- detected_state_dir = project_root / STATE_DIR_NAME
317
- if detected_state_dir.exists() and (detected_state_dir / "config.json").exists():
318
- return detected_state_dir
323
+ # Check new path first, then legacy
324
+ new_state_dir = project_root / STATE_DIR_NAME
325
+ if new_state_dir.exists() and (new_state_dir / "config.json").exists():
326
+ return new_state_dir
327
+
328
+ legacy_state_dir = project_root / LEGACY_STATE_DIR_NAME
329
+ if legacy_state_dir.exists() and (legacy_state_dir / "config.json").exists():
330
+ return legacy_state_dir
319
331
 
320
332
  # 2. Check config file setting
321
333
  if config is None:
@@ -329,7 +341,7 @@ def get_state_dir(
329
341
  if env_state_dir:
330
342
  return Path(env_state_dir).expanduser().resolve()
331
343
 
332
- # 4. Default: project_root/.claude/agent-brain
344
+ # 4. Default: project_root/.agent-brain
333
345
  return project_root / STATE_DIR_NAME
334
346
 
335
347