dotbrain 0.7.0__tar.gz → 0.8.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 (121) hide show
  1. {dotbrain-0.7.0 → dotbrain-0.8.0}/PKG-INFO +1 -1
  2. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/cli-reference.md +3 -2
  3. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/glossary.md +5 -3
  4. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/troubleshooting.md +3 -1
  5. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/wiring.md +4 -6
  6. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/zh/cli-reference.md +2 -2
  7. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/zh/glossary.md +4 -2
  8. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/zh/troubleshooting.md +2 -1
  9. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/zh/wiring.md +3 -5
  10. {dotbrain-0.7.0 → dotbrain-0.8.0}/pyproject.toml +1 -1
  11. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/__init__.py +1 -1
  12. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/_cli_reference.py +3 -1
  13. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/adopter_repos.py +2 -2
  14. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/asset_cli.py +1 -1
  15. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/brainspaces.py +5 -31
  16. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/cli.py +1 -2
  17. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/doctor.py +11 -4
  18. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/paths.py +3 -0
  19. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/presentation.py +11 -9
  20. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/projects.py +1 -1
  21. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/workflows.py +31 -52
  22. {dotbrain-0.7.0 → dotbrain-0.8.0}/.gitignore +0 -0
  23. {dotbrain-0.7.0 → dotbrain-0.8.0}/LICENSE +0 -0
  24. {dotbrain-0.7.0 → dotbrain-0.8.0}/README.md +0 -0
  25. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/.vitepress/config.mts +0 -0
  26. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/.vitepress/theme/Mermaid.vue +0 -0
  27. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/.vitepress/theme/index.ts +0 -0
  28. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/.vitepress/theme/style.css +0 -0
  29. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/AGENTS.md +0 -0
  30. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/CLAUDE.md +0 -0
  31. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/README.md +0 -0
  32. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/agent-team.md +0 -0
  33. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/architecture.md +0 -0
  34. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/beads-backend.md +0 -0
  35. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/brain-site-configuration.md +0 -0
  36. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/brain-site.md +0 -0
  37. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/configuration.md +0 -0
  38. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/getting-started.md +0 -0
  39. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/index.md +0 -0
  40. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/learning.md +0 -0
  41. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/package-lock.json +0 -0
  42. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/package.json +0 -0
  43. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/prompts.md +0 -0
  44. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/public/assets/apple-touch-icon-light.png +0 -0
  45. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/public/assets/banner-dark.png +0 -0
  46. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/public/assets/banner-light.png +0 -0
  47. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/public/assets/favicon-dark-32.png +0 -0
  48. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/public/assets/favicon-light-32.png +0 -0
  49. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/public/assets/how-it-works-dark.png +0 -0
  50. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/public/assets/how-it-works-dark.svg +0 -0
  51. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/public/assets/how-it-works-light.png +0 -0
  52. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/public/assets/how-it-works-light.svg +0 -0
  53. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/public/assets/lockup-horizontal-dark-1024.png +0 -0
  54. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/public/assets/lockup-horizontal-light-1024.png +0 -0
  55. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/public/assets/mark-dark-512.png +0 -0
  56. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/public/assets/mark-dark.png +0 -0
  57. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/public/assets/mark-light-512.png +0 -0
  58. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/public/assets/mark-light.png +0 -0
  59. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/public/assets/workflow-dark.png +0 -0
  60. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/public/assets/workflow-dark.svg +0 -0
  61. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/public/assets/workflow-light.png +0 -0
  62. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/public/assets/workflow-light.svg +0 -0
  63. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/session-context.md +0 -0
  64. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/skills.md +0 -0
  65. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/why-dotbrain.md +0 -0
  66. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/workflow.md +0 -0
  67. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/zh/agent-team.md +0 -0
  68. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/zh/architecture.md +0 -0
  69. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/zh/beads-backend.md +0 -0
  70. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/zh/brain-site-configuration.md +0 -0
  71. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/zh/brain-site.md +0 -0
  72. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/zh/configuration.md +0 -0
  73. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/zh/getting-started.md +0 -0
  74. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/zh/index.md +0 -0
  75. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/zh/learning.md +0 -0
  76. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/zh/prompts.md +0 -0
  77. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/zh/session-context.md +0 -0
  78. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/zh/skills.md +0 -0
  79. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/zh/why-dotbrain.md +0 -0
  80. {dotbrain-0.7.0 → dotbrain-0.8.0}/docs/zh/workflow.md +0 -0
  81. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/assets.py +0 -0
  82. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/beads.py +0 -0
  83. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/bootstrap.py +0 -0
  84. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/config.py +0 -0
  85. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/hooks.py +0 -0
  86. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/migrate.py +0 -0
  87. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/options.py +0 -0
  88. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resource_loader.py +0 -0
  89. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/__init__.py +0 -0
  90. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/agents/codex/researcher.toml +0 -0
  91. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/agents/codex/reviewer.toml +0 -0
  92. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/agents/codex/verifier.toml +0 -0
  93. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/agents/codex/worker.toml +0 -0
  94. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/config.yaml +0 -0
  95. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/core.yaml +0 -0
  96. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/site/package-lock.json +0 -0
  97. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/site/package.json +0 -0
  98. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/site/vitepress/config.mts +0 -0
  99. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/site/vitepress/theme/DocsOverview.vue +0 -0
  100. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/site/vitepress/theme/HomeTiles.vue +0 -0
  101. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/site/vitepress/theme/LearnOverview.vue +0 -0
  102. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/site/vitepress/theme/Mermaid.vue +0 -0
  103. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/site/vitepress/theme/PageStatus.vue +0 -0
  104. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/site/vitepress/theme/base.ts +0 -0
  105. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/site/vitepress/theme/custom.css +0 -0
  106. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/site/vitepress/theme/empty.css +0 -0
  107. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/site/vitepress/theme/empty.ts +0 -0
  108. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/site/vitepress/theme/index.ts +0 -0
  109. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/templates/brain/AGENTS.md +0 -0
  110. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/templates/brain/DOTBRAIN.md +0 -0
  111. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/templates/brain/adr/README.md +0 -0
  112. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/templates/brain/designs/README.md +0 -0
  113. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/templates/brain/docs/README.md +0 -0
  114. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/templates/brain/project.yaml +0 -0
  115. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/templates/brain/site/configure.md +0 -0
  116. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/templates/brain/site/index.md +0 -0
  117. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/resources/templates/gitignore +0 -0
  118. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/results.py +0 -0
  119. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/site.py +0 -0
  120. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/skills.py +0 -0
  121. {dotbrain-0.7.0 → dotbrain-0.8.0}/src/dotbrain/subagents.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: dotbrain
3
- Version: 0.7.0
3
+ Version: 0.8.0
4
4
  Summary: Wires project Brainspaces and skills into coding agents.
5
5
  Project-URL: Homepage, https://github.com/arminzou/dotbrain
6
6
  Project-URL: Repository, https://github.com/arminzou/dotbrain
@@ -8,7 +8,9 @@ for catalogs and project inspection. Empty lists, previews, and skipped work are
8
8
  Doctor shows problems and warnings first; use `doctor -v` to include every healthy check.
9
9
  Runtime activation and session uncertainty are informational, shown with `doctor -v` and in
10
10
  JSON; they do not count as passed checks. Missing registered hook files and failed plugin
11
- checks remain warnings.
11
+ checks remain warnings. With `--all`, a project whose shared `.repo` checkout is absent on this
12
+ machine is reported as skipped, not failed; a missing `.repo.local` checkout or an explicit
13
+ `--project` still fails.
12
14
  Colors follow terminal capabilities; redirected output stays plain.
13
15
 
14
16
  Finite reports support `--json`: stdout contains one result with command, overall status,
@@ -94,7 +96,6 @@ dotbrain wire [OPTIONS]
94
96
  | --- | --- | --- |
95
97
  | `--repo` *path* | — | Checkout to attach; defaults to the current Git checkout. |
96
98
  | `--project` *text* | — | Select a named Brainspace. |
97
- | `--no-repo` | — | Create a Brain-only project; requires `--project`. |
98
99
  | `--skip-beads` | — | Create without a tracker. |
99
100
  | `--remote` *text* | — | Dolt remote for initial tracker creation. |
100
101
  | `--server-host` *text* | — | Dolt server host; defaults to config. |
@@ -33,9 +33,11 @@ agent-facing conventions. In dotbrain terms, the Brain is narrower than the full
33
33
 
34
34
  ### Brain-only project
35
35
 
36
- A project created without a code checkout, using `dotbrain wire --no-repo --project <name>`.
37
- It can still have an execution store. Add `--skip-beads` to create it without a tracker;
38
- tracker-free projects can also have wired checkouts.
36
+ A retired kind of project with no code checkout. `dotbrain wire --no-repo` no longer exists: every
37
+ project has a repository as its working home. That repository can be private and hold only
38
+ documents. To migrate a Brain-only Brainspace, run `dotbrain wire --project <name>` from its new
39
+ repository, or move the Brainspace out of `brainspaces/` to archive it. Add `--skip-beads` to
40
+ create a project without a tracker.
39
41
 
40
42
  ### ADR
41
43
 
@@ -94,7 +94,9 @@ Yes. Set `DOTBRAIN_HOME` to the directory you want.
94
94
 
95
95
  ### Can I use dotbrain without a code repo?
96
96
 
97
- Yes. `dotbrain wire --no-repo --project <project>` creates a Brain-only Brainspace.
97
+ Not anymore. Every project needs a repository as its working home, but it can be a private
98
+ repository that holds only documents. To move an older Brain-only project onto one, run
99
+ `dotbrain wire --project <project>` from that repository.
98
100
 
99
101
  ### How do I stop using dotbrain on a repo?
100
102
 
@@ -72,10 +72,6 @@ dotbrain wire
72
72
  dotbrain wire --repo ~/repos/my-app
73
73
  ```
74
74
 
75
- ```bash [Brain only]
76
- dotbrain wire --no-repo --project research
77
- ```
78
-
79
75
  :::
80
76
 
81
77
  `wire` creates or repairs:
@@ -146,8 +142,10 @@ tracker state. Named selection and `--all` use registered checkouts rather than
146
142
  ## Selection and reports
147
143
 
148
144
  Project commands default to the current wired checkout, including its subdirectories. Outside
149
- one, use `--project <name>`. Named selection respects `.repo.local` before `.repo`; Brain-only
150
- projects are valid for operations that do not need a checkout. Use `dotbrain projects list` and
145
+ one, use `--project <name>`. Named selection respects `.repo.local` before `.repo`. A Brainspace with
146
+ no registered checkout, such as an older Brain-only one, stays valid for operations that do not need
147
+ a checkout; `doctor` warns about it, and `--all` asset linking skips it. Run
148
+ `dotbrain wire --project <name>` from a repository to register one. Use `dotbrain projects list` and
151
149
  `dotbrain projects show` to inspect identities and resolved settings without remote probes.
152
150
 
153
151
  `--all` conflicts with `--project` and `--repo`. Applicable leaf commands accept `--home <path>`
@@ -5,7 +5,8 @@
5
5
  面向人的输出会按目标对改动和问题分组,把主目录路径缩写成 `~`,并用表格展示目录和项目信息。空列表、预览和
6
6
  被跳过的操作都会明确显示。doctor 先显示问题和警告;用 `doctor -v` 可以包括所有健康的检查项。运行时激活状态和
7
7
  会话的不确定性属于参考信息,只在 `doctor -v` 和 JSON 中显示,不算作通过的检查。缺失的已注册 hook 文件和失败的
8
- 插件检查仍然是警告。颜色会根据终端能力调整;重定向的输出保持纯文本。
8
+ 插件检查仍然是警告。使用 `--all` 时,如果项目共享的 `.repo` checkout 不在本机上,该项目显示为跳过而不是失败;
9
+ `.repo.local` 指向的 checkout 缺失,或用 `--project` 明确选择时,仍然报错。颜色会根据终端能力调整;重定向的输出保持纯文本。
9
10
 
10
11
  一次性输出结果的命令支持 `--json`:stdout 包含一个结果,里面有命令、总体状态、每个目标的结果和错误。
11
12
  问题的严重级别有 `info`、`warning` 和 `error`;只有 warning 时不算失败。退出码 `0` 表示成功,`1` 表示操作失败或
@@ -89,7 +90,6 @@ dotbrain wire [OPTIONS]
89
90
  | --- | --- | --- |
90
91
  | `--repo` *path* | — | 要连接的 checkout;默认是当前的 Git checkout。 |
91
92
  | `--project` *text* | — | 选择一个指定的 Brainspace。 |
92
- | `--no-repo` | — | 创建只有 Brain 的项目;需要 `--project`。 |
93
93
  | `--skip-beads` | — | 创建时不带任务跟踪。 |
94
94
  | `--remote` *text* | — | 首次创建任务跟踪时使用的 Dolt remote。 |
95
95
  | `--server-host` *text* | — | Dolt 服务器主机;默认取自配置。 |
@@ -31,8 +31,10 @@ Brainspace 小。
31
31
 
32
32
  ### Brain-only project
33
33
 
34
- 没有代码 checkout 的项目,用 `dotbrain wire --no-repo --project <name>` 创建。它仍然可以有执行存储。加上
35
- `--skip-beads` 可以创建一个没有任务跟踪的项目;没有任务跟踪的项目也可以连接 checkout。
34
+ 已经停用的项目类型,没有代码 checkout。`dotbrain wire --no-repo` 已经移除:每个项目都以一个仓库作为工作目录。
35
+ 这个仓库可以是私有的,只放文档。要迁移 Brain-only 的 Brainspace,在新仓库里运行
36
+ `dotbrain wire --project <name>`;或者把 Brainspace 移出 `brainspaces/` 归档。加上 `--skip-beads` 可以创建一个
37
+ 没有任务跟踪的项目。
36
38
 
37
39
  ### ADR
38
40
 
@@ -86,7 +86,8 @@ dotbrain wire --repo ~/repos/my-app
86
86
 
87
87
  ### 没有代码仓库能用 dotbrain 吗? {#can-i-use-dotbrain-without-a-code-repo}
88
88
 
89
- 可以。`dotbrain wire --no-repo --project <project>` 会创建一个只有 Brain 的 Brainspace。
89
+ 现在不行了。每个项目都需要一个仓库作为工作目录,不过它可以是只放文档的私有仓库。要把以前的 Brain-only 项目
90
+ 迁过去,在那个仓库里运行 `dotbrain wire --project <project>`。
90
91
 
91
92
  ### 怎么让某个仓库不再使用 dotbrain? {#how-do-i-stop-using-dotbrain-on-a-repo}
92
93
 
@@ -70,10 +70,6 @@ dotbrain wire
70
70
  dotbrain wire --repo ~/repos/my-app
71
71
  ```
72
72
 
73
- ```bash [只建 Brain]
74
- dotbrain wire --no-repo --project research
75
- ```
76
-
77
73
  :::
78
74
 
79
75
  `wire` 会创建或修复:
@@ -143,7 +139,9 @@ worktree 里运行不带参数的维护命令和资源链接命令,只影响
143
139
  ## 选择项目与结果输出 {#selection-and-reports}
144
140
 
145
141
  项目命令默认作用于当前已连接的 checkout,包括它的子目录。在它之外,请用 `--project <name>`。按名字选择时,
146
- `.repo.local` 优先于 `.repo`;对于不需要 checkout 的操作,只有 Brain 的项目也是有效的。用
142
+ `.repo.local` 优先于 `.repo`。没有注册 checkout 的 Brainspace(比如以前的 Brain-only 项目)仍然可以用于不需要
143
+ checkout 的操作;`doctor` 会给出警告,`--all` 链接资源时会跳过它。在仓库里运行 `dotbrain wire --project <name>`
144
+ 就能注册 checkout。用
147
145
  `dotbrain projects list` 和 `dotbrain projects show` 查看项目身份和解析后的设置,不会探测远程。
148
146
 
149
147
  `--all` 不能和 `--project`、`--repo` 一起用。适用的子命令都接受 `--home <path>` 和 `--json`。JSON 输出是
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dotbrain"
3
- version = "0.7.0"
3
+ version = "0.8.0"
4
4
  description = "Wires project Brainspaces and skills into coding agents."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -4,4 +4,4 @@ A testable Python CLI for wiring repos into Brainspaces, seeding Brains, and
4
4
  managing skills. Exposes the workflow commands plus pure path/contract helpers.
5
5
  """
6
6
 
7
- __version__ = "0.7.0"
7
+ __version__ = "0.8.0"
@@ -55,7 +55,9 @@ for catalogs and project inspection. Empty lists, previews, and skipped work are
55
55
  Doctor shows problems and warnings first; use `doctor -v` to include every healthy check.
56
56
  Runtime activation and session uncertainty are informational, shown with `doctor -v` and in
57
57
  JSON; they do not count as passed checks. Missing registered hook files and failed plugin
58
- checks remain warnings.
58
+ checks remain warnings. With `--all`, a project whose shared `.repo` checkout is absent on this
59
+ machine is reported as skipped, not failed; a missing `.repo.local` checkout or an explicit
60
+ `--project` still fails.
59
61
  Colors follow terminal capabilities; redirected output stays plain.
60
62
 
61
63
  Finite reports support `--json`: stdout contains one result with command, overall status,
@@ -123,7 +123,7 @@ def repo_for_brainspace(
123
123
  3. dotbrain_home itself when brainspace.name == "dotbrain"
124
124
  4. repo_base/<brainspace.name> when the directory exists
125
125
 
126
- A ``(brain-only)`` pointer resolves to ``None``: the Brainspace declares no adopter repo.
126
+ A retired ``(brain-only)`` pointer resolves to ``None``: it names no checkout.
127
127
  """
128
128
  for pointer_name in (".repo.local", ".repo"):
129
129
  pointer = brainspace / pointer_name
@@ -226,7 +226,7 @@ def repo_root(repo: Path | None, run: Runner = _default_run) -> Path:
226
226
  res = run(argv, cwd=cwd, check=True)
227
227
  except subprocess.CalledProcessError:
228
228
  loc = str(cwd) if cwd else "current directory"
229
- raise ValueError(f"{loc} is not inside a git repository; select --project <name> or use wire --no-repo --project <name>")
229
+ raise ValueError(f"{loc} is not inside a git repository; run from a Git checkout or select --project <name>")
230
230
  return Path((res.stdout or "").strip()).resolve()
231
231
 
232
232
 
@@ -35,7 +35,7 @@ def report(kind, operation, home, runtime, project, json_output, scope="project"
35
35
  result.targets.append(item)
36
36
  try:
37
37
  if target and target.checkout is None and all_projects:
38
- item.findings.append({"severity": "warning", "message": "Brain-only project: no checkout assets"})
38
+ item.findings.append({"severity": "warning", "message": paths.NO_CHECKOUT})
39
39
  continue
40
40
  linked = assets.link_global(root, kind, runtime) if target is None else assets.link_project(root, target, kind, runtime)
41
41
  item.changes = [f"delivered {name}" for name in linked.linked] + [f"pruned {name}" for name in linked.pruned]
@@ -94,35 +94,9 @@ def active_agent_workspaces(brainspace: Path, dotbrain_home: Path) -> tuple[str,
94
94
  return tuple(f".{agent}" for agent in agents if agent in _KNOWN_AGENT_WORKSPACES)
95
95
 
96
96
 
97
- def is_brain_only(brainspace: Path) -> bool:
98
- """True when the Brainspace declares no adopter repo."""
99
-
100
- repo_file = Path(brainspace) / ".repo"
101
- return (
102
- repo_file.is_file()
103
- and repo_file.read_text(encoding="utf-8").strip() == "(brain-only)"
104
- )
105
-
106
-
107
- def seed_agent_workspaces(brainspace: Path, dotbrain_home: Path, home: Path | None = None) -> list[str]:
108
- """Create declared agent workspaces, but only for a brain-only Brainspace.
109
-
110
- A repo-backed Brainspace has its workspaces materialized in the code repo, and skill and
111
- subagent links point from there straight at the dotbrain home. Seeding one here too would
112
- leave directories nothing reads. A brain-only Brainspace has no repo, so it is the only
113
- place its links can live.
114
-
115
- Undeclared agents still warn either way: a typo in ``project.yaml`` should not be silent.
116
- """
97
+ def unknown_agent_warnings(brainspace: Path, dotbrain_home: Path) -> list[str]:
98
+ """Warn on undeclared agents: a typo in ``project.yaml`` should not be silent."""
117
99
  brainspace = Path(brainspace)
118
- warnings: list[str] = []
119
- brain_only = is_brain_only(brainspace)
120
-
121
- for agent in config.load_project_agents(dotbrain_home, brainspace.name):
122
- if agent not in _KNOWN_AGENT_WORKSPACES:
123
- warnings.append(f"ignored unknown agent workspace in {brainspace / '.brain' / 'project.yaml'}: {agent}")
124
- continue
125
- if brain_only:
126
- (brainspace / f".{agent}").mkdir(parents=True, exist_ok=True)
127
-
128
- return warnings
100
+ return [f"ignored unknown agent workspace in {brainspace / '.brain' / 'project.yaml'}: {agent}"
101
+ for agent in config.load_project_agents(dotbrain_home, brainspace.name)
102
+ if agent not in _KNOWN_AGENT_WORKSPACES]
@@ -166,7 +166,6 @@ def doctor(home: HomeOption = None,
166
166
  def wire(
167
167
  repo: Optional[Path] = typer.Option(None, '--repo', help='Checkout to attach; defaults to the current Git checkout.'),
168
168
  project: Optional[str] = typer.Option(None, '--project', help='Select a named Brainspace.'),
169
- no_repo: bool = typer.Option(False, '--no-repo', help='Create a Brain-only project; requires --project.'),
170
169
  skip_beads: bool = typer.Option(False, '--skip-beads', help='Create without a tracker.'),
171
170
  remote: str = typer.Option('', '--remote', help='Dolt remote for initial tracker creation.'),
172
171
  server_host: Optional[str] = typer.Option(None, '--server-host', help='Dolt server host; defaults to config.'),
@@ -183,7 +182,7 @@ def wire(
183
182
  if remote and selected_host:
184
183
  raise ValueError('--remote and --server-host are mutually exclusive')
185
184
  with activity("Wiring project...", enabled=not json_output):
186
- result = workflows.wire_project(dotbrain_home=root, repo=repo, project=project, no_repo=no_repo,
185
+ result = workflows.wire_project(dotbrain_home=root, repo=repo, project=project,
187
186
  run_beads=not skip_beads, remote=remote, server_host=selected_host,
188
187
  server_port=cfg.port if server_port is None else server_port,
189
188
  server_user=cfg.user if server_user is None else server_user, database=database)
@@ -245,7 +245,7 @@ def _check_beads_state(brainspace: Path, name: str, dotbrain_home: Path, *, run:
245
245
  return findings
246
246
 
247
247
 
248
- def _check_project(target: projects.ProjectTarget, root: Path, run: Runner) -> list[Finding]:
248
+ def _check_project(target: projects.ProjectTarget, root: Path, run: Runner, *, skip_absent: bool = False) -> list[Finding]:
249
249
  findings = []
250
250
  beads = config.load_project_config(root, target.project)
251
251
  workspaces = assets.project_workspaces(root, target, "all")
@@ -255,7 +255,12 @@ def _check_project(target: projects.ProjectTarget, root: Path, run: Runner) -> l
255
255
  findings.append(Finding("error", "Brain directory missing", "run dotbrain refresh"))
256
256
  repo = target.checkout
257
257
  if repo is None:
258
- findings.append(Finding("ok", "Brain-only project: no checkout assets"))
258
+ findings.append(Finding("warn", paths.NO_CHECKOUT,
259
+ f"run dotbrain wire --project {target.project} from its repository, or archive the Brainspace"))
260
+ elif not repo.is_dir() and skip_absent and not (target.brainspace / ".repo.local").is_file():
261
+ # A shared .repo may name a checkout that lives only on another machine.
262
+ findings.append(Finding("skip", "checkout not on this machine; checkout checks skipped",
263
+ "add .repo.local to check a checkout here"))
259
264
  elif not repo.is_dir():
260
265
  findings.append(Finding("error", f"registered checkout missing: {repo}", "correct .repo.local or .repo"))
261
266
  else:
@@ -304,7 +309,7 @@ def run_doctor(dotbrain_home: Path, home: Path | None = None, *, project: str |
304
309
  for target in targets:
305
310
  report.checkouts[target.project] = str(target.checkout) if target.checkout else None
306
311
  try:
307
- report.projects[target.project] = _check_project(target, root, run)
312
+ report.projects[target.project] = _check_project(target, root, run, skip_absent=all_projects)
308
313
  except DIAGNOSIS_ERRORS as exc:
309
314
  report.projects[target.project] = [Finding("error", f"project diagnosis failed: {exc}", "correct the declaration or inaccessible paths")]
310
315
  return report
@@ -317,9 +322,11 @@ def as_result(report: DoctorReport) -> CommandResult:
317
322
  item = TargetResult(project=name, checkout=report.checkouts.get(name), scope=scope)
318
323
  for finding in findings:
319
324
  message = finding.message + (f"; {finding.suggestion}" if finding.suggestion else "")
320
- item.findings.append({"severity": {"ok": "info", "info": "info", "warn": "warning", "error": "error"}[finding.status], "message": message})
325
+ item.findings.append({"severity": {"ok": "info", "info": "info", "skip": "info", "warn": "warning", "error": "error"}[finding.status], "message": message})
321
326
  if any(f.status == "error" for f in findings):
322
327
  item.status = "failure"
328
+ elif any(f.status == "skip" for f in findings):
329
+ item.status = "skipped"
323
330
  result.targets.append(item)
324
331
  failures = sum(item.status == "failure" for item in result.targets)
325
332
  if failures:
@@ -21,6 +21,9 @@ DATA_DIRS: tuple[str, ...] = ("brainspaces", "projects")
21
21
  # Matching anchored entries written to an adopter repo's .git/info/exclude.
22
22
  EXCLUDE_ENTRIES: tuple[str, ...] = ("/.brain", "/.beads")
23
23
 
24
+ # Reported for a Brainspace with no registered checkout, such as a retired Brain-only one.
25
+ NO_CHECKOUT = "no registered checkout"
26
+
24
27
  # Breadcrumb appended to an adopter repo's real AGENTS.md / CLAUDE.md.
25
28
  ADOPTER_POINTER: str = (
26
29
  "@.brain/CLAUDE.md\n"
@@ -13,6 +13,7 @@ from rich.padding import Padding
13
13
  from rich.table import Table
14
14
  from rich.text import Text
15
15
 
16
+ from dotbrain import paths
16
17
  from dotbrain.adopter_repos import abbrev_home
17
18
 
18
19
  HEADING_STYLE = "bold green"
@@ -149,7 +150,7 @@ def _messages(output: Console, messages: list[str], severity: str, checkout: str
149
150
 
150
151
 
151
152
  def _human_status(target) -> str:
152
- if target.status == "success" and any(f["message"] == "Brain-only project: no checkout assets" for f in target.findings):
153
+ if target.status == "success" and any(f["message"] == paths.NO_CHECKOUT for f in target.findings):
153
154
  return "skipped"
154
155
  return target.status
155
156
 
@@ -176,7 +177,7 @@ def render_operation(result, *, preview: bool = False) -> None:
176
177
  if target.checkout:
177
178
  label += " · " + abbrev_home(Path(target.checkout))
178
179
  elif target.project:
179
- label += " · Brain-only"
180
+ label += " · no checkout"
180
181
  line(output, label, NAME_STYLE)
181
182
  _messages(output, target.errors, "error", target.checkout)
182
183
  for finding in target.findings:
@@ -258,7 +259,7 @@ def render_catalog(result) -> None:
258
259
 
259
260
 
260
261
  def _wiring(record) -> str:
261
- if record["brain_only"]:
262
+ if record["checkout"] is None:
262
263
  return "No checkout"
263
264
  problems = [f"{name} {status}" for name, status in record["wiring"].items() if status != "healthy"]
264
265
  return ", ".join(problems) or "Healthy"
@@ -276,8 +277,8 @@ def render_projects(result) -> None:
276
277
  for record in records:
277
278
  failure = "error" in record
278
279
  wiring = "Declaration error" if failure else _wiring(record)
279
- rows.append([record["project"], abbrev_home(Path(record["checkout"])) if record["checkout"] else "Brain-only",
280
- text(wiring, output, "red" if failure or wiring not in {"Healthy", "No checkout"} else "green"),
280
+ rows.append([record["project"], abbrev_home(Path(record["checkout"])) if record["checkout"] else "-",
281
+ text(wiring, output, "red" if failure or wiring != "Healthy" else "green"),
281
282
  *([] if shared_runtime else ["unknown" if failure else _runtimes(record["runtimes"])]),
282
283
  *([] if shared_tracker else ["unknown" if failure else record["beads"]["mode"]])])
283
284
  if rows:
@@ -299,7 +300,7 @@ def render_projects(result) -> None:
299
300
  record = target.data
300
301
  output.print(Text.assemble(text("Project: ", output, HEADING_STYLE),
301
302
  text(target.project, output, NAME_STYLE)))
302
- table(output, ["Property", "Value"], [["Checkout", record["checkout"] or "Brain-only"],
303
+ table(output, ["Property", "Value"], [["Checkout", record["checkout"] or "none"],
303
304
  ["Runtimes", _runtimes(record["runtimes"])], ["Tracker", record["beads"]["mode"]], ["Wiring", _wiring(record)]])
304
305
  line(output, "Paths", HEADING_STYLE)
305
306
  table(output, ["Path", "Location"], [["Brainspace", record["brainspace"]],
@@ -327,15 +328,16 @@ def render_doctor(report, *, verbose: bool = False) -> None:
327
328
  def counted(count, noun):
328
329
  return f"{count} {noun}{'' if count == 1 else 's'}"
329
330
  passed = counted(counts["ok"], "check") + " passed"
330
- line(output, counted(counts["error"], "problem") + " · " + counted(counts["warn"], "warning") + " · " + passed, "dim")
331
- statuses = [("error", "red"), ("warn", "yellow")] + ([("ok", "green"), ("info", "dim")] if verbose else [])
331
+ skipped = f" · {counts['skip']} skipped" if counts["skip"] else ""
332
+ line(output, counted(counts["error"], "problem") + " · " + counted(counts["warn"], "warning") + " · " + passed + skipped, "dim")
333
+ statuses = [("error", "red"), ("warn", "yellow"), ("skip", "dim")] + ([("ok", "green"), ("info", "dim")] if verbose else [])
332
334
  for status, style in statuses:
333
335
  for name, checkout, findings in groups:
334
336
  selected = [finding for finding in findings if finding.status == status]
335
337
  if not selected:
336
338
  continue
337
339
  line(output)
338
- marker = symbol(output, False) if status == "error" else "!" if status == "warn" else "i" if status == "info" else symbol(output, True)
340
+ marker = symbol(output, False) if status == "error" else "!" if status == "warn" else "-" if status == "skip" else "i" if status == "info" else symbol(output, True)
339
341
  line(output, f"{marker} {name}" + (" · " + abbrev_home(Path(checkout)) if checkout else ""), "bold " + style)
340
342
  combined = {}
341
343
  for finding in selected:
@@ -88,7 +88,7 @@ def inspect_project(home: Path, target: ProjectTarget, *, expand_assets: bool =
88
88
  return {
89
89
  "project": target.project, "brainspace": str(target.brainspace),
90
90
  "checkout": str(target.checkout) if target.checkout is not None else None,
91
- "brain_only": target.checkout is None, "runtimes": list(runtimes),
91
+ "runtimes": list(runtimes),
92
92
  "paths": {"brain": str(paths.confined_path(target.brainspace, ".brain")),
93
93
  "execution": str(paths.confined_path(target.brainspace, ".beads"))
94
94
  if beads.mode != "none" else None,
@@ -94,7 +94,6 @@ def wire_project(
94
94
  dotbrain_home: Path,
95
95
  repo: Path | None = None,
96
96
  project: str | None = None,
97
- no_repo: bool = False,
98
97
  run_beads: bool = True,
99
98
  remote: str = "",
100
99
  server_host: str = "",
@@ -109,23 +108,15 @@ def wire_project(
109
108
  if not (dotbrain_home / ".git").exists():
110
109
  raise RuntimeError(f"{dotbrain_home} is not a dotbrain git checkout")
111
110
 
112
- resolved_repo: Path | None
113
- if no_repo:
114
- if repo is not None:
115
- raise ValueError("--no-repo cannot be combined with --repo")
116
- if not project:
117
- raise ValueError("--no-repo requires --project <name>")
118
- resolved_repo = None
119
- else:
120
- resolved_repo = adopter_repos.repo_root(repo, run)
121
- adopter_repos.ensure_not_wired_to_foreign_dotbrain(resolved_repo, dotbrain_home)
122
- if (resolved_repo / ".brain").is_symlink():
123
- wired = project_selection._wired(dotbrain_home, resolved_repo)
124
- if project is not None and project != wired.project:
125
- raise ValueError("--project does not match this checkout's Brainspace")
126
- project = wired.project
127
-
128
- parent = adopter_repos.linked_worktree_parent(resolved_repo, run) if resolved_repo else None
111
+ resolved_repo = adopter_repos.repo_root(repo, run)
112
+ adopter_repos.ensure_not_wired_to_foreign_dotbrain(resolved_repo, dotbrain_home)
113
+ if (resolved_repo / ".brain").is_symlink():
114
+ wired = project_selection._wired(dotbrain_home, resolved_repo)
115
+ if project is not None and project != wired.project:
116
+ raise ValueError("--project does not match this checkout's Brainspace")
117
+ project = wired.project
118
+
119
+ parent = adopter_repos.linked_worktree_parent(resolved_repo, run)
129
120
  if parent is not None:
130
121
  main = project_selection.select_projects(dotbrain_home, repo=parent, run=run)[0]
131
122
  if project is not None and project != main.project:
@@ -137,15 +128,14 @@ def wire_project(
137
128
  # Resolve identity and every managed destination before even root housekeeping:
138
129
  # rejecting a selector must not seed a phantom project or rewrite registration.
139
130
  paths.brainspace_link_targets(dotbrain_home, project)
140
- for relative in (".repo", ".repo.local", ".brain/project.yaml", ".claude", ".codex"):
131
+ for relative in (".repo", ".repo.local", ".brain/project.yaml"):
141
132
  paths.confined_path(brainspace, relative)
142
133
  paths.confined_path(dotbrain_home, ".gitignore")
143
- if resolved_repo is not None:
144
- adopter_repos.ensure_wiring_matches(resolved_repo, dotbrain_home, project)
145
- for name in (".brain", ".beads"):
146
- entry = resolved_repo / name
147
- if entry.exists() and not entry.is_symlink():
148
- raise ValueError(f"{entry}: a user-owned entry conflicts with attachment")
134
+ adopter_repos.ensure_wiring_matches(resolved_repo, dotbrain_home, project)
135
+ for name in (".brain", ".beads"):
136
+ entry = resolved_repo / name
137
+ if entry.exists() and not entry.is_symlink():
138
+ raise ValueError(f"{entry}: a user-owned entry conflicts with attachment")
149
139
  existing = brainspace.is_dir()
150
140
  if existing:
151
141
  legacy = brainspace / ".brain/agents/skills.yaml"
@@ -168,44 +158,37 @@ def wire_project(
168
158
  rel = paths.data_dir(dotbrain_home).name
169
159
  bootstrap.ensure_root_gitignore(dotbrain_home)
170
160
  brainspace.mkdir(parents=True, exist_ok=True)
171
- if no_repo and not existing:
172
- (brainspace / ".repo").write_text(
173
- "(brain-only)\n", encoding="utf-8", newline="\n"
174
- )
175
- elif not existing:
176
- assert resolved_repo is not None
161
+ if not existing:
177
162
  (brainspace / ".repo").write_text(
178
163
  f"{adopter_repos.abbrev_home(resolved_repo, home)}\n",
179
164
  encoding="utf-8",
180
165
  newline="\n",
181
166
  )
182
- elif parent is None and resolved_repo is not None and adopter_repos.repo_for_brainspace(brainspace, dotbrain_home) is None:
167
+ elif parent is None and adopter_repos.repo_for_brainspace(brainspace, dotbrain_home) is None:
168
+ # A Brainspace without a usable pointer, including a retired Brain-only one, adopts this checkout.
183
169
  pointer = brainspace / (".repo.local" if (brainspace / ".repo.local").exists() else ".repo")
184
170
  paths.confined_path(brainspace, pointer.name)
185
171
  pointer.write_text(f"{adopter_repos.abbrev_home(resolved_repo, home)}\n", encoding="utf-8", newline="\n")
186
172
 
187
173
  result = WireResult(brainspace=brainspace, repo=resolved_repo, project=project)
188
- missing_links = [name for name in paths.BRAINSPACE_LINKS
189
- if resolved_repo is not None and not (resolved_repo / name).is_symlink()]
174
+ missing_links = [name for name in paths.BRAINSPACE_LINKS if not (resolved_repo / name).is_symlink()]
190
175
  changed_workspaces = [name for name in brainspaces.active_agent_workspaces(brainspace, dotbrain_home)
191
- if resolved_repo is not None and
192
- ((resolved_repo / name).is_symlink() or not (resolved_repo / name).exists())]
176
+ if (resolved_repo / name).is_symlink() or not (resolved_repo / name).exists()]
193
177
 
194
178
  if parent is None:
195
179
  result.logs += [f"shared Brain: updated {relative}" for relative in brainspaces.seed_brain(brainspace, dotbrain_home)]
196
180
  active_workspaces = brainspaces.active_agent_workspaces(brainspace, dotbrain_home)
197
- result.warnings += brainspaces.seed_agent_workspaces(brainspace, dotbrain_home, home)
198
- if resolved_repo is not None:
199
- workspace_dirs, workspace_warnings = project_workspace_dirs(brainspace, resolved_repo, active_workspaces, run)
200
- result.errors += workspace_warnings
201
- result.logs += [f"materialized {name}" for name in changed_workspaces if name in workspace_dirs]
202
- target = project_selection.ProjectTarget(project, brainspace, resolved_repo)
203
- if not result.errors:
204
- for kind in ("skills", "agents"):
205
- linked = assets.link_project(dotbrain_home, target, kind, run=run)
206
- result.errors += linked.warnings
207
- result.logs += [f"linked {entry}" for entry in linked.linked]
208
- result.logs += [f"pruned {entry}" for entry in linked.pruned]
181
+ result.warnings += brainspaces.unknown_agent_warnings(brainspace, dotbrain_home)
182
+ workspace_dirs, workspace_warnings = project_workspace_dirs(brainspace, resolved_repo, active_workspaces, run)
183
+ result.errors += workspace_warnings
184
+ result.logs += [f"materialized {name}" for name in changed_workspaces if name in workspace_dirs]
185
+ target = project_selection.ProjectTarget(project, brainspace, resolved_repo)
186
+ if not result.errors:
187
+ for kind in ("skills", "agents"):
188
+ linked = assets.link_project(dotbrain_home, target, kind, run=run)
189
+ result.errors += linked.warnings
190
+ result.logs += [f"linked {entry}" for entry in linked.linked]
191
+ result.logs += [f"pruned {entry}" for entry in linked.pruned]
209
192
  if existing and run_beads and parent is None:
210
193
  synced = beads.pull_beads_for_all(dotbrain_home, projects=[project], run=run)
211
194
  result.logs += _tracker_changes(synced)
@@ -237,10 +220,6 @@ def wire_project(
237
220
  if not existing:
238
221
  result.logs.append(f"created Brainspace at {rel}/{project}")
239
222
 
240
- if no_repo:
241
- return result
242
-
243
- assert resolved_repo is not None
244
223
  result.warnings += adopter_repos.wire_repo(
245
224
  resolved_repo,
246
225
  brainspace,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes