brainclaw 0.19.10 → 0.19.12

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 CHANGED
@@ -129,6 +129,7 @@ After that, the agent should stay on Brainclaw's MCP path for live state:
129
129
 
130
130
  ```text
131
131
  bclaw_session_start -> open session + return board/context
132
+ bclaw_get_execution_context -> inspect local tooling + notice Brainclaw package updates
132
133
  bclaw_get_context -> fetch fresh prompt-ready context for a path
133
134
  bclaw_list_plans -> inspect shared work
134
135
  bclaw_claim -> claim scope before editing
@@ -177,6 +178,8 @@ If you want a machine-level CLI for operator workflows, debugging, or repeated l
177
178
  npm install -g brainclaw
178
179
  ```
179
180
 
181
+ 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.
182
+
180
183
  If you are working from source while developing Brainclaw itself:
181
184
 
182
185
  ```bash
package/dist/cli.js CHANGED
@@ -515,6 +515,7 @@ program
515
515
  .option('--refresh', 'Force a fresh bootstrap scan instead of reusing the current profile')
516
516
  .option('--interview', 'Render the adaptive interview prompts instead of the bootstrap summary')
517
517
  .option('--audience <audience>', 'Target interview prompts for cli, ide_chat, or any')
518
+ .option('--answers-file <path>', 'Load structured bootstrap interview answers from a JSON file')
518
519
  .option('--apply', 'Import the current bootstrap proposal into canonical memory')
519
520
  .option('--uninstall', 'Deactivate the last bootstrap import managed by this workspace')
520
521
  .option('-y, --yes', 'Skip confirmation prompts for apply/uninstall')
@@ -1,7 +1,9 @@
1
+ import fs from 'node:fs';
1
2
  import readline from 'node:readline/promises';
2
3
  import { stdin as input, stdout as output } from 'node:process';
3
4
  import { memoryExists } from '../core/io.js';
4
5
  import { applyBootstrapImport, renderBootstrapInterview, renderBootstrapSummary, runBootstrapProfile, uninstallBootstrapImport, } from '../core/bootstrap.js';
6
+ import { BootstrapInterviewAnswerSchema } from '../core/schema.js';
5
7
  export async function runBootstrap(options = {}) {
6
8
  const cwd = options.cwd ?? process.cwd();
7
9
  if (!memoryExists(cwd)) {
@@ -14,6 +16,7 @@ export async function runBootstrap(options = {}) {
14
16
  process.exit(1);
15
17
  }
16
18
  const audience = resolveBootstrapInterviewAudience(options.audience);
19
+ const interviewAnswers = loadBootstrapInterviewAnswers(options.answersFile);
17
20
  if (options.uninstall) {
18
21
  await confirmBootstrapAction('Remove the last bootstrap import?', options.yes);
19
22
  const result = uninstallBootstrapImport(cwd);
@@ -21,7 +24,7 @@ export async function runBootstrap(options = {}) {
21
24
  console.log('No bootstrap import receipt found.');
22
25
  return;
23
26
  }
24
- console.log(`✔ Bootstrap uninstall completed: ${result.deactivatedCount} instruction(s) deactivated, ${result.skippedCount} artifact(s) skipped.`);
27
+ console.log(`✔ Bootstrap uninstall completed: ${result.deactivatedCount} instruction(s) deactivated, ${result.deletedCount} artifact(s) deleted, ${result.skippedCount} artifact(s) skipped.`);
25
28
  return;
26
29
  }
27
30
  if (options.apply) {
@@ -29,6 +32,7 @@ export async function runBootstrap(options = {}) {
29
32
  const result = applyBootstrapImport({
30
33
  target: options.for,
31
34
  refresh: options.refresh,
35
+ interviewAnswers,
32
36
  cwd,
33
37
  });
34
38
  console.log(`✔ Bootstrap import applied: ${result.createdCount} item(s) created, ${result.skippedCount} suggestion(s) skipped.`);
@@ -40,6 +44,7 @@ export async function runBootstrap(options = {}) {
40
44
  const result = runBootstrapProfile({
41
45
  target: options.for,
42
46
  refresh: options.refresh,
47
+ interviewAnswers,
43
48
  cwd,
44
49
  });
45
50
  if (options.json) {
@@ -76,6 +81,17 @@ function resolveBootstrapInterviewAudience(value) {
76
81
  }
77
82
  throw new Error(`Unsupported bootstrap interview audience '${value}'. Use cli, ide_chat, or any.`);
78
83
  }
84
+ function loadBootstrapInterviewAnswers(filepath) {
85
+ if (!filepath) {
86
+ return [];
87
+ }
88
+ const raw = fs.readFileSync(filepath, 'utf-8');
89
+ const parsed = JSON.parse(raw);
90
+ if (!Array.isArray(parsed)) {
91
+ throw new Error(`Bootstrap interview answers file must contain a JSON array: ${filepath}`);
92
+ }
93
+ return parsed.map((entry) => BootstrapInterviewAnswerSchema.parse(entry));
94
+ }
79
95
  async function confirmBootstrapAction(question, yes) {
80
96
  if (yes) {
81
97
  return;
@@ -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) {
@@ -243,6 +243,7 @@ export async function runInit(options = {}) {
243
243
  if ((onboardingPreflight.importPlan.interview?.question_count ?? 0) > 0) {
244
244
  console.log('');
245
245
  console.log(`Interview: run 'brainclaw bootstrap --interview --audience cli' for terminal agents or '--audience ide_chat' for IDE chat agents.`);
246
+ console.log(`Apply confirmed answers: write a JSON answers file and run 'brainclaw bootstrap --answers-file <path> --apply'.`);
246
247
  }
247
248
  else if ((onboardingPreflight.profile.gaps?.length ?? 0) > 0) {
248
249
  console.log('');
@@ -2,11 +2,12 @@ import readline from 'node:readline';
2
2
  import { Worker } from 'node:worker_threads';
3
3
  import { getTriggeredItems, renderTriggeredItems } from '../core/lifecycle.js';
4
4
  import { resolveCrossProjectTarget, writeCrossProjectNote } from '../core/cross-project.js';
5
- import { renderBootstrapSummary, runBootstrapProfile } from '../core/bootstrap.js';
5
+ import { applyBootstrapImport, renderBootstrapInterview, renderBootstrapSummary, runBootstrapProfile, uninstallBootstrapImport } from '../core/bootstrap.js';
6
6
  import { buildAgentToolingContext, renderAgentToolingSummary } from '../core/agent-context.js';
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';
@@ -30,6 +31,7 @@ import { detectAiAgent } from '../core/ai-agent-detection.js';
30
31
  import { checkGitPresence, scanGitRepos, parseRoots, parseRepoSelection, parseAgentSelection, runGlobalInstall, initReposAndConfigureAgents, readSetupState, ALL_KNOWN_AGENTS, } from './setup.js';
31
32
  import { resolveTargetStore, resolveStoreChain } from '../core/store-resolution.js';
32
33
  import { readUnseenEvents, buildNotificationSummary } from '../core/event-log.js';
34
+ import { BootstrapInterviewAnswerSchema } from '../core/schema.js';
33
35
  export const SCHEMA_VERSION = '0.6.0';
34
36
  export const MCP_PROTOCOL_VERSIONS = ['2025-11-25', '2024-11-05'];
35
37
  export const MCP_SERVER_NOT_INITIALIZED = -32002;
@@ -67,12 +69,21 @@ export const MCP_READ_TOOLS = [
67
69
  properties: {
68
70
  target: { type: 'string', description: 'Optional path or scope to tailor the bootstrap.' },
69
71
  refresh: { type: 'boolean', description: 'Force a fresh bootstrap scan.' },
72
+ audience: { type: 'string', description: 'Optional interview audience filter: cli, ide_chat, or any.' },
73
+ interview: { type: 'boolean', description: 'Render interview text instead of the summary text.' },
74
+ apply: { type: 'boolean', description: 'Apply the current import proposal into canonical memory.' },
75
+ uninstall: { type: 'boolean', description: 'Uninstall the last bootstrap-managed import.' },
76
+ interviewAnswers: {
77
+ type: 'array',
78
+ description: 'Optional structured interview answers. Each answer may include question_id, response_text, response_items, response_boolean, and explicit suggestions.',
79
+ items: { type: 'object' },
80
+ },
70
81
  },
71
82
  },
72
83
  },
73
84
  {
74
85
  name: 'bclaw_get_execution_context',
75
- 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.',
76
87
  inputSchema: {
77
88
  type: 'object',
78
89
  properties: {
@@ -506,6 +517,18 @@ export function createToolErrorResponse(kind, message, details) {
506
517
  },
507
518
  }, true);
508
519
  }
520
+ function normalizeBootstrapInterviewAnswersArg(value) {
521
+ if (!Array.isArray(value)) {
522
+ return [];
523
+ }
524
+ return value.map((entry) => BootstrapInterviewAnswerSchema.parse(entry));
525
+ }
526
+ function normalizeBootstrapInterviewAudienceArg(value) {
527
+ if (value === 'cli' || value === 'ide_chat' || value === 'any') {
528
+ return value;
529
+ }
530
+ return 'any';
531
+ }
509
532
  function requireObjectParams(params, id) {
510
533
  if (params === undefined) {
511
534
  return {};
@@ -1040,19 +1063,64 @@ export function handleMcpReadToolCall(name, args = {}, context = {}) {
1040
1063
  };
1041
1064
  }
1042
1065
  if (name === 'bclaw_bootstrap') {
1066
+ const interviewAnswers = normalizeBootstrapInterviewAnswersArg(args.interviewAnswers);
1067
+ if (args.apply && args.uninstall) {
1068
+ throw new Error('bclaw_bootstrap does not allow apply and uninstall at the same time.');
1069
+ }
1070
+ if (args.uninstall) {
1071
+ const result = uninstallBootstrapImport(cwd);
1072
+ const text = !result.receipt
1073
+ ? 'No bootstrap import receipt found.'
1074
+ : `Bootstrap uninstall completed: ${result.deactivatedCount} instruction(s) deactivated, ${result.deletedCount} artifact(s) deleted, ${result.skippedCount} artifact(s) skipped.`;
1075
+ return {
1076
+ content: [{ type: 'text', text }],
1077
+ structuredContent: {
1078
+ receipt: result.receipt,
1079
+ deactivated_count: result.deactivatedCount,
1080
+ deleted_count: result.deletedCount,
1081
+ skipped_count: result.skippedCount,
1082
+ },
1083
+ };
1084
+ }
1085
+ if (args.apply) {
1086
+ const applied = applyBootstrapImport({
1087
+ target: args.target,
1088
+ refresh: args.refresh,
1089
+ interviewAnswers,
1090
+ cwd,
1091
+ });
1092
+ return {
1093
+ content: [{
1094
+ type: 'text',
1095
+ text: `Bootstrap import applied: ${applied.createdCount} item(s) created, ${applied.skippedCount} suggestion(s) skipped.`,
1096
+ }],
1097
+ structuredContent: {
1098
+ created_count: applied.createdCount,
1099
+ skipped_count: applied.skippedCount,
1100
+ receipt: applied.receipt,
1101
+ import_plan: applied.proposal,
1102
+ },
1103
+ };
1104
+ }
1043
1105
  const result = runBootstrapProfile({
1044
1106
  target: args.target,
1045
1107
  refresh: args.refresh,
1108
+ interviewAnswers,
1046
1109
  cwd,
1047
1110
  });
1111
+ const audience = normalizeBootstrapInterviewAudienceArg(args.audience);
1112
+ const text = args.interview
1113
+ ? renderBootstrapInterview(result, audience)
1114
+ : renderBootstrapSummary(result);
1048
1115
  return {
1049
- content: [{ type: 'text', text: renderBootstrapSummary(result) }],
1116
+ content: [{ type: 'text', text }],
1050
1117
  structuredContent: {
1051
1118
  summary: result.profile.summary,
1052
1119
  target: result.profile.target,
1053
1120
  repo_fingerprint: result.profile.repo_fingerprint,
1054
1121
  sources_scanned: result.profile.sources_scanned,
1055
1122
  workspace_kind: result.profile.workspace_kind,
1123
+ onboarding_mode: result.profile.onboarding_mode,
1056
1124
  confidence: result.profile.confidence,
1057
1125
  native_instruction_files: result.profile.native_instruction_files,
1058
1126
  gaps: result.profile.gaps,
@@ -1066,15 +1134,20 @@ export function handleMcpReadToolCall(name, args = {}, context = {}) {
1066
1134
  }
1067
1135
  if (name === 'bclaw_get_execution_context') {
1068
1136
  const executionContext = buildExecutionContext({ cwd });
1137
+ const config = loadConfig(cwd);
1138
+ const installableUpdate = checkBrainclawInstallableUpdate(config, cwd, { useDefaultNpmSource: true });
1139
+ const installableUpdateNotice = renderBrainclawInstallableUpdateNotice(installableUpdate);
1069
1140
  const agentTooling = args.includeAgentTooling ? buildAgentToolingContext({ cwd }) : undefined;
1070
1141
  const text = [
1071
1142
  renderExecutionContextSummary(executionContext, true),
1143
+ ...(installableUpdateNotice ? ['', installableUpdateNotice] : []),
1072
1144
  ...(agentTooling ? ['', renderAgentToolingSummary(agentTooling)] : []),
1073
1145
  ].join('\n');
1074
1146
  return {
1075
1147
  content: [{ type: 'text', text }],
1076
1148
  structuredContent: {
1077
1149
  execution_context: executionContext,
1150
+ installable_update: installableUpdate,
1078
1151
  ...(agentTooling ? { agent_tooling: agentTooling } : {}),
1079
1152
  },
1080
1153
  };
@@ -31,7 +31,9 @@ export function runVersion(options = {}) {
31
31
  }
32
32
  }
33
33
  const assessment = assessBrainclawVersion(config);
34
- const updateCheck = options.check ? checkBrainclawInstallableUpdate(config, cwd) : undefined;
34
+ const updateCheck = options.check
35
+ ? checkBrainclawInstallableUpdate(config, cwd, { useDefaultNpmSource: true })
36
+ : undefined;
35
37
  const result = {
36
38
  initialized,
37
39
  ...assessment,
@@ -4,7 +4,7 @@ import { loadConfig } from '../core/config.js';
4
4
  import { resolveCurrentHostId } from '../core/host.js';
5
5
  import { buildOperationalIdentity } from '../core/identity.js';
6
6
  import { assessAgentIntegrationReadiness } from '../core/agent-integrations.js';
7
- import { assessBrainclawVersion } from '../core/brainclaw-version.js';
7
+ import { assessBrainclawVersion, checkBrainclawInstallableUpdate, renderBrainclawInstallableUpdateNotice, } from '../core/brainclaw-version.js';
8
8
  import { buildExecutionContext, compactExecutionContext } from '../core/execution-context.js';
9
9
  import { buildAgentToolingContext } from '../core/agent-context.js';
10
10
  export function runWhoami(options = {}) {
@@ -18,6 +18,8 @@ export function runWhoami(options = {}) {
18
18
  const executionContext = compactExecutionContext(buildExecutionContext({ cwd }));
19
19
  const integrationReadiness = assessAgentIntegrationReadiness(config, cwd);
20
20
  const brainclawVersion = assessBrainclawVersion(config);
21
+ const installableUpdate = checkBrainclawInstallableUpdate(config, cwd, { useDefaultNpmSource: true });
22
+ const installableUpdateNotice = renderBrainclawInstallableUpdateNotice(installableUpdate);
21
23
  const agentTooling = buildAgentToolingContext({ cwd });
22
24
  let identity;
23
25
  try {
@@ -45,6 +47,7 @@ export function runWhoami(options = {}) {
45
47
  env_session: process.env.BRAINCLAW_SESSION_ID ?? null,
46
48
  env_host: process.env.BRAINCLAW_HOST_ID ?? null,
47
49
  brainclaw_version: brainclawVersion,
50
+ installable_update: installableUpdate,
48
51
  declared_agent_integrations: config.agent_integrations,
49
52
  integration_readiness: integrationReadiness,
50
53
  execution_context: executionContext,
@@ -80,6 +83,13 @@ export function runWhoami(options = {}) {
80
83
  if (result.brainclaw_version.status !== 'ok') {
81
84
  console.log(` Version : ${result.brainclaw_version.message}`);
82
85
  }
86
+ if (installableUpdateNotice) {
87
+ const lines = installableUpdateNotice.split('\n');
88
+ console.log(` Update : ${lines[0]}`);
89
+ for (const line of lines.slice(1)) {
90
+ console.log(` ${line}`);
91
+ }
92
+ }
83
93
  console.log(` Declared integrations: ${result.declared_agent_integrations.declarations.length}`);
84
94
  const missingIntegrations = result.integration_readiness.filter((entry) => !entry.ready);
85
95
  if (missingIntegrations.length > 0) {