brainclaw 0.19.11 → 0.19.14
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.
- package/README.md +19 -0
- package/dist/cli.js +44 -1
- package/dist/commands/claude-desktop-extension.js +18 -0
- package/dist/commands/doctor.js +12 -5
- package/dist/commands/env.js +7 -1
- package/dist/commands/init.js +15 -0
- package/dist/commands/list-surface-tasks.js +39 -0
- package/dist/commands/mcp.js +7 -1
- package/dist/commands/reconcile.js +138 -0
- package/dist/commands/setup.js +19 -0
- package/dist/commands/status.js +17 -12
- package/dist/commands/surface-task-resource.js +35 -0
- package/dist/commands/surface-task.js +57 -0
- package/dist/commands/update-surface-task.js +30 -0
- package/dist/commands/version.js +3 -1
- package/dist/commands/whoami.js +11 -1
- package/dist/core/agent-files.js +18 -18
- package/dist/core/ai-surface-inventory.js +321 -0
- package/dist/core/ai-surface-tasks.js +40 -0
- package/dist/core/brainclaw-version.js +303 -62
- package/dist/core/claude-desktop-extension.js +224 -0
- package/dist/core/ids.js +1 -0
- package/dist/core/io.js +1 -0
- package/dist/core/machine-profile.js +7 -1
- package/dist/core/migration.js +3 -1
- package/dist/core/schema.js +25 -0
- package/dist/core/workspace-projects.js +115 -0
- package/docs/cli.md +141 -1
- package/docs/integrations/mcp.md +11 -6
- package/docs/quickstart.md +40 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,6 +21,8 @@ The best setup is to tell your agent, in natural language, to install and initia
|
|
|
21
21
|
|
|
22
22
|
It sits alongside Copilot, Claude Code, Cursor, Codex, Windsurf, OpenCode, Antigravity/Gemini CLI and other coding agents. It does not replace them. It gives them a shared state layer they can resume from reliably across sessions.
|
|
23
23
|
|
|
24
|
+
brainclaw is also starting to model other local AI work surfaces on the same machine, such as ChatGPT Desktop, Claude Desktop, Claude Cowork, and Gemini web or CLI. That makes it possible to keep a project-level queue of non-code work for those surfaces, instead of treating every task as something the active coding agent must do itself.
|
|
25
|
+
|
|
24
26
|
---
|
|
25
27
|
|
|
26
28
|
## Why brainclaw exists
|
|
@@ -40,6 +42,8 @@ brainclaw solves this by making the repo itself agent-readable and agent-writeab
|
|
|
40
42
|
| **Coordination state** | shared plans, file claims, runtime notes, and board views for active work |
|
|
41
43
|
| **Agent-ready context** | compact, prompt-sized context built from real workspace state instead of stale instructions |
|
|
42
44
|
| **Native agent files** | auto-writes `CLAUDE.md`, `AGENTS.md`, `GEMINI.md`, `.cursor/rules/`, `.windsurfrules`, and similar local guidance |
|
|
45
|
+
| **Machine AI surface discovery** | detects local coding agents plus desktop AI work surfaces such as ChatGPT Desktop and Gemini CLI |
|
|
46
|
+
| **Queued surface tasks** | stores project-scoped requests for other local AI surfaces, such as visual generation, drafting, summaries, or research |
|
|
43
47
|
| **Local-first storage** | plain text + JSON, Git-friendly, no mandatory cloud, no telemetry by default |
|
|
44
48
|
|
|
45
49
|
---
|
|
@@ -129,6 +133,7 @@ After that, the agent should stay on Brainclaw's MCP path for live state:
|
|
|
129
133
|
|
|
130
134
|
```text
|
|
131
135
|
bclaw_session_start -> open session + return board/context
|
|
136
|
+
bclaw_get_execution_context -> inspect local tooling + notice Brainclaw package updates
|
|
132
137
|
bclaw_get_context -> fetch fresh prompt-ready context for a path
|
|
133
138
|
bclaw_list_plans -> inspect shared work
|
|
134
139
|
bclaw_claim -> claim scope before editing
|
|
@@ -145,6 +150,18 @@ brainclaw context --for src/auth/routes.ts --digest
|
|
|
145
150
|
brainclaw status
|
|
146
151
|
```
|
|
147
152
|
|
|
153
|
+
And if the current coding agent wants to hand off a non-code task to another local AI surface for later:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
brainclaw surface-task create "Generate homepage hero visual" \
|
|
157
|
+
--target chatgpt \
|
|
158
|
+
--kind visual_asset \
|
|
159
|
+
--instructions "Create a lightweight product hero visual for the landing page." \
|
|
160
|
+
--output assets/hero-home.png
|
|
161
|
+
|
|
162
|
+
brainclaw surface-task list
|
|
163
|
+
```
|
|
164
|
+
|
|
148
165
|
---
|
|
149
166
|
|
|
150
167
|
## Installation
|
|
@@ -177,6 +194,8 @@ If you want a machine-level CLI for operator workflows, debugging, or repeated l
|
|
|
177
194
|
npm install -g brainclaw
|
|
178
195
|
```
|
|
179
196
|
|
|
197
|
+
By default, Brainclaw's update checks for end-user installs follow the public npm `latest` channel. Projects that need a different track can override `brainclaw_update_source`, for example to use `prelaunch` or a local tarball channel.
|
|
198
|
+
|
|
180
199
|
If you are working from source while developing Brainclaw itself:
|
|
181
200
|
|
|
182
201
|
```bash
|
package/dist/cli.js
CHANGED
|
@@ -3,6 +3,7 @@ import { Command } from 'commander';
|
|
|
3
3
|
import { runInit } from './commands/init.js';
|
|
4
4
|
import { runSetup } from './commands/setup.js';
|
|
5
5
|
import { runUpgrade } from './commands/upgrade.js';
|
|
6
|
+
import { runReconcile } from './commands/reconcile.js';
|
|
6
7
|
import { getMemoryLog, rollbackMemory, hasMemoryRepo } from './core/memory-git.js';
|
|
7
8
|
import { buildMachineProfile, saveMachineProfile, loadMachineProfile, renderMachineProfileSummary } from './core/machine-profile.js';
|
|
8
9
|
import { buildAgentInventory, saveAgentInventory, loadAgentInventory, renderAgentInventorySummary } from './core/agent-inventory.js';
|
|
@@ -22,6 +23,7 @@ import { runCompleteStep } from './commands/complete-step.js';
|
|
|
22
23
|
import { runUpdateHandoff } from './commands/update-handoff.js';
|
|
23
24
|
import { runInstruction } from './commands/instruction.js';
|
|
24
25
|
import { runListAgents } from './commands/list-agents.js';
|
|
26
|
+
import { runSurfaceTaskResource } from './commands/surface-task-resource.js';
|
|
25
27
|
import { runListInstructions } from './commands/list-instructions.js';
|
|
26
28
|
import { runDoctor } from './commands/doctor.js';
|
|
27
29
|
import { runRebuild } from './commands/rebuild.js';
|
|
@@ -169,7 +171,7 @@ program
|
|
|
169
171
|
// --- machine-profile ---
|
|
170
172
|
program
|
|
171
173
|
.command('machine-profile')
|
|
172
|
-
.description('Detect and persist machine capabilities (OS, shells, git users, SSH keys, toolchains, WSL)')
|
|
174
|
+
.description('Detect and persist machine capabilities (OS, shells, git users, SSH keys, toolchains, WSL, AI surfaces)')
|
|
173
175
|
.option('--refresh', 'Force regeneration even if profile exists')
|
|
174
176
|
.option('--json', 'Output as JSON')
|
|
175
177
|
.action(async (options) => {
|
|
@@ -402,6 +404,28 @@ program
|
|
|
402
404
|
.action((id, options) => {
|
|
403
405
|
runUpdatePlan(id, { ...options, actualEffort: options.actualEffort });
|
|
404
406
|
});
|
|
407
|
+
// --- surface-task ---
|
|
408
|
+
program
|
|
409
|
+
.command('surface-task <subcommand> [args...]')
|
|
410
|
+
.description('Manage queued tasks for desktop AI surfaces such as ChatGPT Desktop or Claude Desktop')
|
|
411
|
+
.option('--json', 'Output as JSON for list')
|
|
412
|
+
.option('--all', 'Include completed, cancelled, and failed tasks in list')
|
|
413
|
+
.option('--status <status>', 'Status filter/update: queued, in_progress, completed, cancelled, failed')
|
|
414
|
+
.option('--target <surface>', 'Target surface, e.g. chatgpt, claude, gemini')
|
|
415
|
+
.option('--kind <kind>', 'Task kind: visual_asset, draft, summary, analysis, research, custom')
|
|
416
|
+
.option('--instructions <text>', 'Detailed instructions for the target surface')
|
|
417
|
+
.option('--output <paths...>', 'Expected output paths')
|
|
418
|
+
.option('--result <text>', 'Optional result note when updating a task')
|
|
419
|
+
.option('--tag <tags...>', 'Tags for this task')
|
|
420
|
+
.option('--path <paths...>', 'Related file paths')
|
|
421
|
+
.option('--agent <agent>', 'Author agent name')
|
|
422
|
+
.option('--agent-id <agentId>', 'Author agent id')
|
|
423
|
+
.action((subcommand, args, options) => {
|
|
424
|
+
runSurfaceTaskResource(subcommand, args, {
|
|
425
|
+
...options,
|
|
426
|
+
agentId: options.agentId,
|
|
427
|
+
});
|
|
428
|
+
});
|
|
405
429
|
// --- delete-plan ---
|
|
406
430
|
program
|
|
407
431
|
.command('delete-plan <id>')
|
|
@@ -915,6 +939,25 @@ program
|
|
|
915
939
|
.action((options) => {
|
|
916
940
|
runExport(options);
|
|
917
941
|
});
|
|
942
|
+
program
|
|
943
|
+
.command('reconcile')
|
|
944
|
+
.description('Refresh machine and workspace bootstrap state after updates or onboarding on complex installs')
|
|
945
|
+
.option('--json', 'Output as JSON')
|
|
946
|
+
.option('--dry-run', 'Preview the reconciliation plan without writing machine or bootstrap state')
|
|
947
|
+
.option('--apply-bootstrap', 'Apply bootstrap suggestions across all selected stores after refresh')
|
|
948
|
+
.option('-y, --yes', 'Skip confirmation prompts for multi-store bootstrap apply')
|
|
949
|
+
.option('--skip-machine-profile', 'Skip machine-profile refresh')
|
|
950
|
+
.option('--skip-agent-inventory', 'Skip agent-inventory refresh')
|
|
951
|
+
.action(async (options) => {
|
|
952
|
+
await runReconcile({
|
|
953
|
+
json: options.json,
|
|
954
|
+
dryRun: options.dryRun,
|
|
955
|
+
applyBootstrap: options.applyBootstrap,
|
|
956
|
+
yes: options.yes,
|
|
957
|
+
skipMachineProfile: options.skipMachineProfile,
|
|
958
|
+
skipAgentInventory: options.skipAgentInventory,
|
|
959
|
+
});
|
|
960
|
+
});
|
|
918
961
|
// --- hooks ---
|
|
919
962
|
program
|
|
920
963
|
.command('hooks')
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { buildClaudeDesktopExtension, renderClaudeDesktopExtensionSummary, } from '../core/claude-desktop-extension.js';
|
|
3
|
+
export function runClaudeDesktopExtension(options = {}) {
|
|
4
|
+
const cwd = options.cwd ?? process.cwd();
|
|
5
|
+
const result = buildClaudeDesktopExtension({
|
|
6
|
+
cwd,
|
|
7
|
+
workspaceDir: options.workspace ? path.resolve(cwd, options.workspace) : undefined,
|
|
8
|
+
outputFile: options.output ? path.resolve(cwd, options.output) : undefined,
|
|
9
|
+
projectRoot: options.projectRoot ? path.resolve(cwd, options.projectRoot) : undefined,
|
|
10
|
+
pack: options.pack,
|
|
11
|
+
});
|
|
12
|
+
if (options.json) {
|
|
13
|
+
console.log(JSON.stringify(result, null, 2));
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
console.log(renderClaudeDesktopExtensionSummary(result));
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=claude-desktop-extension.js.map
|
package/dist/commands/doctor.js
CHANGED
|
@@ -25,6 +25,7 @@ import { assessBrainclawVersion } from '../core/brainclaw-version.js';
|
|
|
25
25
|
import { resolveStoreChain } from '../core/store-resolution.js';
|
|
26
26
|
import { resolveCrossProjectLinks, detectCrossProjectCycles } from '../core/cross-project.js';
|
|
27
27
|
import { auditLocalAgentWorkspaceFiles, ensureGitignoreEntries } from '../core/agent-files.js';
|
|
28
|
+
import { summarizeWorkspaceProjects } from '../core/workspace-projects.js';
|
|
28
29
|
const BACKLOG_KEYWORDS = /\b(TODO|NEXT|backlog|next[\s-]step|action[\s-]item|prochaine?s?\s+étapes?|à\s+faire)\b/i;
|
|
29
30
|
function hasBacklogPatterns(text) {
|
|
30
31
|
const lines = text.split(/\r?\n/);
|
|
@@ -138,14 +139,19 @@ export function runDoctor(options = {}) {
|
|
|
138
139
|
});
|
|
139
140
|
}
|
|
140
141
|
}
|
|
141
|
-
|
|
142
|
+
const workspaceProjects = summarizeWorkspaceProjects(options.cwd ?? process.cwd(), config);
|
|
143
|
+
if (config.project_mode === 'multi-project' && workspaceProjects.effective_project_count === 0) {
|
|
142
144
|
checks.push({
|
|
143
145
|
name: 'project_mode',
|
|
144
146
|
status: 'warn',
|
|
145
|
-
message:
|
|
147
|
+
message: config.projects?.strategy === 'folder'
|
|
148
|
+
? 'project_mode is multi-project with folder strategy but no child projects were resolved from config, registry, or nested stores yet.'
|
|
149
|
+
: 'project_mode is multi-project but no project namespaces are configured yet.',
|
|
146
150
|
});
|
|
147
151
|
if (!options.json) {
|
|
148
|
-
console.warn(
|
|
152
|
+
console.warn(config.projects?.strategy === 'folder'
|
|
153
|
+
? '⚠ project_mode is multi-project with folder strategy but no child projects were resolved from config, registry, or nested stores yet.'
|
|
154
|
+
: '⚠ project_mode is multi-project but no project namespaces are configured yet.');
|
|
149
155
|
}
|
|
150
156
|
hasIssues = true;
|
|
151
157
|
}
|
|
@@ -153,10 +159,11 @@ export function runDoctor(options = {}) {
|
|
|
153
159
|
checks.push({
|
|
154
160
|
name: 'project_mode',
|
|
155
161
|
status: 'ok',
|
|
156
|
-
message: `project_mode=${config.project_mode}, strategy=${config.projects?.strategy ?? 'manual'},
|
|
162
|
+
message: `project_mode=${config.project_mode}, strategy=${config.projects?.strategy ?? 'manual'}, configured_projects=${workspaceProjects.configured_projects.length}, effective_projects=${workspaceProjects.effective_project_count}`,
|
|
163
|
+
details: workspaceProjects,
|
|
157
164
|
});
|
|
158
165
|
if (!options.json) {
|
|
159
|
-
console.log(`✔ project mode: ${config.project_mode} (${config.projects?.strategy ?? 'manual'})`);
|
|
166
|
+
console.log(`✔ project mode: ${config.project_mode} (${config.projects?.strategy ?? 'manual'}), effective projects=${workspaceProjects.effective_project_count}`);
|
|
160
167
|
}
|
|
161
168
|
}
|
|
162
169
|
try {
|
package/dist/commands/env.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { memoryExists } from '../core/io.js';
|
|
2
2
|
import { loadConfig } from '../core/config.js';
|
|
3
3
|
import { assessAgentIntegrationReadiness } from '../core/agent-integrations.js';
|
|
4
|
-
import { assessBrainclawVersion } from '../core/brainclaw-version.js';
|
|
4
|
+
import { assessBrainclawVersion, checkBrainclawInstallableUpdate, renderBrainclawInstallableUpdateNotice, } from '../core/brainclaw-version.js';
|
|
5
5
|
import { buildExecutionContext, compactExecutionContext, renderExecutionContextSummary } from '../core/execution-context.js';
|
|
6
6
|
import { buildAgentToolingContext, renderAgentToolingSummary } from '../core/agent-context.js';
|
|
7
7
|
export function runEnv(options = {}) {
|
|
@@ -14,11 +14,14 @@ export function runEnv(options = {}) {
|
|
|
14
14
|
const config = loadConfig(cwd);
|
|
15
15
|
const integrationReadiness = assessAgentIntegrationReadiness(config, cwd);
|
|
16
16
|
const brainclawVersion = assessBrainclawVersion(config);
|
|
17
|
+
const installableUpdate = checkBrainclawInstallableUpdate(config, cwd, { useDefaultNpmSource: true });
|
|
18
|
+
const installableUpdateNotice = renderBrainclawInstallableUpdateNotice(installableUpdate);
|
|
17
19
|
const agentTooling = options.agentTooling ? buildAgentToolingContext({ cwd }) : undefined;
|
|
18
20
|
if (options.json) {
|
|
19
21
|
console.log(JSON.stringify({
|
|
20
22
|
execution_context: executionContext,
|
|
21
23
|
brainclaw_version: brainclawVersion,
|
|
24
|
+
installable_update: installableUpdate,
|
|
22
25
|
declared_agent_integrations: config.agent_integrations,
|
|
23
26
|
integration_readiness: integrationReadiness,
|
|
24
27
|
...(agentTooling ? { agent_tooling: agentTooling } : {}),
|
|
@@ -33,6 +36,9 @@ export function runEnv(options = {}) {
|
|
|
33
36
|
console.log(`Upgrade benefits: ${brainclawVersion.upgrade_message}`);
|
|
34
37
|
}
|
|
35
38
|
}
|
|
39
|
+
if (installableUpdateNotice) {
|
|
40
|
+
console.log(installableUpdateNotice);
|
|
41
|
+
}
|
|
36
42
|
console.log(`Declared agent integrations: ${config.agent_integrations.declarations.length}`);
|
|
37
43
|
const missingDeclarations = integrationReadiness.filter((entry) => !entry.ready);
|
|
38
44
|
if (missingDeclarations.length > 0) {
|
package/dist/commands/init.js
CHANGED
|
@@ -14,6 +14,7 @@ import { renderBootstrapSummary, runBootstrapProfile } from '../core/bootstrap.j
|
|
|
14
14
|
import { isAgentIntegrationName, upsertAgentIntegrationDeclaration } from '../core/agent-integrations.js';
|
|
15
15
|
import { describeAutoConfigWrite, ensureAgentFiles, ensureGitignoreEntries, writeDetectedAgentAutoConfig } from '../core/agent-files.js';
|
|
16
16
|
import { detectAiAgent, detectWslEnvironment } from '../core/ai-agent-detection.js';
|
|
17
|
+
import { buildAiSurfaceInventory, renderAiSurfaceUsageHints } from '../core/ai-surface-inventory.js';
|
|
17
18
|
import { hasCompletedSetup } from '../core/setup-state.js';
|
|
18
19
|
import { writeDetectedAgentExport } from './export.js';
|
|
19
20
|
import { writeDetectedAgentHooks } from './hooks.js';
|
|
@@ -178,6 +179,20 @@ export async function runInit(options = {}) {
|
|
|
178
179
|
if (detectedExport) {
|
|
179
180
|
console.log(`\u2714 Agent instructions written to ${detectedExport.relativePath} (${detectedExport.created ? 'created' : 'updated'})`);
|
|
180
181
|
}
|
|
182
|
+
const visibleSurfaces = buildAiSurfaceInventory().filter((surface) => surface.status !== 'not_detected');
|
|
183
|
+
if (visibleSurfaces.length > 0) {
|
|
184
|
+
console.log('✔ Other AI work surfaces detected on this machine:');
|
|
185
|
+
for (const surface of visibleSurfaces) {
|
|
186
|
+
console.log(` - ${surface.display_name} [${surface.surface_kind}, ${surface.status}]`);
|
|
187
|
+
}
|
|
188
|
+
const usageHints = renderAiSurfaceUsageHints(visibleSurfaces);
|
|
189
|
+
if (usageHints.length > 0) {
|
|
190
|
+
console.log(' Suggested uses:');
|
|
191
|
+
for (const line of usageHints) {
|
|
192
|
+
console.log(` ${line}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
181
196
|
for (const hook of detectedHooks) {
|
|
182
197
|
console.log(`\u2714 Session hook written to ${hook.relativePath} (${hook.created ? 'created' : 'updated'})`);
|
|
183
198
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { memoryExists } from '../core/io.js';
|
|
2
|
+
import { listAiSurfaceTasks } from '../core/ai-surface-tasks.js';
|
|
3
|
+
export function runListSurfaceTasks(options = {}) {
|
|
4
|
+
if (!memoryExists(options.cwd)) {
|
|
5
|
+
console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
|
|
6
|
+
process.exit(1);
|
|
7
|
+
}
|
|
8
|
+
let tasks = listAiSurfaceTasks(options.cwd);
|
|
9
|
+
if (!options.all) {
|
|
10
|
+
tasks = tasks.filter((task) => task.status === 'queued' || task.status === 'in_progress');
|
|
11
|
+
}
|
|
12
|
+
if (options.status) {
|
|
13
|
+
tasks = tasks.filter((task) => task.status === options.status);
|
|
14
|
+
}
|
|
15
|
+
if (options.target) {
|
|
16
|
+
const target = options.target.toLowerCase();
|
|
17
|
+
tasks = tasks.filter((task) => task.target_surface.toLowerCase() === target);
|
|
18
|
+
}
|
|
19
|
+
if (options.json) {
|
|
20
|
+
console.log(JSON.stringify(tasks, null, 2));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (tasks.length === 0) {
|
|
24
|
+
console.log('No surface tasks.');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
console.log(`${tasks.length} surface task(s):`);
|
|
28
|
+
console.log('');
|
|
29
|
+
for (const task of tasks) {
|
|
30
|
+
console.log(` [${task.id}] ${task.title} (${task.status}, target ${task.target_surface}, kind ${task.kind})`);
|
|
31
|
+
if (task.requested_outputs.length > 0) {
|
|
32
|
+
console.log(` outputs: ${task.requested_outputs.join(', ')}`);
|
|
33
|
+
}
|
|
34
|
+
if (task.result_note) {
|
|
35
|
+
console.log(` result: ${task.result_note}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=list-surface-tasks.js.map
|
package/dist/commands/mcp.js
CHANGED
|
@@ -7,6 +7,7 @@ import { buildAgentToolingContext, renderAgentToolingSummary } from '../core/age
|
|
|
7
7
|
import { buildCoordinationSnapshot } from '../core/coordination.js';
|
|
8
8
|
import { buildContext, renderContextMarkdown, renderContextPromptTemplate } from '../core/context.js';
|
|
9
9
|
import { buildExecutionContext, renderExecutionContextSummary } from '../core/execution-context.js';
|
|
10
|
+
import { checkBrainclawInstallableUpdate, renderBrainclawInstallableUpdateNotice } from '../core/brainclaw-version.js';
|
|
10
11
|
import { loadConfig } from '../core/config.js';
|
|
11
12
|
import { loadState, persistState } from '../core/state.js';
|
|
12
13
|
import { memoryExists } from '../core/io.js';
|
|
@@ -82,7 +83,7 @@ export const MCP_READ_TOOLS = [
|
|
|
82
83
|
},
|
|
83
84
|
{
|
|
84
85
|
name: 'bclaw_get_execution_context',
|
|
85
|
-
description: 'Inspect the local execution environment and optionally agent tooling signals.',
|
|
86
|
+
description: 'Inspect the local execution environment, installable Brainclaw update channel, and optionally agent tooling signals.',
|
|
86
87
|
inputSchema: {
|
|
87
88
|
type: 'object',
|
|
88
89
|
properties: {
|
|
@@ -1133,15 +1134,20 @@ export function handleMcpReadToolCall(name, args = {}, context = {}) {
|
|
|
1133
1134
|
}
|
|
1134
1135
|
if (name === 'bclaw_get_execution_context') {
|
|
1135
1136
|
const executionContext = buildExecutionContext({ cwd });
|
|
1137
|
+
const config = loadConfig(cwd);
|
|
1138
|
+
const installableUpdate = checkBrainclawInstallableUpdate(config, cwd, { useDefaultNpmSource: true });
|
|
1139
|
+
const installableUpdateNotice = renderBrainclawInstallableUpdateNotice(installableUpdate);
|
|
1136
1140
|
const agentTooling = args.includeAgentTooling ? buildAgentToolingContext({ cwd }) : undefined;
|
|
1137
1141
|
const text = [
|
|
1138
1142
|
renderExecutionContextSummary(executionContext, true),
|
|
1143
|
+
...(installableUpdateNotice ? ['', installableUpdateNotice] : []),
|
|
1139
1144
|
...(agentTooling ? ['', renderAgentToolingSummary(agentTooling)] : []),
|
|
1140
1145
|
].join('\n');
|
|
1141
1146
|
return {
|
|
1142
1147
|
content: [{ type: 'text', text }],
|
|
1143
1148
|
structuredContent: {
|
|
1144
1149
|
execution_context: executionContext,
|
|
1150
|
+
installable_update: installableUpdate,
|
|
1145
1151
|
...(agentTooling ? { agent_tooling: agentTooling } : {}),
|
|
1146
1152
|
},
|
|
1147
1153
|
};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import readline from 'node:readline/promises';
|
|
2
|
+
import { stdin as input, stdout as output } from 'node:process';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { buildMachineProfile, saveMachineProfile } from '../core/machine-profile.js';
|
|
5
|
+
import { buildAgentInventory, saveAgentInventory } from '../core/agent-inventory.js';
|
|
6
|
+
import { applyBootstrapImport, runBootstrapProfile } from '../core/bootstrap.js';
|
|
7
|
+
import { loadConfig } from '../core/config.js';
|
|
8
|
+
import { memoryExists } from '../core/io.js';
|
|
9
|
+
import { summarizeWorkspaceProjects } from '../core/workspace-projects.js';
|
|
10
|
+
export async function runReconcile(options = {}) {
|
|
11
|
+
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
12
|
+
if (!memoryExists(cwd)) {
|
|
13
|
+
console.error('Error: .brainclaw/ not found in the current directory. Run `brainclaw init` first.');
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
const rootConfig = loadConfig(cwd);
|
|
17
|
+
const workspaceSummary = summarizeWorkspaceProjects(cwd, rootConfig);
|
|
18
|
+
const storeTargets = [
|
|
19
|
+
cwd,
|
|
20
|
+
...workspaceSummary.uses_folder_resolution
|
|
21
|
+
? workspaceSummary.discovered_projects
|
|
22
|
+
.filter((project) => project.source !== 'config')
|
|
23
|
+
.map((project) => project.path)
|
|
24
|
+
: [],
|
|
25
|
+
];
|
|
26
|
+
const uniqueTargets = [...new Set(storeTargets.map((store) => path.resolve(store)))];
|
|
27
|
+
if (options.json && options.dryRun) {
|
|
28
|
+
console.log(JSON.stringify({
|
|
29
|
+
cwd,
|
|
30
|
+
mode: 'dry_run',
|
|
31
|
+
workspace_summary: workspaceSummary,
|
|
32
|
+
planned_actions: {
|
|
33
|
+
machine_profile_refresh: !options.skipMachineProfile,
|
|
34
|
+
agent_inventory_refresh: !options.skipAgentInventory,
|
|
35
|
+
bootstrap_refresh: uniqueTargets.map((store) => ({
|
|
36
|
+
cwd: store,
|
|
37
|
+
relative_path: path.relative(cwd, store) || '.',
|
|
38
|
+
apply_bootstrap: Boolean(options.applyBootstrap),
|
|
39
|
+
})),
|
|
40
|
+
},
|
|
41
|
+
}, null, 2));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (options.applyBootstrap) {
|
|
45
|
+
await confirmBootstrapApply(options.yes);
|
|
46
|
+
}
|
|
47
|
+
const machineProfilePath = !options.skipMachineProfile
|
|
48
|
+
? saveMachineProfile(buildMachineProfile())
|
|
49
|
+
: null;
|
|
50
|
+
const agentInventoryPath = !options.skipAgentInventory
|
|
51
|
+
? saveAgentInventory(buildAgentInventory())
|
|
52
|
+
: null;
|
|
53
|
+
const stores = [];
|
|
54
|
+
for (const store of uniqueTargets) {
|
|
55
|
+
const config = loadConfig(store);
|
|
56
|
+
if (options.applyBootstrap) {
|
|
57
|
+
const result = applyBootstrapImport({ cwd: store, refresh: true });
|
|
58
|
+
stores.push({
|
|
59
|
+
cwd: store,
|
|
60
|
+
relative_path: path.relative(cwd, store) || '.',
|
|
61
|
+
project_name: config.project_name,
|
|
62
|
+
project_mode: config.project_mode,
|
|
63
|
+
project_strategy: config.projects.strategy,
|
|
64
|
+
bootstrap_refreshed: true,
|
|
65
|
+
bootstrap_applied: true,
|
|
66
|
+
workspace_kind: result.proposal.workspace_kind,
|
|
67
|
+
onboarding_mode: result.proposal.onboarding_mode,
|
|
68
|
+
confidence: result.proposal.confidence,
|
|
69
|
+
suggestion_count: result.proposal.suggestion_count,
|
|
70
|
+
created_count: result.createdCount,
|
|
71
|
+
skipped_count: result.skippedCount,
|
|
72
|
+
});
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const result = runBootstrapProfile({ cwd: store, refresh: true });
|
|
76
|
+
stores.push({
|
|
77
|
+
cwd: store,
|
|
78
|
+
relative_path: path.relative(cwd, store) || '.',
|
|
79
|
+
project_name: config.project_name,
|
|
80
|
+
project_mode: config.project_mode,
|
|
81
|
+
project_strategy: config.projects.strategy,
|
|
82
|
+
bootstrap_refreshed: true,
|
|
83
|
+
bootstrap_applied: false,
|
|
84
|
+
workspace_kind: result.profile.workspace_kind,
|
|
85
|
+
onboarding_mode: result.profile.onboarding_mode,
|
|
86
|
+
confidence: result.profile.confidence,
|
|
87
|
+
suggestion_count: result.importPlan.suggestion_count,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
if (options.json) {
|
|
91
|
+
console.log(JSON.stringify({
|
|
92
|
+
cwd,
|
|
93
|
+
mode: options.applyBootstrap ? 'apply' : 'refresh',
|
|
94
|
+
workspace_summary: workspaceSummary,
|
|
95
|
+
machine_profile_refreshed: !options.skipMachineProfile,
|
|
96
|
+
machine_profile_path: machineProfilePath,
|
|
97
|
+
agent_inventory_refreshed: !options.skipAgentInventory,
|
|
98
|
+
agent_inventory_path: agentInventoryPath,
|
|
99
|
+
stores,
|
|
100
|
+
}, null, 2));
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (!options.skipMachineProfile) {
|
|
104
|
+
console.log(`✔ Machine profile refreshed: ${machineProfilePath}`);
|
|
105
|
+
}
|
|
106
|
+
if (!options.skipAgentInventory) {
|
|
107
|
+
console.log(`✔ Agent inventory refreshed: ${agentInventoryPath}`);
|
|
108
|
+
}
|
|
109
|
+
console.log('');
|
|
110
|
+
console.log(`Reconciled ${stores.length} store(s):`);
|
|
111
|
+
for (const store of stores) {
|
|
112
|
+
const suffix = store.bootstrap_applied
|
|
113
|
+
? `${store.created_count ?? 0} created, ${store.skipped_count ?? 0} skipped`
|
|
114
|
+
: `${store.suggestion_count ?? 0} bootstrap suggestion(s) available`;
|
|
115
|
+
console.log(` - ${store.relative_path} (${store.project_name}) — ${store.onboarding_mode ?? 'unknown'}, confidence=${store.confidence ?? 'unknown'}, ${suffix}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async function confirmBootstrapApply(yes) {
|
|
119
|
+
if (yes) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
123
|
+
console.error('Error: bootstrap apply across multiple stores requires --yes in non-interactive mode.');
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
const rl = readline.createInterface({ input, output });
|
|
127
|
+
try {
|
|
128
|
+
const answer = await rl.question('Apply bootstrap suggestions across all selected stores? [y/N] ');
|
|
129
|
+
if (answer.trim().toLowerCase() !== 'y') {
|
|
130
|
+
console.error('Cancelled.');
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
finally {
|
|
135
|
+
rl.close();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=reconcile.js.map
|
package/dist/commands/setup.js
CHANGED
|
@@ -4,6 +4,7 @@ import readline from 'node:readline/promises';
|
|
|
4
4
|
import { spawnSync } from 'node:child_process';
|
|
5
5
|
import { runInit } from './init.js';
|
|
6
6
|
import { detectAiAgent } from '../core/ai-agent-detection.js';
|
|
7
|
+
import { buildAiSurfaceInventory, renderAiSurfaceUsageHints } from '../core/ai-surface-inventory.js';
|
|
7
8
|
import { buildMachineProfile, saveMachineProfile, loadMachineProfile } from '../core/machine-profile.js';
|
|
8
9
|
import { buildAgentInventory, saveAgentInventory, loadAgentInventory } from '../core/agent-inventory.js';
|
|
9
10
|
import { ensureClaudeCodeUserSettings, ensureClaudeCodeUserCommand, ensureCursorMcpConfig, ensureWindsurfMcpConfig, ensureAntigravityMcpConfig, ensureContinueUserMcpConfig, ensureCodexMcpConfig, writeDetectedAgentAutoConfig, describeAutoConfigWrite, ensureGitignoreEntries, collectWorkspaceGitignoreEntries, } from '../core/agent-files.js';
|
|
@@ -388,6 +389,7 @@ export async function runSetup(options = {}) {
|
|
|
388
389
|
// Step 4: Agent detection & selection
|
|
389
390
|
const detectedAi = detectAiAgent(env);
|
|
390
391
|
const detectedName = detectedAi?.name;
|
|
392
|
+
const detectedSurfaces = buildAiSurfaceInventory();
|
|
391
393
|
console.log('');
|
|
392
394
|
if (detectedName) {
|
|
393
395
|
console.log(`Detected AI agent: ${detectedName}`);
|
|
@@ -395,6 +397,23 @@ export async function runSetup(options = {}) {
|
|
|
395
397
|
else {
|
|
396
398
|
console.log('No AI agent detected automatically.');
|
|
397
399
|
}
|
|
400
|
+
const visibleSurfaces = detectedSurfaces.filter((surface) => surface.status !== 'not_detected');
|
|
401
|
+
if (visibleSurfaces.length > 0) {
|
|
402
|
+
console.log('Other AI work surfaces on this machine:');
|
|
403
|
+
for (const surface of visibleSurfaces) {
|
|
404
|
+
console.log(` - ${surface.display_name} [${surface.surface_kind}, ${surface.status}]`);
|
|
405
|
+
}
|
|
406
|
+
const usageHints = renderAiSurfaceUsageHints(visibleSurfaces);
|
|
407
|
+
if (usageHints.length > 0) {
|
|
408
|
+
console.log('');
|
|
409
|
+
console.log('Suggested uses:');
|
|
410
|
+
for (const line of usageHints) {
|
|
411
|
+
console.log(` ${line}`);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
console.log('');
|
|
415
|
+
console.log('These surfaces are tracked separately from coding agents and will use tailored onboarding flows.');
|
|
416
|
+
}
|
|
398
417
|
console.log('Supported agents:');
|
|
399
418
|
ALL_KNOWN_AGENTS.forEach((a, i) => {
|
|
400
419
|
const tag = a === detectedName ? ' ← detected' : '';
|
package/dist/commands/status.js
CHANGED
|
@@ -9,7 +9,8 @@ import { loadState } from '../core/state.js';
|
|
|
9
9
|
import { isTrapActive, listOperationalTraps } from '../core/traps.js';
|
|
10
10
|
import { generateMarkdown } from '../core/markdown.js';
|
|
11
11
|
import { memoryExists } from '../core/io.js';
|
|
12
|
-
|
|
12
|
+
import { summarizeWorkspaceProjects } from '../core/workspace-projects.js';
|
|
13
|
+
function printHumanStatus(state, config, cwd) {
|
|
13
14
|
const activePlans = state.plan_items.filter((plan) => plan.status !== 'done' && plan.status !== 'dropped');
|
|
14
15
|
const activeInstructions = loadInstructions().filter((entry) => entry.active);
|
|
15
16
|
const activeClaims = listClaims().filter((claim) => claim.status === 'active');
|
|
@@ -21,6 +22,7 @@ function printHumanStatus(state, config) {
|
|
|
21
22
|
const activeSharedTraps = state.known_traps.filter((trap) => isTrapActive(trap));
|
|
22
23
|
const resolvedSharedTraps = state.known_traps.filter((trap) => trap.status === 'resolved');
|
|
23
24
|
const activeMachineTraps = machineTraps.filter((trap) => isTrapActive(trap));
|
|
25
|
+
const workspaceProjects = summarizeWorkspaceProjects(cwd, config);
|
|
24
26
|
const counts = {
|
|
25
27
|
instructions: activeInstructions.length,
|
|
26
28
|
claims: activeClaims.length,
|
|
@@ -50,7 +52,7 @@ function printHumanStatus(state, config) {
|
|
|
50
52
|
console.log(` Traps : ${activeSharedTraps.length} active shared, ${resolvedSharedTraps.length} resolved shared, ${activeMachineTraps.length} active machine-local (${currentHost})`);
|
|
51
53
|
console.log(` Handoffs : ${counts.handoffs}`);
|
|
52
54
|
if (config.project_mode === 'multi-project') {
|
|
53
|
-
console.log(` Projects : ${
|
|
55
|
+
console.log(` Projects : ${workspaceProjects.effective_project_count} effective (${workspaceProjects.strategy})`);
|
|
54
56
|
}
|
|
55
57
|
// Show recent items (last 3 per section)
|
|
56
58
|
const sections = [
|
|
@@ -84,24 +86,27 @@ function printHumanStatus(state, config) {
|
|
|
84
86
|
}
|
|
85
87
|
}
|
|
86
88
|
export function runStatus(options = {}) {
|
|
87
|
-
|
|
89
|
+
const cwd = process.cwd();
|
|
90
|
+
if (!memoryExists(cwd)) {
|
|
88
91
|
console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
|
|
89
92
|
process.exit(1);
|
|
90
93
|
}
|
|
91
|
-
const config = loadConfig();
|
|
92
|
-
const state = loadState();
|
|
94
|
+
const config = loadConfig(cwd);
|
|
95
|
+
const state = loadState(cwd);
|
|
93
96
|
const currentHost = resolveCurrentHostId();
|
|
94
|
-
const sharedRuntimeNotes = listRuntimeNotes({ visibility: 'shared' });
|
|
95
|
-
const machineRuntimeNotes = listRuntimeNotes({ visibility: 'machine' });
|
|
96
|
-
const machineTraps = listOperationalTraps({ visibility: 'machine' });
|
|
97
|
+
const sharedRuntimeNotes = listRuntimeNotes({ visibility: 'shared' }, cwd);
|
|
98
|
+
const machineRuntimeNotes = listRuntimeNotes({ visibility: 'machine' }, cwd);
|
|
99
|
+
const machineTraps = listOperationalTraps({ visibility: 'machine' }, cwd);
|
|
97
100
|
const activeSharedTraps = state.known_traps.filter((trap) => isTrapActive(trap));
|
|
98
101
|
const resolvedSharedTraps = state.known_traps.filter((trap) => trap.status === 'resolved');
|
|
99
102
|
const activeMachineTraps = machineTraps.filter((trap) => isTrapActive(trap));
|
|
100
|
-
const registeredAgents = listAgentIdentities();
|
|
101
|
-
const reputation = buildReputationSnapshot();
|
|
103
|
+
const registeredAgents = listAgentIdentities(cwd);
|
|
104
|
+
const reputation = buildReputationSnapshot(cwd);
|
|
105
|
+
const workspaceProjects = summarizeWorkspaceProjects(cwd, config);
|
|
102
106
|
if (options.json) {
|
|
103
107
|
console.log(JSON.stringify({
|
|
104
108
|
config,
|
|
109
|
+
workspace_projects: workspaceProjects,
|
|
105
110
|
agents: {
|
|
106
111
|
current_name: config.current_agent,
|
|
107
112
|
current_id: config.current_agent_id,
|
|
@@ -125,9 +130,9 @@ export function runStatus(options = {}) {
|
|
|
125
130
|
return;
|
|
126
131
|
}
|
|
127
132
|
if (options.markdown) {
|
|
128
|
-
console.log(generateMarkdown(state));
|
|
133
|
+
console.log(generateMarkdown(state, cwd));
|
|
129
134
|
return;
|
|
130
135
|
}
|
|
131
|
-
printHumanStatus(state, config);
|
|
136
|
+
printHumanStatus(state, config, cwd);
|
|
132
137
|
}
|
|
133
138
|
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { runSurfaceTask } from './surface-task.js';
|
|
2
|
+
import { runListSurfaceTasks } from './list-surface-tasks.js';
|
|
3
|
+
import { runUpdateSurfaceTask } from './update-surface-task.js';
|
|
4
|
+
export function runSurfaceTaskResource(subcommand, args, options = {}) {
|
|
5
|
+
const normalized = subcommand.trim().toLowerCase();
|
|
6
|
+
if (normalized === 'create') {
|
|
7
|
+
const title = args.join(' ').trim();
|
|
8
|
+
if (!title) {
|
|
9
|
+
console.error('Error: surface-task create requires <title>.');
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
runSurfaceTask(title, options);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (normalized === 'list' || normalized === 'ls') {
|
|
16
|
+
runListSurfaceTasks(options);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (normalized === 'update') {
|
|
20
|
+
const id = args[0];
|
|
21
|
+
if (!id) {
|
|
22
|
+
console.error('Error: surface-task update requires <id>.');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
runUpdateSurfaceTask(id, options);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const legacyTitle = [subcommand, ...args].join(' ').trim();
|
|
29
|
+
if (!legacyTitle) {
|
|
30
|
+
console.error('Error: missing surface-task subcommand or title.');
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
runSurfaceTask(legacyTitle, options);
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=surface-task-resource.js.map
|