brainclaw 0.19.12 → 0.20.0
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 +42 -11
- package/dist/cli.js +55 -1
- package/dist/commands/claude-desktop-extension.js +18 -0
- package/dist/commands/context.js +3 -1
- package/dist/commands/doctor.js +12 -5
- package/dist/commands/export.js +44 -0
- package/dist/commands/init.js +22 -6
- package/dist/commands/list-surface-tasks.js +39 -0
- package/dist/commands/mcp.js +86 -5
- 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/uninstall.js +145 -0
- package/dist/commands/update-surface-task.js +30 -0
- package/dist/core/agent-capability.js +184 -0
- package/dist/core/agent-context.js +24 -6
- 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/bootstrap.js +177 -0
- package/dist/core/claude-desktop-extension.js +224 -0
- package/dist/core/context.js +47 -24
- package/dist/core/ids.js +1 -0
- package/dist/core/instruction-templates.js +308 -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 +34 -0
- package/dist/core/setup-flow.js +191 -0
- package/dist/core/setup-state.js +30 -1
- package/dist/core/store-resolution.js +58 -0
- package/dist/core/workspace-projects.js +115 -0
- package/docs/architecture/project-refs.md +305 -0
- package/docs/cli.md +133 -1
- package/docs/integrations/agents.md +102 -150
- package/docs/integrations/overview.md +71 -45
- package/docs/quickstart.md +44 -111
- 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
|
---
|
|
@@ -62,18 +66,20 @@ If you are documenting or integrating an agent workflow, treat MCP as the primar
|
|
|
62
66
|
|
|
63
67
|
brainclaw is designed to sit alongside the coding agents teams are already using, not behind a separate hosted control plane.
|
|
64
68
|
|
|
65
|
-
| Logo | Agent |
|
|
69
|
+
| Logo | Agent | Integration | What brainclaw configures |
|
|
66
70
|
|---|---|---|---|
|
|
67
|
-
| [](https://github.com/anthropics/claude-code) | **[Claude Code](https://github.com/anthropics/claude-code)** |
|
|
68
|
-
| [](https://openai.com/codex/) | **[Codex](https://openai.com/codex/)** |
|
|
69
|
-
| [](https://cursor.com/en-US) | **[Cursor](https://cursor.com/en-US)** |
|
|
70
|
-
| [](https://github.com/opencode-ai/opencode) | **[OpenCode](https://github.com/opencode-ai/opencode)** |
|
|
71
|
-
| [](https://windsurf.com/) | **[Windsurf](https://windsurf.com/)** |
|
|
72
|
-
| [](https://github.com/RooCodeInc/Roo-Code) | **[Roo](https://github.com/RooCodeInc/Roo-Code)** |
|
|
73
|
-
| [](https://github.com/features/copilot) | **[GitHub Copilot](https://github.com/features/copilot)** |
|
|
71
|
+
| [](https://github.com/anthropics/claude-code) | **[Claude Code](https://github.com/anthropics/claude-code)** | Full | MCP + CLAUDE.md + hooks + permissions + /brainclaw command |
|
|
72
|
+
| [](https://openai.com/codex/) | **[Codex](https://openai.com/codex/)** | Standard | MCP + AGENTS.md |
|
|
73
|
+
| [](https://cursor.com/en-US) | **[Cursor](https://cursor.com/en-US)** | Standard | MCP + .cursor/rules/ + alwaysApply MDC |
|
|
74
|
+
| [](https://github.com/opencode-ai/opencode) | **[OpenCode](https://github.com/opencode-ai/opencode)** | Standard | MCP + AGENTS.md |
|
|
75
|
+
| [](https://windsurf.com/) | **[Windsurf](https://windsurf.com/)** | Standard | MCP + .windsurfrules session trigger |
|
|
76
|
+
| [](https://github.com/RooCodeInc/Roo-Code) | **[Roo](https://github.com/RooCodeInc/Roo-Code)** | Standard | MCP (auto-approve) + .roo/rules/ |
|
|
77
|
+
| [](https://github.com/cline/cline) | **[Cline](https://github.com/cline/cline)** | Standard | MCP (auto-approve) + .clinerules/ |
|
|
78
|
+
| [](https://github.com/continuedev/continue) | **[Continue](https://github.com/continuedev/continue)** | Standard | MCP + .continue/rules/ |
|
|
79
|
+
| [](https://github.com/google-gemini/gemini-cli) | **[Antigravity / Gemini CLI](https://github.com/google-gemini/gemini-cli)** | Standard | MCP + GEMINI.md |
|
|
80
|
+
| [](https://github.com/features/copilot) | **[GitHub Copilot](https://github.com/features/copilot)** | Limited | copilot-instructions.md + brainclaw-context skill |
|
|
81
|
+
|
|
82
|
+
**Full** = MCP + hooks + auto-approve (context injected every prompt). **Standard** = MCP + instruction file (agent must call tools explicitly). **Limited** = no MCP (static instruction file is the only source of context).
|
|
77
83
|
|
|
78
84
|
brainclaw is most effective today when one agent works at a time in a given checkout and the next agent resumes from shared context, claims, and handoffs.
|
|
79
85
|
|
|
@@ -146,6 +152,18 @@ brainclaw context --for src/auth/routes.ts --digest
|
|
|
146
152
|
brainclaw status
|
|
147
153
|
```
|
|
148
154
|
|
|
155
|
+
And if the current coding agent wants to hand off a non-code task to another local AI surface for later:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
brainclaw surface-task create "Generate homepage hero visual" \
|
|
159
|
+
--target chatgpt \
|
|
160
|
+
--kind visual_asset \
|
|
161
|
+
--instructions "Create a lightweight product hero visual for the landing page." \
|
|
162
|
+
--output assets/hero-home.png
|
|
163
|
+
|
|
164
|
+
brainclaw surface-task list
|
|
165
|
+
```
|
|
166
|
+
|
|
149
167
|
---
|
|
150
168
|
|
|
151
169
|
## Installation
|
|
@@ -287,6 +305,19 @@ npm run test:coverage # with coverage report
|
|
|
287
305
|
|
|
288
306
|
## Changelog
|
|
289
307
|
|
|
308
|
+
### v0.20.0
|
|
309
|
+
|
|
310
|
+
- **Onboarding rework** : nouveau parcours d'installation en 2-3 étapes au lieu de 4, avec choix explicites (type de projet, topologie mémoire) au lieu de termes techniques
|
|
311
|
+
- **Agent capability profiles** : chaque agent (10 supportés) a un profil de capacité (MCP, hooks, auto-approve, skills, rules) qui détermine le contenu de ses fichiers d'instructions
|
|
312
|
+
- **Templates adaptatifs** : les fichiers d'instructions (CLAUDE.md, AGENTS.md, etc.) sont générés selon 3 tiers — Full (léger, hooks font le reste), Standard (directif, top traps inclus), Limited (riche, tout en statique)
|
|
313
|
+
- **Séparation core/run** : les fichiers statiques ne contiennent plus que le protocole, les contraintes et instructions. Les traps, plans, decisions et claims sont exclusivement dans le contexte dynamique MCP
|
|
314
|
+
- **Bootstrap enrichi** : scan des workflows CI/CD, ADR, CONTRIBUTING, Docker, .env.example, branches actives et tags git
|
|
315
|
+
- **Quick setup MCP** : `bclaw_setup` détecte le repo courant et propose un init rapide au lieu de forcer le scan multi-repo
|
|
316
|
+
- **`brainclaw uninstall`** : nouvelle commande pour retirer brainclaw d'un projet (`--project`) ou d'une machine (`--machine`)
|
|
317
|
+
- **Traps machine-scoped** : nouveau champ `platform_scope` sur les traps — les traps machine ne polluent plus les fichiers d'instructions statiques
|
|
318
|
+
- **Init sans setup préalable** : `brainclaw init` crée automatiquement le user store si absent, plus besoin de `brainclaw setup` d'abord
|
|
319
|
+
- **Docs réécrites** : intégrations, quickstart et README avec niveaux Full/Standard/Limited et matrice agent factuelle
|
|
320
|
+
|
|
290
321
|
### v0.9.10
|
|
291
322
|
|
|
292
323
|
- **OpenCode** : détection et auto-config MCP workspace via `opencode.json`; l'export réutilise `AGENTS.md`
|
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>')
|
|
@@ -439,6 +463,17 @@ program
|
|
|
439
463
|
.action((options) => {
|
|
440
464
|
runVersion(options);
|
|
441
465
|
});
|
|
466
|
+
// --- uninstall ---
|
|
467
|
+
import { runUninstall } from './commands/uninstall.js';
|
|
468
|
+
program
|
|
469
|
+
.command('uninstall')
|
|
470
|
+
.description('Remove brainclaw from a project and/or machine')
|
|
471
|
+
.option('--project', 'Remove brainclaw from the current project (.brainclaw/, agent files, configs)')
|
|
472
|
+
.option('--machine', 'Remove brainclaw global config (~/.brainclaw/)')
|
|
473
|
+
.option('-y, --yes', 'Skip confirmation prompts')
|
|
474
|
+
.action(async (options) => {
|
|
475
|
+
await runUninstall(options);
|
|
476
|
+
});
|
|
442
477
|
// --- rebuild ---
|
|
443
478
|
program
|
|
444
479
|
.command('rebuild')
|
|
@@ -915,6 +950,25 @@ program
|
|
|
915
950
|
.action((options) => {
|
|
916
951
|
runExport(options);
|
|
917
952
|
});
|
|
953
|
+
program
|
|
954
|
+
.command('reconcile')
|
|
955
|
+
.description('Refresh machine and workspace bootstrap state after updates or onboarding on complex installs')
|
|
956
|
+
.option('--json', 'Output as JSON')
|
|
957
|
+
.option('--dry-run', 'Preview the reconciliation plan without writing machine or bootstrap state')
|
|
958
|
+
.option('--apply-bootstrap', 'Apply bootstrap suggestions across all selected stores after refresh')
|
|
959
|
+
.option('-y, --yes', 'Skip confirmation prompts for multi-store bootstrap apply')
|
|
960
|
+
.option('--skip-machine-profile', 'Skip machine-profile refresh')
|
|
961
|
+
.option('--skip-agent-inventory', 'Skip agent-inventory refresh')
|
|
962
|
+
.action(async (options) => {
|
|
963
|
+
await runReconcile({
|
|
964
|
+
json: options.json,
|
|
965
|
+
dryRun: options.dryRun,
|
|
966
|
+
applyBootstrap: options.applyBootstrap,
|
|
967
|
+
yes: options.yes,
|
|
968
|
+
skipMachineProfile: options.skipMachineProfile,
|
|
969
|
+
skipAgentInventory: options.skipAgentInventory,
|
|
970
|
+
});
|
|
971
|
+
});
|
|
918
972
|
// --- hooks ---
|
|
919
973
|
program
|
|
920
974
|
.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/context.js
CHANGED
|
@@ -3,6 +3,7 @@ import { buildContext, renderContextMarkdown, renderContextPromptTemplate } from
|
|
|
3
3
|
import { writeContextMarker } from '../core/freshness.js';
|
|
4
4
|
import { nowISO } from '../core/ids.js';
|
|
5
5
|
import { logger } from '../core/logger.js';
|
|
6
|
+
import { resolveContextStoreCwd } from '../core/store-resolution.js';
|
|
6
7
|
export function runContext(options = {}) {
|
|
7
8
|
const cwd = options.cwd ?? process.cwd();
|
|
8
9
|
if (!memoryExists(cwd)) {
|
|
@@ -17,6 +18,7 @@ export function runContext(options = {}) {
|
|
|
17
18
|
}
|
|
18
19
|
return;
|
|
19
20
|
}
|
|
21
|
+
const contextCwd = resolveContextStoreCwd(cwd, options.for);
|
|
20
22
|
const result = buildContext({
|
|
21
23
|
target: options.for,
|
|
22
24
|
project: options.project,
|
|
@@ -43,7 +45,7 @@ export function runContext(options = {}) {
|
|
|
43
45
|
else {
|
|
44
46
|
console.log(renderContextMarkdown(result, options.explain));
|
|
45
47
|
}
|
|
46
|
-
writeLastContextMarker(result, options,
|
|
48
|
+
writeLastContextMarker(result, options, contextCwd);
|
|
47
49
|
}
|
|
48
50
|
function writeLastContextMarker(result, options, cwd) {
|
|
49
51
|
try {
|
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/export.js
CHANGED
|
@@ -8,6 +8,9 @@ import { resolveInstructions, loadInstructions } from '../core/instructions.js';
|
|
|
8
8
|
import { detectAiAgent } from '../core/ai-agent-detection.js';
|
|
9
9
|
import { resolveExportTarget, resolveExportTargetByFormat, writeExportFile, buildHygieneSection, describeAutoConfigWrite, writeExportCompanionFiles, collectExportGitignoreEntries, ensureGitignoreEntries, } from '../core/agent-files.js';
|
|
10
10
|
import { logger } from '../core/logger.js';
|
|
11
|
+
import { getAgentCapabilityProfile } from '../core/agent-capability.js';
|
|
12
|
+
import { renderBrainclawSection } from '../core/instruction-templates.js';
|
|
13
|
+
import { getInstalledBrainclawVersion } from '../core/brainclaw-version.js';
|
|
11
14
|
export function runExport(options) {
|
|
12
15
|
const cwd = options.cwd ?? process.cwd();
|
|
13
16
|
if (!memoryExists(cwd)) {
|
|
@@ -125,7 +128,28 @@ function declareAgentIntegrationFromTarget(cwd, agentName, declarationSource) {
|
|
|
125
128
|
saveConfig(config, cwd);
|
|
126
129
|
}
|
|
127
130
|
}
|
|
131
|
+
function formatToAgentName(format) {
|
|
132
|
+
const map = {
|
|
133
|
+
'claude-md': 'claude-code',
|
|
134
|
+
'cursor-rules': 'cursor',
|
|
135
|
+
'copilot-instructions': 'github-copilot',
|
|
136
|
+
'agents-md': 'codex',
|
|
137
|
+
'gemini-md': 'antigravity',
|
|
138
|
+
'windsurf': 'windsurf',
|
|
139
|
+
'cline': 'cline',
|
|
140
|
+
'roo': 'roo',
|
|
141
|
+
'continue': 'continue',
|
|
142
|
+
};
|
|
143
|
+
return map[format];
|
|
144
|
+
}
|
|
128
145
|
function generateExport(format, options, cwd) {
|
|
146
|
+
const agentName = formatToAgentName(format);
|
|
147
|
+
if (agentName) {
|
|
148
|
+
const adaptive = generateAdaptiveExport(agentName, options, cwd);
|
|
149
|
+
if (adaptive)
|
|
150
|
+
return adaptive;
|
|
151
|
+
}
|
|
152
|
+
// Fallback to legacy generators for unknown formats
|
|
129
153
|
switch (format) {
|
|
130
154
|
case 'copilot-instructions': return generateCopilotInstructions(options, cwd);
|
|
131
155
|
case 'cursor-rules': return generateCursorRules(options, cwd);
|
|
@@ -140,6 +164,26 @@ function generateExport(format, options, cwd) {
|
|
|
140
164
|
throw new Error(`Unknown export format: ${format}`);
|
|
141
165
|
}
|
|
142
166
|
}
|
|
167
|
+
/**
|
|
168
|
+
* Generate export content using adaptive templates when a capability profile
|
|
169
|
+
* exists for the agent, falling back to the legacy per-format generators.
|
|
170
|
+
*/
|
|
171
|
+
function generateAdaptiveExport(agentName, options, cwd) {
|
|
172
|
+
const profile = getAgentCapabilityProfile(agentName);
|
|
173
|
+
if (!profile)
|
|
174
|
+
return undefined;
|
|
175
|
+
const state = loadState(cwd);
|
|
176
|
+
const instructions = getInstructionText(options, cwd);
|
|
177
|
+
const config = loadConfig(cwd);
|
|
178
|
+
const result = renderBrainclawSection({
|
|
179
|
+
profile,
|
|
180
|
+
state,
|
|
181
|
+
projectName: config.project_name,
|
|
182
|
+
brainclawVersion: getInstalledBrainclawVersion(),
|
|
183
|
+
resolvedInstructions: instructions,
|
|
184
|
+
});
|
|
185
|
+
return result.content;
|
|
186
|
+
}
|
|
143
187
|
function getInstructionText(options, cwd) {
|
|
144
188
|
try {
|
|
145
189
|
const all = loadInstructions(cwd);
|
package/dist/commands/init.js
CHANGED
|
@@ -14,17 +14,19 @@ 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 {
|
|
17
|
+
import { buildAiSurfaceInventory, renderAiSurfaceUsageHints } from '../core/ai-surface-inventory.js';
|
|
18
|
+
import { ensureUserStore, hasCompletedSetup } from '../core/setup-state.js';
|
|
18
19
|
import { writeDetectedAgentExport } from './export.js';
|
|
19
20
|
import { writeDetectedAgentHooks } from './hooks.js';
|
|
20
21
|
export async function runInit(options = {}) {
|
|
21
22
|
const cwd = options.cwd ?? process.cwd();
|
|
22
23
|
const containingMemoryStore = resolveContainingMemoryStore(cwd);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
// Auto-create user store if absent (replaces the old "setup required" guard).
|
|
25
|
+
// The skipSetupRequirement flag and BRAINCLAW_SKIP_SETUP_REQUIREMENT env var
|
|
26
|
+
// are kept for backward compatibility but are now effectively no-ops —
|
|
27
|
+
// init always ensures the user store exists before proceeding.
|
|
28
|
+
if (!hasCompletedSetup()) {
|
|
29
|
+
ensureUserStore();
|
|
28
30
|
}
|
|
29
31
|
if (containingMemoryStore) {
|
|
30
32
|
console.error(`Error: cannot run \`brainclaw init\` from inside an existing project memory store (${containingMemoryStore}).`);
|
|
@@ -178,6 +180,20 @@ export async function runInit(options = {}) {
|
|
|
178
180
|
if (detectedExport) {
|
|
179
181
|
console.log(`\u2714 Agent instructions written to ${detectedExport.relativePath} (${detectedExport.created ? 'created' : 'updated'})`);
|
|
180
182
|
}
|
|
183
|
+
const visibleSurfaces = buildAiSurfaceInventory().filter((surface) => surface.status !== 'not_detected');
|
|
184
|
+
if (visibleSurfaces.length > 0) {
|
|
185
|
+
console.log('✔ Other AI work surfaces detected on this machine:');
|
|
186
|
+
for (const surface of visibleSurfaces) {
|
|
187
|
+
console.log(` - ${surface.display_name} [${surface.surface_kind}, ${surface.status}]`);
|
|
188
|
+
}
|
|
189
|
+
const usageHints = renderAiSurfaceUsageHints(visibleSurfaces);
|
|
190
|
+
if (usageHints.length > 0) {
|
|
191
|
+
console.log(' Suggested uses:');
|
|
192
|
+
for (const line of usageHints) {
|
|
193
|
+
console.log(` ${line}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
181
197
|
for (const hook of detectedHooks) {
|
|
182
198
|
console.log(`\u2714 Session hook written to ${hook.relativePath} (${hook.created ? 'created' : 'updated'})`);
|
|
183
199
|
}
|
|
@@ -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
|
@@ -30,6 +30,8 @@ import { buildEstimationReport } from './estimation-report.js';
|
|
|
30
30
|
import { detectAiAgent } from '../core/ai-agent-detection.js';
|
|
31
31
|
import { checkGitPresence, scanGitRepos, parseRoots, parseRepoSelection, parseAgentSelection, runGlobalInstall, initReposAndConfigureAgents, readSetupState, ALL_KNOWN_AGENTS, } from './setup.js';
|
|
32
32
|
import { resolveTargetStore, resolveStoreChain } from '../core/store-resolution.js';
|
|
33
|
+
import { probeForQuickSetup, buildQuickSetupProbeResponse, buildOnboardingPreview } from '../core/setup-flow.js';
|
|
34
|
+
import { ensureUserStore } from '../core/setup-state.js';
|
|
33
35
|
import { readUnseenEvents, buildNotificationSummary } from '../core/event-log.js';
|
|
34
36
|
import { BootstrapInterviewAnswerSchema } from '../core/schema.js';
|
|
35
37
|
export const SCHEMA_VERSION = '0.6.0';
|
|
@@ -246,14 +248,17 @@ export const MCP_READ_TOOLS = [
|
|
|
246
248
|
const MCP_WRITE_TOOLS = [
|
|
247
249
|
{
|
|
248
250
|
name: 'bclaw_setup',
|
|
249
|
-
description: 'Interactive onboarding wizard
|
|
251
|
+
description: 'Interactive onboarding wizard. Two modes: (1) Quick mode (default): probes the current repo and asks project type + topology, then inits. (2) Batch mode: scan root directories and init multiple repos. Call without step to start — brainclaw auto-detects the best mode.',
|
|
250
252
|
inputSchema: {
|
|
251
253
|
type: 'object',
|
|
252
254
|
properties: {
|
|
253
|
-
step: { type: 'string', description: '
|
|
254
|
-
choice: { type: 'string', description: 'User choice for the current step
|
|
255
|
-
|
|
256
|
-
|
|
255
|
+
step: { type: 'string', description: 'Resume step: "quick_init" (quick mode), or "project_roots"/"repo_selection"/"agent_selection" (batch mode). Omit to start.' },
|
|
256
|
+
choice: { type: 'string', description: 'User choice for the current step.' },
|
|
257
|
+
project_type: { type: 'string', description: 'Quick mode: "standalone", "workspace", or "linked".' },
|
|
258
|
+
topology: { type: 'string', description: 'Quick mode: "embedded" (shared via git) or "sidecar" (local only).' },
|
|
259
|
+
roots: { type: 'string', description: 'Batch mode: comma-separated root paths.' },
|
|
260
|
+
repo_selection: { type: 'string', description: 'Batch mode: repo selection from previous step.' },
|
|
261
|
+
mode: { type: 'string', description: 'Force "quick" or "batch" mode. Default: auto-detect.' },
|
|
257
262
|
},
|
|
258
263
|
},
|
|
259
264
|
},
|
|
@@ -1112,6 +1117,16 @@ export function handleMcpReadToolCall(name, args = {}, context = {}) {
|
|
|
1112
1117
|
const text = args.interview
|
|
1113
1118
|
? renderBootstrapInterview(result, audience)
|
|
1114
1119
|
: renderBootstrapSummary(result);
|
|
1120
|
+
// Extract top-level suggested questions for conversational bootstrap (step 11)
|
|
1121
|
+
const suggestedQuestions = result.importPlan.interview?.questions?.map((q) => ({
|
|
1122
|
+
id: q.id,
|
|
1123
|
+
prompt: q.prompt,
|
|
1124
|
+
rationale: q.rationale,
|
|
1125
|
+
priority: q.priority,
|
|
1126
|
+
})) ?? [];
|
|
1127
|
+
// Separate auto-imports (high confidence) from proposals (need discussion)
|
|
1128
|
+
const autoImports = result.importPlan.suggestions.filter((s) => s.confidence === 'high');
|
|
1129
|
+
const proposals = result.importPlan.suggestions.filter((s) => s.confidence !== 'high');
|
|
1115
1130
|
return {
|
|
1116
1131
|
content: [{ type: 'text', text }],
|
|
1117
1132
|
structuredContent: {
|
|
@@ -1127,6 +1142,9 @@ export function handleMcpReadToolCall(name, args = {}, context = {}) {
|
|
|
1127
1142
|
seed_count: result.profile.seed_count,
|
|
1128
1143
|
seeds: result.seeds,
|
|
1129
1144
|
import_plan: result.importPlan,
|
|
1145
|
+
auto_imports: autoImports,
|
|
1146
|
+
proposals,
|
|
1147
|
+
suggested_questions: suggestedQuestions,
|
|
1130
1148
|
last_application: result.lastApplication,
|
|
1131
1149
|
reused_profile: result.reusedProfile,
|
|
1132
1150
|
},
|
|
@@ -1571,15 +1589,78 @@ export async function executeMcpToolCall(payload) {
|
|
|
1571
1589
|
const choice = args.choice ?? '';
|
|
1572
1590
|
const rootsArg = args.roots;
|
|
1573
1591
|
const repoSelectionArg = args.repo_selection;
|
|
1592
|
+
const modeArg = args.mode;
|
|
1574
1593
|
const env = process.env;
|
|
1575
1594
|
if (!checkGitPresence()) {
|
|
1576
1595
|
return { response: toolResponse({ content: [{ type: 'text', text: 'Git is not installed or not found in PATH. Install git from https://git-scm.com before running brainclaw setup.' }], structuredContent: { error: 'git_not_found' } }, true) };
|
|
1577
1596
|
}
|
|
1597
|
+
// ─── Quick mode: probe current repo ──────────────────────────────
|
|
1578
1598
|
if (!step) {
|
|
1599
|
+
// Auto-detect mode: if we're in a git repo, use quick mode unless batch is forced
|
|
1600
|
+
const forceBatch = modeArg === 'batch';
|
|
1601
|
+
if (!forceBatch) {
|
|
1602
|
+
const probe = probeForQuickSetup(cwd);
|
|
1603
|
+
if (probe.isGitRepo || probe.alreadyInitialized) {
|
|
1604
|
+
const response = buildQuickSetupProbeResponse(probe);
|
|
1605
|
+
return { response: toolResponse({ content: [{ type: 'text', text: response.text }], structuredContent: response.structured }) };
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
// Fall through to batch mode
|
|
1579
1609
|
const existingState = readSetupState(env);
|
|
1580
1610
|
const alreadyRun = existingState ? `Setup was previously run on ${new Date(existingState.completed_at).toLocaleDateString()}. You can re-run it.` : undefined;
|
|
1581
1611
|
return { response: toolResponse({ content: [{ type: 'text', text: [alreadyRun, "Where are the user's project directories? Please ask the user to provide one or more root paths where their git repositories are located (e.g. ~/Projects, C:\\Users\\user\\code)."].filter(Boolean).join('\n\n') }], structuredContent: { pending_question: 'project_roots', prompt: 'Please ask the user: "Where are your projects? Enter one or more root directories (comma-separated):"', ...(alreadyRun ? { already_run: alreadyRun } : {}) } }) };
|
|
1582
1612
|
}
|
|
1613
|
+
// ─── Quick mode step: init with choices ──────────────────────────
|
|
1614
|
+
if (step === 'quick_init') {
|
|
1615
|
+
const projectType = args.project_type ?? 'standalone';
|
|
1616
|
+
const topology = args.topology ?? 'embedded';
|
|
1617
|
+
// Ensure user store exists
|
|
1618
|
+
ensureUserStore(env);
|
|
1619
|
+
// Map choices to init options
|
|
1620
|
+
const projectMode = projectType === 'workspace' ? 'multi-project' : 'auto';
|
|
1621
|
+
const topologyMode = topology === 'sidecar' ? 'sidecar' : 'embedded';
|
|
1622
|
+
// Run init
|
|
1623
|
+
try {
|
|
1624
|
+
const { runInit } = await import('./init.js');
|
|
1625
|
+
await runInit({
|
|
1626
|
+
yes: true,
|
|
1627
|
+
cwd,
|
|
1628
|
+
skipAgentBootstrap: false,
|
|
1629
|
+
projectMode,
|
|
1630
|
+
topology: topologyMode,
|
|
1631
|
+
});
|
|
1632
|
+
}
|
|
1633
|
+
catch (err) {
|
|
1634
|
+
return { response: toolResponse({ content: [{ type: 'text', text: `Init failed: ${err instanceof Error ? err.message : String(err)}` }], structuredContent: { error: 'init_failed', details: err instanceof Error ? err.message : String(err) } }, true) };
|
|
1635
|
+
}
|
|
1636
|
+
// Detect agent and report
|
|
1637
|
+
const detected = detectAiAgent(env);
|
|
1638
|
+
const summary = [
|
|
1639
|
+
`✔ Initialized ${cwd.split(/[\\/]/).pop() ?? cwd} (${projectType}, ${topology})`,
|
|
1640
|
+
];
|
|
1641
|
+
if (detected) {
|
|
1642
|
+
summary.push(`✔ Agent detected: ${detected.name}`);
|
|
1643
|
+
}
|
|
1644
|
+
summary.push('✔ Reload your agent session to activate brainclaw MCP tools.');
|
|
1645
|
+
// Check if bootstrap is available and generate preview
|
|
1646
|
+
const probe = probeForQuickSetup(cwd);
|
|
1647
|
+
const bootstrapAvailable = probe.hasContent;
|
|
1648
|
+
const preview = buildOnboardingPreview(cwd);
|
|
1649
|
+
return {
|
|
1650
|
+
response: toolResponse({
|
|
1651
|
+
content: [{ type: 'text', text: summary.join('\n') + (bootstrapAvailable ? '\n\nThe repo has existing content. Run bclaw_bootstrap to extract initial project context.' : '') + '\n\n' + preview }],
|
|
1652
|
+
structuredContent: {
|
|
1653
|
+
setup_complete: true,
|
|
1654
|
+
project_type: projectType,
|
|
1655
|
+
topology,
|
|
1656
|
+
detected_agent: detected?.name ?? null,
|
|
1657
|
+
bootstrap_available: bootstrapAvailable,
|
|
1658
|
+
preview,
|
|
1659
|
+
summary,
|
|
1660
|
+
},
|
|
1661
|
+
}),
|
|
1662
|
+
};
|
|
1663
|
+
}
|
|
1583
1664
|
if (step === 'project_roots') {
|
|
1584
1665
|
const roots = parseRoots(choice, env);
|
|
1585
1666
|
if (roots.length === 0) {
|