brainclaw 1.9.0 → 1.9.1

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 (91) hide show
  1. package/README.md +585 -499
  2. package/dist/brainclaw-vscode.vsix +0 -0
  3. package/dist/commands/harvest.js +1 -1
  4. package/dist/commands/hooks.js +73 -73
  5. package/dist/commands/init.js +1 -1
  6. package/dist/commands/install-hooks.js +78 -78
  7. package/dist/commands/mcp-read-handlers.js +57 -14
  8. package/dist/commands/mcp.js +79 -13
  9. package/dist/commands/switch.js +26 -5
  10. package/dist/commands/version.js +1 -1
  11. package/dist/core/agent-capability.js +19 -4
  12. package/dist/core/agent-files.js +119 -119
  13. package/dist/core/codev-prompts.js +38 -38
  14. package/dist/core/default-profiles/doctor.yaml +11 -11
  15. package/dist/core/default-profiles/janitor.yaml +11 -11
  16. package/dist/core/default-profiles/onboarder.yaml +11 -11
  17. package/dist/core/default-profiles/reviewer.yaml +13 -13
  18. package/dist/core/dispatcher.js +1 -1
  19. package/dist/core/entity-operations.js +29 -3
  20. package/dist/core/execution.js +1 -1
  21. package/dist/core/loops/verbs.js +0 -1
  22. package/dist/core/messaging.js +2 -2
  23. package/dist/core/protocol-skills.js +164 -164
  24. package/dist/core/runtime-signals.js +1 -1
  25. package/dist/core/search.js +19 -2
  26. package/dist/core/security-guard.js +207 -207
  27. package/dist/core/spawn-check.js +16 -2
  28. package/dist/core/staleness.js +1 -1
  29. package/dist/core/store-resolution.js +26 -7
  30. package/dist/core/worktree.js +18 -18
  31. package/dist/facts.js +3 -3
  32. package/dist/facts.json +2 -2
  33. package/docs/PROTOCOL.md +1 -1
  34. package/docs/adapters/openclaw.md +43 -43
  35. package/docs/architecture/project-refs.md +328 -328
  36. package/docs/cli.md +2093 -2093
  37. package/docs/concepts/coordination.md +52 -52
  38. package/docs/concepts/coordinator-runbook.md +129 -129
  39. package/docs/concepts/dispatch-lifecycle.md +245 -245
  40. package/docs/concepts/event-log-store.md +928 -928
  41. package/docs/concepts/ideation-loop.md +317 -317
  42. package/docs/concepts/loop-engine.md +520 -511
  43. package/docs/concepts/mcp-governance.md +268 -268
  44. package/docs/concepts/memory.md +84 -84
  45. package/docs/concepts/multi-agent-workflows.md +167 -167
  46. package/docs/concepts/observer-protocol.md +361 -361
  47. package/docs/concepts/plans-and-claims.md +217 -217
  48. package/docs/concepts/project-md-convention.md +35 -35
  49. package/docs/concepts/runtime-notes.md +38 -38
  50. package/docs/concepts/troubleshooting.md +254 -254
  51. package/docs/concepts/workspace-bootstrapping.md +142 -142
  52. package/docs/context-format-changelog.md +35 -35
  53. package/docs/context-format.md +48 -48
  54. package/docs/index.md +65 -65
  55. package/docs/integrations/agents.md +158 -158
  56. package/docs/integrations/claude-code.md +23 -23
  57. package/docs/integrations/cline.md +77 -77
  58. package/docs/integrations/continue.md +55 -55
  59. package/docs/integrations/copilot.md +68 -68
  60. package/docs/integrations/cursor.md +23 -23
  61. package/docs/integrations/kilocode.md +72 -72
  62. package/docs/integrations/mcp.md +377 -377
  63. package/docs/integrations/mistral-vibe.md +122 -122
  64. package/docs/integrations/openclaw.md +92 -92
  65. package/docs/integrations/opencode.md +84 -84
  66. package/docs/integrations/overview.md +115 -115
  67. package/docs/integrations/roo.md +71 -71
  68. package/docs/integrations/windsurf.md +77 -77
  69. package/docs/mcp-schema-changelog.md +360 -356
  70. package/docs/playbooks/integration/index.md +121 -121
  71. package/docs/playbooks/orchestration.md +37 -0
  72. package/docs/playbooks/productivity/index.md +99 -99
  73. package/docs/playbooks/team/index.md +117 -117
  74. package/docs/product/agent-first-model.md +184 -184
  75. package/docs/product/entity-model-audit.md +462 -462
  76. package/docs/product/positioning.md +86 -86
  77. package/docs/quickstart-existing-project.md +107 -107
  78. package/docs/quickstart.md +183 -183
  79. package/docs/release-maintenance.md +79 -79
  80. package/docs/reputation.md +52 -52
  81. package/docs/review.md +45 -45
  82. package/docs/security.md +212 -212
  83. package/docs/server-operations.md +118 -118
  84. package/docs/storage.md +106 -106
  85. package/package.json +80 -65
  86. package/docs/concepts/event-log-store-critique-A.md +0 -333
  87. package/docs/concepts/event-log-store-critique-B.md +0 -353
  88. package/docs/concepts/event-log-store-phase0-measurements.md +0 -58
  89. package/docs/concepts/event-log-store-proposal-A.md +0 -365
  90. package/docs/concepts/event-log-store-proposal-B.md +0 -404
  91. package/docs/concepts/identity-model-proposal.md +0 -371
@@ -12,216 +12,216 @@
12
12
  * a config change, no regeneration required.
13
13
  */
14
14
  export function generateBashGuard(brainclawBin) {
15
- return `#!/usr/bin/env bash
16
- # brainclaw-guard — preinstall security gate
17
- # Generated by: brainclaw setup-security
18
- # Do not edit manually — regenerate with brainclaw setup-security
19
-
20
- BRAINCLAW_BIN="${brainclawBin}"
21
- ORIGINAL_CMD="\${BRAINCLAW_GUARD_ORIGINAL_CMD:-npm}"
22
-
23
- is_install_command() {
24
- for arg in "$@"; do
25
- case "$arg" in
26
- install|add|i) return 0 ;;
27
- -*) continue ;;
28
- *) break ;;
29
- esac
30
- done
31
- return 1
32
- }
33
-
34
- is_local_or_url() {
35
- case "$1" in
36
- .|..|./*|../*|/*|*:/*|git+*|git@*|*.tgz|*.tar.gz|*.whl) return 0 ;;
37
- esac
38
- case "$1" in
39
- [A-Za-z]:[\\\\/]*) return 0 ;;
40
- esac
41
- return 1
42
- }
43
-
44
- extract_packages() {
45
- local packages=""
46
- local req_file=""
47
- local skip_next=false
48
- local past_command=false
49
- for arg in "$@"; do
50
- if [ "$skip_next" = true ]; then
51
- # The flag's value lands here; capture it for -r/--requirement.
52
- case "$prev_flag" in
53
- -r|--requirement) req_file="$arg" ;;
54
- esac
55
- skip_next=false
56
- prev_flag=""
57
- continue
58
- fi
59
- case "$arg" in
60
- install|add|i)
61
- past_command=true
62
- continue
63
- ;;
64
- --save-dev|--save-peer|--save-optional|--save-exact|--save-prod|-D|-P|-O|-E|-S|-g|--global|--no-save|--user|--upgrade|-U|--break-system-packages|--no-deps|--pre|--force-reinstall|--ignore-installed)
65
- continue
66
- ;;
67
- --registry|--prefix|--tag|--registry-url|--cache|--index-url|--extra-index-url|--find-links|--proxy|--cert|--client-cert|--trusted-host|--target|--root|--python|--platform|--abi|--implementation)
68
- prev_flag="$arg"
69
- skip_next=true
70
- continue
71
- ;;
72
- -r|--requirement|-e|--editable|-c|--constraint)
73
- prev_flag="$arg"
74
- skip_next=true
75
- continue
76
- ;;
77
- -*)
78
- continue
79
- ;;
80
- *)
81
- if [ "$past_command" = true ]; then
82
- if is_local_or_url "$arg"; then
83
- continue
84
- fi
85
- if [ -n "$packages" ]; then
86
- packages="$packages,$arg"
87
- else
88
- packages="$arg"
89
- fi
90
- fi
91
- ;;
92
- esac
93
- done
94
- printf '%s\\t%s' "$packages" "$req_file"
95
- }
96
-
97
- # Main logic
98
- if is_install_command "$@"; then
99
- extracted=$(extract_packages "$@")
100
- packages="\${extracted%% *}"
101
- req_file="\${extracted##* }"
102
-
103
- if [ -n "$packages" ] || [ -n "$req_file" ]; then
104
- ecosystem="npm"
105
- if [ "$ORIGINAL_CMD" = "pip" ] || [ "$ORIGINAL_CMD" = "pip3" ]; then
106
- ecosystem="pypi"
107
- fi
108
-
109
- args=(check-security --ecosystem "$ecosystem" --json)
110
- if [ -n "$packages" ]; then args+=(--packages "$packages"); fi
111
- if [ -n "$req_file" ]; then args+=(--requirements "$req_file"); fi
112
-
113
- "$BRAINCLAW_BIN" "\${args[@]}" >/dev/null 2>&1
114
- exit_code=$?
115
-
116
- if [ $exit_code -eq 2 ]; then
117
- echo "[brainclaw-guard] BLOCKED — supply chain risk detected (enforced mode)" >&2
118
- details_pkgs="\${packages:-$req_file}"
119
- echo "[brainclaw-guard] Details: $BRAINCLAW_BIN check-security --ecosystem $ecosystem --packages \\"$details_pkgs\\"" >&2
120
- exit 1
121
- elif [ $exit_code -eq 1 ]; then
122
- echo "[brainclaw-guard] WARNING — potential supply chain risk (advisory or warn-threshold verdict)" >&2
123
- details_pkgs="\${packages:-$req_file}"
124
- echo "[brainclaw-guard] Details: $BRAINCLAW_BIN check-security --ecosystem $ecosystem --packages \\"$details_pkgs\\"" >&2
125
- fi
126
- # exit_code 0 = pass, continue silently
127
- fi
128
- fi
129
-
130
- exec "$ORIGINAL_CMD" "$@"
15
+ return `#!/usr/bin/env bash
16
+ # brainclaw-guard — preinstall security gate
17
+ # Generated by: brainclaw setup-security
18
+ # Do not edit manually — regenerate with brainclaw setup-security
19
+
20
+ BRAINCLAW_BIN="${brainclawBin}"
21
+ ORIGINAL_CMD="\${BRAINCLAW_GUARD_ORIGINAL_CMD:-npm}"
22
+
23
+ is_install_command() {
24
+ for arg in "$@"; do
25
+ case "$arg" in
26
+ install|add|i) return 0 ;;
27
+ -*) continue ;;
28
+ *) break ;;
29
+ esac
30
+ done
31
+ return 1
32
+ }
33
+
34
+ is_local_or_url() {
35
+ case "$1" in
36
+ .|..|./*|../*|/*|*:/*|git+*|git@*|*.tgz|*.tar.gz|*.whl) return 0 ;;
37
+ esac
38
+ case "$1" in
39
+ [A-Za-z]:[\\\\/]*) return 0 ;;
40
+ esac
41
+ return 1
42
+ }
43
+
44
+ extract_packages() {
45
+ local packages=""
46
+ local req_file=""
47
+ local skip_next=false
48
+ local past_command=false
49
+ for arg in "$@"; do
50
+ if [ "$skip_next" = true ]; then
51
+ # The flag's value lands here; capture it for -r/--requirement.
52
+ case "$prev_flag" in
53
+ -r|--requirement) req_file="$arg" ;;
54
+ esac
55
+ skip_next=false
56
+ prev_flag=""
57
+ continue
58
+ fi
59
+ case "$arg" in
60
+ install|add|i)
61
+ past_command=true
62
+ continue
63
+ ;;
64
+ --save-dev|--save-peer|--save-optional|--save-exact|--save-prod|-D|-P|-O|-E|-S|-g|--global|--no-save|--user|--upgrade|-U|--break-system-packages|--no-deps|--pre|--force-reinstall|--ignore-installed)
65
+ continue
66
+ ;;
67
+ --registry|--prefix|--tag|--registry-url|--cache|--index-url|--extra-index-url|--find-links|--proxy|--cert|--client-cert|--trusted-host|--target|--root|--python|--platform|--abi|--implementation)
68
+ prev_flag="$arg"
69
+ skip_next=true
70
+ continue
71
+ ;;
72
+ -r|--requirement|-e|--editable|-c|--constraint)
73
+ prev_flag="$arg"
74
+ skip_next=true
75
+ continue
76
+ ;;
77
+ -*)
78
+ continue
79
+ ;;
80
+ *)
81
+ if [ "$past_command" = true ]; then
82
+ if is_local_or_url "$arg"; then
83
+ continue
84
+ fi
85
+ if [ -n "$packages" ]; then
86
+ packages="$packages,$arg"
87
+ else
88
+ packages="$arg"
89
+ fi
90
+ fi
91
+ ;;
92
+ esac
93
+ done
94
+ printf '%s\\t%s' "$packages" "$req_file"
95
+ }
96
+
97
+ # Main logic
98
+ if is_install_command "$@"; then
99
+ extracted=$(extract_packages "$@")
100
+ packages="\${extracted%% *}"
101
+ req_file="\${extracted##* }"
102
+
103
+ if [ -n "$packages" ] || [ -n "$req_file" ]; then
104
+ ecosystem="npm"
105
+ if [ "$ORIGINAL_CMD" = "pip" ] || [ "$ORIGINAL_CMD" = "pip3" ]; then
106
+ ecosystem="pypi"
107
+ fi
108
+
109
+ args=(check-security --ecosystem "$ecosystem" --json)
110
+ if [ -n "$packages" ]; then args+=(--packages "$packages"); fi
111
+ if [ -n "$req_file" ]; then args+=(--requirements "$req_file"); fi
112
+
113
+ "$BRAINCLAW_BIN" "\${args[@]}" >/dev/null 2>&1
114
+ exit_code=$?
115
+
116
+ if [ $exit_code -eq 2 ]; then
117
+ echo "[brainclaw-guard] BLOCKED — supply chain risk detected (enforced mode)" >&2
118
+ details_pkgs="\${packages:-$req_file}"
119
+ echo "[brainclaw-guard] Details: $BRAINCLAW_BIN check-security --ecosystem $ecosystem --packages \\"$details_pkgs\\"" >&2
120
+ exit 1
121
+ elif [ $exit_code -eq 1 ]; then
122
+ echo "[brainclaw-guard] WARNING — potential supply chain risk (advisory or warn-threshold verdict)" >&2
123
+ details_pkgs="\${packages:-$req_file}"
124
+ echo "[brainclaw-guard] Details: $BRAINCLAW_BIN check-security --ecosystem $ecosystem --packages \\"$details_pkgs\\"" >&2
125
+ fi
126
+ # exit_code 0 = pass, continue silently
127
+ fi
128
+ fi
129
+
130
+ exec "$ORIGINAL_CMD" "$@"
131
131
  `;
132
132
  }
133
133
  export function generatePowerShellGuard(brainclawBin) {
134
- return `# brainclaw-guard — preinstall security gate (PowerShell)
135
- # Generated by: brainclaw setup-security
136
- # Do not edit manually — regenerate with brainclaw setup-security
137
-
138
- param([Parameter(ValueFromRemainingArguments)]$Args)
139
-
140
- $BrainclawBin = "${brainclawBin}"
141
- $OriginalCmd = if ($env:BRAINCLAW_GUARD_ORIGINAL_CMD) { $env:BRAINCLAW_GUARD_ORIGINAL_CMD } else { "npm" }
142
-
143
- function Is-InstallCommand($arguments) {
144
- foreach ($arg in $arguments) {
145
- if ($arg -match "^(install|add|i)$") { return $true }
146
- if ($arg -notmatch "^-") { break }
147
- }
148
- return $false
149
- }
150
-
151
- function Is-LocalOrUrl($arg) {
152
- if ($arg -eq "." -or $arg -eq "..") { return $true }
153
- if ($arg -match "^(\\./|\\.\\./|/)") { return $true }
154
- if ($arg -match "^[A-Za-z]:[\\\\/]") { return $true }
155
- if ($arg -match "^[a-z]+://") { return $true }
156
- if ($arg -match "^(git\\+|git@)") { return $true }
157
- if ($arg -match "\\.(tgz|tar\\.gz|whl)$") { return $true }
158
- return $false
159
- }
160
-
161
- function Extract-Packages($arguments) {
162
- $packages = @()
163
- $reqFile = ""
164
- $pastCommand = $false
165
- $skipNext = $false
166
- $prevFlag = ""
167
- foreach ($arg in $arguments) {
168
- if ($skipNext) {
169
- if ($prevFlag -match "^(-r|--requirement)$") { $reqFile = $arg }
170
- $skipNext = $false
171
- $prevFlag = ""
172
- continue
173
- }
174
- if ($arg -match "^(install|add|i)$") { $pastCommand = $true; continue }
175
- if ($arg -match "^(--save-dev|--save-peer|--save-optional|--save-exact|--save-prod|-D|-P|-O|-E|-S|-g|--global|--no-save|--user|--upgrade|-U|--break-system-packages|--no-deps|--pre|--force-reinstall|--ignore-installed)$") { continue }
176
- if ($arg -match "^(--registry|--prefix|--tag|--registry-url|--cache|--index-url|--extra-index-url|--find-links|--proxy|--cert|--client-cert|--trusted-host|--target|--root|--python|--platform|--abi|--implementation)$") {
177
- $prevFlag = $arg; $skipNext = $true; continue
178
- }
179
- if ($arg -match "^(-r|--requirement|-e|--editable|-c|--constraint)$") {
180
- $prevFlag = $arg; $skipNext = $true; continue
181
- }
182
- if ($arg -match "^-") { continue }
183
- if ($pastCommand) {
184
- if (Is-LocalOrUrl $arg) { continue }
185
- $packages += $arg
186
- }
187
- }
188
- return [PSCustomObject]@{ Packages = ($packages -join ","); RequirementsFile = $reqFile }
189
- }
190
-
191
- if (Is-InstallCommand $Args) {
192
- $extracted = Extract-Packages $Args
193
- $packages = $extracted.Packages
194
- $reqFile = $extracted.RequirementsFile
195
-
196
- if ($packages -or $reqFile) {
197
- $ecosystem = "npm"
198
- if ($OriginalCmd -match "^pip") { $ecosystem = "pypi" }
199
-
200
- $cliArgs = @("check-security", "--ecosystem", $ecosystem, "--json")
201
- if ($packages) { $cliArgs += @("--packages", $packages) }
202
- if ($reqFile) { $cliArgs += @("--requirements", $reqFile) }
203
-
204
- try {
205
- & $BrainclawBin @cliArgs *> $null
206
- $exitCode = $LASTEXITCODE
207
- } catch {
208
- $exitCode = 0
209
- }
210
-
211
- $detailsPkgs = if ($packages) { $packages } else { $reqFile }
212
- if ($exitCode -eq 2) {
213
- Write-Error "[brainclaw-guard] BLOCKED - supply chain risk detected (enforced mode)"
214
- Write-Error "[brainclaw-guard] Details: $BrainclawBin check-security --ecosystem $ecosystem --packages \`"$detailsPkgs\`""
215
- exit 1
216
- } elseif ($exitCode -eq 1) {
217
- Write-Warning "[brainclaw-guard] WARNING - potential supply chain risk (advisory or warn-threshold verdict)"
218
- Write-Warning "[brainclaw-guard] Details: $BrainclawBin check-security --ecosystem $ecosystem --packages \`"$detailsPkgs\`""
219
- }
220
- }
221
- }
222
-
223
- & $OriginalCmd @Args
224
- exit $LASTEXITCODE
134
+ return `# brainclaw-guard — preinstall security gate (PowerShell)
135
+ # Generated by: brainclaw setup-security
136
+ # Do not edit manually — regenerate with brainclaw setup-security
137
+
138
+ param([Parameter(ValueFromRemainingArguments)]$Args)
139
+
140
+ $BrainclawBin = "${brainclawBin}"
141
+ $OriginalCmd = if ($env:BRAINCLAW_GUARD_ORIGINAL_CMD) { $env:BRAINCLAW_GUARD_ORIGINAL_CMD } else { "npm" }
142
+
143
+ function Is-InstallCommand($arguments) {
144
+ foreach ($arg in $arguments) {
145
+ if ($arg -match "^(install|add|i)$") { return $true }
146
+ if ($arg -notmatch "^-") { break }
147
+ }
148
+ return $false
149
+ }
150
+
151
+ function Is-LocalOrUrl($arg) {
152
+ if ($arg -eq "." -or $arg -eq "..") { return $true }
153
+ if ($arg -match "^(\\./|\\.\\./|/)") { return $true }
154
+ if ($arg -match "^[A-Za-z]:[\\\\/]") { return $true }
155
+ if ($arg -match "^[a-z]+://") { return $true }
156
+ if ($arg -match "^(git\\+|git@)") { return $true }
157
+ if ($arg -match "\\.(tgz|tar\\.gz|whl)$") { return $true }
158
+ return $false
159
+ }
160
+
161
+ function Extract-Packages($arguments) {
162
+ $packages = @()
163
+ $reqFile = ""
164
+ $pastCommand = $false
165
+ $skipNext = $false
166
+ $prevFlag = ""
167
+ foreach ($arg in $arguments) {
168
+ if ($skipNext) {
169
+ if ($prevFlag -match "^(-r|--requirement)$") { $reqFile = $arg }
170
+ $skipNext = $false
171
+ $prevFlag = ""
172
+ continue
173
+ }
174
+ if ($arg -match "^(install|add|i)$") { $pastCommand = $true; continue }
175
+ if ($arg -match "^(--save-dev|--save-peer|--save-optional|--save-exact|--save-prod|-D|-P|-O|-E|-S|-g|--global|--no-save|--user|--upgrade|-U|--break-system-packages|--no-deps|--pre|--force-reinstall|--ignore-installed)$") { continue }
176
+ if ($arg -match "^(--registry|--prefix|--tag|--registry-url|--cache|--index-url|--extra-index-url|--find-links|--proxy|--cert|--client-cert|--trusted-host|--target|--root|--python|--platform|--abi|--implementation)$") {
177
+ $prevFlag = $arg; $skipNext = $true; continue
178
+ }
179
+ if ($arg -match "^(-r|--requirement|-e|--editable|-c|--constraint)$") {
180
+ $prevFlag = $arg; $skipNext = $true; continue
181
+ }
182
+ if ($arg -match "^-") { continue }
183
+ if ($pastCommand) {
184
+ if (Is-LocalOrUrl $arg) { continue }
185
+ $packages += $arg
186
+ }
187
+ }
188
+ return [PSCustomObject]@{ Packages = ($packages -join ","); RequirementsFile = $reqFile }
189
+ }
190
+
191
+ if (Is-InstallCommand $Args) {
192
+ $extracted = Extract-Packages $Args
193
+ $packages = $extracted.Packages
194
+ $reqFile = $extracted.RequirementsFile
195
+
196
+ if ($packages -or $reqFile) {
197
+ $ecosystem = "npm"
198
+ if ($OriginalCmd -match "^pip") { $ecosystem = "pypi" }
199
+
200
+ $cliArgs = @("check-security", "--ecosystem", $ecosystem, "--json")
201
+ if ($packages) { $cliArgs += @("--packages", $packages) }
202
+ if ($reqFile) { $cliArgs += @("--requirements", $reqFile) }
203
+
204
+ try {
205
+ & $BrainclawBin @cliArgs *> $null
206
+ $exitCode = $LASTEXITCODE
207
+ } catch {
208
+ $exitCode = 0
209
+ }
210
+
211
+ $detailsPkgs = if ($packages) { $packages } else { $reqFile }
212
+ if ($exitCode -eq 2) {
213
+ Write-Error "[brainclaw-guard] BLOCKED - supply chain risk detected (enforced mode)"
214
+ Write-Error "[brainclaw-guard] Details: $BrainclawBin check-security --ecosystem $ecosystem --packages \`"$detailsPkgs\`""
215
+ exit 1
216
+ } elseif ($exitCode -eq 1) {
217
+ Write-Warning "[brainclaw-guard] WARNING - potential supply chain risk (advisory or warn-threshold verdict)"
218
+ Write-Warning "[brainclaw-guard] Details: $BrainclawBin check-security --ecosystem $ecosystem --packages \`"$detailsPkgs\`""
219
+ }
220
+ }
221
+ }
222
+
223
+ & $OriginalCmd @Args
224
+ exit $LASTEXITCODE
225
225
  `;
226
226
  }
227
227
  export function generatePipBashGuard(brainclawBin) {
@@ -48,7 +48,14 @@ function initProbeGitRepo(root) {
48
48
  export async function checkAgentSpawn(agent, options = {}) {
49
49
  const start = Date.now();
50
50
  const profile = getCapabilityProfile(agent);
51
- if (!profile?.invoke_template || !profile?.invoke_binary || !profile.runtime.canBeSpawnedCli) {
51
+ if (!profile) {
52
+ // Distinct from no_template: the name didn't resolve to any profile at all
53
+ // (resolution is case-insensitive, so this is a genuine typo/unknown agent,
54
+ // not a casing slip). Reported separately so the pre-flight reason points at
55
+ // the spelling instead of the misleading "IDE-only?" template message.
56
+ return { agent, status: 'unknown_agent', delivered: false, completed: false, duration_ms: 0, detail: `unknown agent '${agent}' — not a registered brainclaw profile` };
57
+ }
58
+ if (!profile.invoke_template || !profile.invoke_binary || !profile.runtime.canBeSpawnedCli) {
52
59
  return { agent, status: 'no_template', delivered: false, completed: false, duration_ms: 0, detail: 'no CLI invoke template' };
53
60
  }
54
61
  const binary = resolveBinaryOnPath(profile.invoke_binary);
@@ -120,7 +127,7 @@ export async function runSpawnCheck(options = {}) {
120
127
  });
121
128
  }
122
129
  }
123
- const installed = entries.filter((e) => e.status !== 'not_installed' && e.status !== 'no_template');
130
+ const installed = entries.filter((e) => e.status !== 'not_installed' && e.status !== 'no_template' && e.status !== 'unknown_agent');
124
131
  const ok = installed.filter((e) => e.status === 'ok').length;
125
132
  const failures = installed.filter((e) => e.status === 'failed' || e.status === 'delivered_no_completion').length;
126
133
  const not_installed = entries.filter((e) => e.status === 'not_installed').length;
@@ -172,6 +179,13 @@ export function preflightResultFromEntry(entry) {
172
179
  if (entry.status === 'ok' || entry.status === 'delivered_no_completion') {
173
180
  return { agent, ok: true, status: entry.status, reason: entry.detail };
174
181
  }
182
+ if (entry.status === 'unknown_agent') {
183
+ return {
184
+ agent, ok: false, status: entry.status,
185
+ reason: `unknown agent '${agent}' — not a registered brainclaw profile (check spelling/case)`,
186
+ recommended_next_action: `Use a registered agent name (e.g. codex, claude-code, github-copilot). Names are case-insensitive — list installed agents with \`brainclaw doctor --spawn-check\`.`,
187
+ };
188
+ }
175
189
  if (entry.status === 'not_installed') {
176
190
  return {
177
191
  agent, ok: false, status: entry.status,
@@ -108,7 +108,7 @@ const VERIFIED_STALE_DAYS = 30;
108
108
  * pln#530 — flag perishable memories (traps that opted in by carrying a
109
109
  * `verify_cmd` and/or `verified_at`) whose last empirical verification is stale
110
110
  * or never happened, so an agent re-probes the live system instead of trusting a
111
- * value that may have drifted (the LeaseUp `service_tier` trap that the API later
111
+ * value that may have drifted (the `service_tier` trap that the API later
112
112
  * rejected is the motivating case). Only traps with these fields are considered —
113
113
  * durable facts are untouched.
114
114
  */
@@ -3,7 +3,7 @@ import os from 'node:os';
3
3
  import path from 'node:path';
4
4
  import { loadActiveProject } from './active-project.js';
5
5
  import { loadConfig } from './config.js';
6
- import { loadCurrentSession } from './identity.js';
6
+ import { loadCurrentSession, loadSessionById } from './identity.js';
7
7
  import { MEMORY_DIR } from './io.js';
8
8
  import { summarizeWorkspaceProjects } from './workspace-projects.js';
9
9
  /**
@@ -98,10 +98,18 @@ export function resolveTargetStore(cwd = process.cwd(), target = 'local', option
98
98
  * 6. Workspace anchor or process.cwd()
99
99
  */
100
100
  export function resolveEffectiveCwd(options = {}) {
101
+ return resolveEffectiveCwdInfo(options).cwd;
102
+ }
103
+ /**
104
+ * Resolve the effective cwd and explain which selector won. Use this for MCP
105
+ * facades that must echo their project scope to avoid silent cross-project reads.
106
+ */
107
+ export function resolveEffectiveCwdInfo(options = {}) {
101
108
  const baseCwd = path.resolve(options.baseCwd ?? process.cwd());
102
109
  // 1. Explicit --cwd flag
103
110
  if (options.explicitCwd) {
104
- return path.resolve(options.explicitCwd);
111
+ const cwd = path.resolve(options.explicitCwd);
112
+ return { cwd, active_source: 'explicit', resolved_project: projectInfo(cwd) };
105
113
  }
106
114
  // 2. BRAINCLAW_CWD env var — set by MCP configs to anchor resolution to the
107
115
  // workspace regardless of the IDE's process.cwd() at launch time. It is a
@@ -118,14 +126,16 @@ export function resolveEffectiveCwd(options = {}) {
118
126
  if (envProject) {
119
127
  const resolved = resolveProjectRef(envProject, anchorCwd, options.storeChainOptions);
120
128
  if (resolved)
121
- return resolved;
129
+ return { cwd: resolved, active_source: 'env_project', resolved_project: projectInfo(resolved) };
122
130
  }
123
131
  // 4. Session-scoped active project (per-agent, no cross-agent interference)
124
- const session = loadCurrentSession(anchorCwd);
132
+ const session = options.sessionId
133
+ ? loadSessionById(options.sessionId, anchorCwd)
134
+ : loadCurrentSession(anchorCwd);
125
135
  if (session?.active_project) {
126
136
  const sp = session.active_project;
127
137
  if (fs.existsSync(path.join(sp.path, MEMORY_DIR, 'config.yaml'))) {
128
- return sp.path;
138
+ return { cwd: sp.path, active_source: 'session', resolved_project: { path: sp.path, name: sp.name } };
129
139
  }
130
140
  }
131
141
  // 5. Global active-project.json from workspace root
@@ -133,11 +143,20 @@ export function resolveEffectiveCwd(options = {}) {
133
143
  if (wsRoot) {
134
144
  const active = loadActiveProject(wsRoot);
135
145
  if (active && fs.existsSync(path.join(active.path, MEMORY_DIR, 'config.yaml'))) {
136
- return active.path;
146
+ return { cwd: active.path, active_source: 'global', resolved_project: { path: active.path, name: active.name } };
137
147
  }
138
148
  }
139
149
  // 6. Default
140
- return anchorCwd;
150
+ return { cwd: anchorCwd, active_source: 'cwd', resolved_project: projectInfo(anchorCwd) };
151
+ }
152
+ function projectInfo(cwd) {
153
+ try {
154
+ const config = loadConfig(cwd);
155
+ return { path: cwd, name: config.project_name };
156
+ }
157
+ catch {
158
+ return { path: cwd };
159
+ }
141
160
  }
142
161
  /**
143
162
  * Find the workspace root (farthest store in the chain, or the one with
@@ -573,24 +573,24 @@ export const WORKTREE_HOOKS_DIRNAME = '.brainclaw-hooks';
573
573
  * blocking — a tooling gap must not trap a worker.
574
574
  */
575
575
  export function buildTypecheckPreCommitScript() {
576
- return `#!/bin/sh
577
- # brainclaw worktree typecheck gate (pln#479) — do not edit manually.
578
- # Blocks the commit when 'tsc --noEmit' fails. Bypass: git commit --no-verify.
579
- exec node -e "
580
- const fs = require('fs');
581
- const { execSync } = require('child_process');
582
- if (!fs.existsSync('tsconfig.json')) process.exit(0);
583
- if (!fs.existsSync('node_modules/typescript/bin/tsc')) {
584
- process.stderr.write('\\\\n[brainclaw] typecheck gate: typescript not found in worktree node_modules — skipping (commit allowed).\\\\n');
585
- process.exit(0);
586
- }
587
- try {
588
- execSync('node node_modules/typescript/bin/tsc --noEmit', { stdio: 'inherit' });
589
- } catch (e) {
590
- process.stderr.write('\\\\n[brainclaw] commit blocked: tsc --noEmit reported type errors (above). Fix them, or bypass with: git commit --no-verify\\\\n\\\\n');
591
- process.exit(1);
592
- }
593
- " 2>&1 || exit $?
576
+ return `#!/bin/sh
577
+ # brainclaw worktree typecheck gate (pln#479) — do not edit manually.
578
+ # Blocks the commit when 'tsc --noEmit' fails. Bypass: git commit --no-verify.
579
+ exec node -e "
580
+ const fs = require('fs');
581
+ const { execSync } = require('child_process');
582
+ if (!fs.existsSync('tsconfig.json')) process.exit(0);
583
+ if (!fs.existsSync('node_modules/typescript/bin/tsc')) {
584
+ process.stderr.write('\\\\n[brainclaw] typecheck gate: typescript not found in worktree node_modules — skipping (commit allowed).\\\\n');
585
+ process.exit(0);
586
+ }
587
+ try {
588
+ execSync('node node_modules/typescript/bin/tsc --noEmit', { stdio: 'inherit' });
589
+ } catch (e) {
590
+ process.stderr.write('\\\\n[brainclaw] commit blocked: tsc --noEmit reported type errors (above). Fix them, or bypass with: git commit --no-verify\\\\n\\\\n');
591
+ process.exit(1);
592
+ }
593
+ " 2>&1 || exit $?
594
594
  `;
595
595
  }
596
596
  /**
package/dist/facts.js CHANGED
@@ -1,8 +1,8 @@
1
1
  // Generated by scripts/emit-site-facts.mjs at build time. Do not edit manually.
2
- // Source: brainclaw v1.9.0 on 2026-06-15T14:49:43.510Z
2
+ // Source: brainclaw v1.9.1 on 2026-06-18T15:15:04.496Z
3
3
  export const FACTS = {
4
- "version": "1.9.0",
5
- "generated_at": "2026-06-15T14:49:43.510Z",
4
+ "version": "1.9.1",
5
+ "generated_at": "2026-06-18T15:15:04.496Z",
6
6
  "tools": {
7
7
  "count": 62,
8
8
  "published_count": 61,
package/dist/facts.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "1.9.0",
3
- "generated_at": "2026-06-15T14:49:43.510Z",
2
+ "version": "1.9.1",
3
+ "generated_at": "2026-06-18T15:15:04.496Z",
4
4
  "tools": {
5
5
  "count": 62,
6
6
  "published_count": 61,
package/docs/PROTOCOL.md CHANGED
@@ -219,5 +219,5 @@ integrators before declaring any of them stable:
219
219
  dependency) lower the adoption bar for OSS agents that don't speak MCP
220
220
  yet?
221
221
 
222
- Open an issue, ping `ai@thomgroup.com`, or comment on the brainclaw plan
222
+ Open an issue, ping `support@brainclaw.dev`, or comment on the brainclaw plan
223
223
  tracking this draft (pln#546).