brainclaw 0.19.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/LICENSE +74 -0
  2. package/README.md +226 -0
  3. package/dist/cli.js +1037 -0
  4. package/dist/commands/accept.js +149 -0
  5. package/dist/commands/adapter-openclaw-import.js +75 -0
  6. package/dist/commands/add-step.js +35 -0
  7. package/dist/commands/agent-board.js +106 -0
  8. package/dist/commands/audit.js +35 -0
  9. package/dist/commands/bootstrap.js +34 -0
  10. package/dist/commands/capability.js +104 -0
  11. package/dist/commands/changes.js +112 -0
  12. package/dist/commands/check-constraints.js +63 -0
  13. package/dist/commands/claim-resource.js +54 -0
  14. package/dist/commands/claim.js +92 -0
  15. package/dist/commands/complete-step.js +34 -0
  16. package/dist/commands/constraint.js +44 -0
  17. package/dist/commands/context-diff.js +32 -0
  18. package/dist/commands/context.js +63 -0
  19. package/dist/commands/decision.js +45 -0
  20. package/dist/commands/delete-plan.js +20 -0
  21. package/dist/commands/diff.js +99 -0
  22. package/dist/commands/doctor.js +1275 -0
  23. package/dist/commands/enable-agent.js +63 -0
  24. package/dist/commands/env.js +46 -0
  25. package/dist/commands/estimation-report.js +167 -0
  26. package/dist/commands/explore.js +47 -0
  27. package/dist/commands/export.js +381 -0
  28. package/dist/commands/handoff.js +63 -0
  29. package/dist/commands/history.js +22 -0
  30. package/dist/commands/hooks.js +123 -0
  31. package/dist/commands/init.js +356 -0
  32. package/dist/commands/install-hooks.js +115 -0
  33. package/dist/commands/instruction.js +56 -0
  34. package/dist/commands/list-agents.js +44 -0
  35. package/dist/commands/list-claims.js +45 -0
  36. package/dist/commands/list-instructions.js +50 -0
  37. package/dist/commands/list-plans.js +48 -0
  38. package/dist/commands/mcp-worker.js +12 -0
  39. package/dist/commands/mcp.js +2272 -0
  40. package/dist/commands/memory.js +283 -0
  41. package/dist/commands/metrics.js +175 -0
  42. package/dist/commands/plan-resource.js +62 -0
  43. package/dist/commands/plan.js +76 -0
  44. package/dist/commands/prune-candidates.js +36 -0
  45. package/dist/commands/prune.js +48 -0
  46. package/dist/commands/pull.js +25 -0
  47. package/dist/commands/push.js +28 -0
  48. package/dist/commands/rebuild.js +14 -0
  49. package/dist/commands/reflect-runtime-note.js +74 -0
  50. package/dist/commands/reflect.js +286 -0
  51. package/dist/commands/register-agent.js +29 -0
  52. package/dist/commands/reject.js +52 -0
  53. package/dist/commands/release-claim.js +41 -0
  54. package/dist/commands/release-claims.js +67 -0
  55. package/dist/commands/review.js +242 -0
  56. package/dist/commands/rollback.js +156 -0
  57. package/dist/commands/runtime-note.js +144 -0
  58. package/dist/commands/runtime-status.js +49 -0
  59. package/dist/commands/search.js +36 -0
  60. package/dist/commands/session-end.js +187 -0
  61. package/dist/commands/session-start.js +147 -0
  62. package/dist/commands/set-trust.js +92 -0
  63. package/dist/commands/setup.js +446 -0
  64. package/dist/commands/show-candidate.js +31 -0
  65. package/dist/commands/star-candidate.js +28 -0
  66. package/dist/commands/status.js +133 -0
  67. package/dist/commands/sync.js +159 -0
  68. package/dist/commands/tool.js +126 -0
  69. package/dist/commands/trap.js +74 -0
  70. package/dist/commands/update-handoff.js +23 -0
  71. package/dist/commands/update-plan.js +37 -0
  72. package/dist/commands/upgrade.js +382 -0
  73. package/dist/commands/use-candidate.js +35 -0
  74. package/dist/commands/version.js +96 -0
  75. package/dist/commands/watch.js +215 -0
  76. package/dist/commands/whoami.js +104 -0
  77. package/dist/core/agent-context.js +340 -0
  78. package/dist/core/agent-files.js +874 -0
  79. package/dist/core/agent-integrations.js +135 -0
  80. package/dist/core/agent-inventory.js +401 -0
  81. package/dist/core/agent-registry.js +420 -0
  82. package/dist/core/ai-agent-detection.js +140 -0
  83. package/dist/core/audit.js +85 -0
  84. package/dist/core/bootstrap.js +658 -0
  85. package/dist/core/brainclaw-version.js +433 -0
  86. package/dist/core/candidates.js +137 -0
  87. package/dist/core/circuit-breaker.js +118 -0
  88. package/dist/core/claims.js +72 -0
  89. package/dist/core/config.js +86 -0
  90. package/dist/core/context-diff.js +122 -0
  91. package/dist/core/context.js +1212 -0
  92. package/dist/core/contradictions.js +270 -0
  93. package/dist/core/coordination.js +86 -0
  94. package/dist/core/cross-project.js +99 -0
  95. package/dist/core/duplicates.js +72 -0
  96. package/dist/core/event-log.js +152 -0
  97. package/dist/core/events.js +56 -0
  98. package/dist/core/execution-context.js +204 -0
  99. package/dist/core/freshness.js +87 -0
  100. package/dist/core/global-registry.js +182 -0
  101. package/dist/core/host.js +10 -0
  102. package/dist/core/identity.js +151 -0
  103. package/dist/core/ids.js +56 -0
  104. package/dist/core/input-validation.js +81 -0
  105. package/dist/core/instructions.js +117 -0
  106. package/dist/core/io.js +191 -0
  107. package/dist/core/json-store.js +63 -0
  108. package/dist/core/lifecycle.js +45 -0
  109. package/dist/core/lock.js +129 -0
  110. package/dist/core/logger.js +49 -0
  111. package/dist/core/machine-profile.js +332 -0
  112. package/dist/core/markdown.js +120 -0
  113. package/dist/core/memory-git.js +133 -0
  114. package/dist/core/migration.js +247 -0
  115. package/dist/core/project-registry.js +64 -0
  116. package/dist/core/reflection-safety.js +21 -0
  117. package/dist/core/repo-analysis.js +133 -0
  118. package/dist/core/reputation.js +409 -0
  119. package/dist/core/runtime.js +134 -0
  120. package/dist/core/schema.js +580 -0
  121. package/dist/core/search.js +115 -0
  122. package/dist/core/security.js +66 -0
  123. package/dist/core/setup-state.js +50 -0
  124. package/dist/core/state.js +83 -0
  125. package/dist/core/store-resolution.js +119 -0
  126. package/dist/core/sync-remote.js +83 -0
  127. package/dist/core/traps.js +86 -0
  128. package/package.json +60 -0
@@ -0,0 +1,658 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { spawnSync } from 'node:child_process';
4
+ import { JsonStore } from './json-store.js';
5
+ import { generateId, nowISO } from './ids.js';
6
+ import { resolveEntityDir } from './io.js';
7
+ import { BootstrapProfileDocumentSchema, MemorySeedDocumentSchema, } from './schema.js';
8
+ import { loadVersionedJsonFile, saveVersionedJsonFile } from './migration.js';
9
+ import { analyzeRepository } from './repo-analysis.js';
10
+ import { buildExecutionContext, compactExecutionContext } from './execution-context.js';
11
+ import { buildAgentToolingContext } from './agent-context.js';
12
+ const README_CANDIDATES = ['README.md', 'README', 'README.txt', 'README.mdx'];
13
+ const DOC_HINTS = ['docs', 'doc'];
14
+ const MAKEFILE_NAME = 'Makefile';
15
+ const PROFILE_FILE = 'profile.json';
16
+ const HOTSPOT_LIMIT = 3;
17
+ const DERIVED_SCHEMA_VERSION = 2;
18
+ export function runBootstrapProfile(options = {}) {
19
+ const cwd = options.cwd ?? process.cwd();
20
+ const target = normalizeTarget(options.target);
21
+ const existing = loadBootstrapProfile(cwd);
22
+ const existingFingerprint = currentRepoFingerprint(cwd);
23
+ if (!options.refresh && existing && isProfileReusable(existing, target, existingFingerprint)) {
24
+ return {
25
+ profile: existing,
26
+ seeds: listBootstrapSeeds(cwd),
27
+ reusedProfile: true,
28
+ };
29
+ }
30
+ const artifacts = buildBootstrapArtifacts({ cwd, target, repoFingerprint: existingFingerprint });
31
+ persistBootstrapArtifacts(artifacts, cwd);
32
+ return {
33
+ profile: artifacts.profile,
34
+ seeds: artifacts.seeds,
35
+ reusedProfile: false,
36
+ };
37
+ }
38
+ export function listBootstrapSeeds(cwd) {
39
+ return bootstrapSeedStore(cwd).list();
40
+ }
41
+ export function loadBootstrapProfile(cwd) {
42
+ const filepath = bootstrapProfilePath(cwd);
43
+ if (!fs.existsSync(filepath)) {
44
+ return undefined;
45
+ }
46
+ try {
47
+ return loadVersionedJsonFile('bootstrap_profile', filepath).document;
48
+ }
49
+ catch {
50
+ return undefined;
51
+ }
52
+ }
53
+ export function hasReusableBootstrapProfile(target, cwd) {
54
+ const profile = loadBootstrapProfile(cwd);
55
+ if (!profile) {
56
+ return false;
57
+ }
58
+ return isProfileReusable(profile, normalizeTarget(target), currentRepoFingerprint(cwd ?? process.cwd()));
59
+ }
60
+ export function selectDerivedSignals(target, maxSignals, cwd) {
61
+ const normalizedTarget = normalizeTarget(target);
62
+ const seeds = listBootstrapSeeds(cwd);
63
+ const ranked = seeds
64
+ .map((seed) => ({ seed, score: scoreSeed(seed, normalizedTarget) }))
65
+ .filter((entry) => entry.score > 0)
66
+ .sort((left, right) => {
67
+ if (right.score !== left.score)
68
+ return right.score - left.score;
69
+ return right.seed.derived_at.localeCompare(left.seed.derived_at);
70
+ })
71
+ .slice(0, maxSignals)
72
+ .map((entry) => ({
73
+ id: entry.seed.id,
74
+ text: entry.seed.text,
75
+ seed_kind: entry.seed.seed_kind,
76
+ source_kind: entry.seed.source_kind,
77
+ source_ref: entry.seed.source_ref,
78
+ confidence: entry.seed.confidence,
79
+ ...(entry.seed.related_paths ? { related_paths: entry.seed.related_paths } : {}),
80
+ }));
81
+ return ranked;
82
+ }
83
+ export function renderBootstrapSummary(result) {
84
+ const lines = [result.profile.summary];
85
+ lines.push(`Sources scanned: ${result.profile.sources_scanned.join(', ') || 'none'}`);
86
+ lines.push(`Seed count: ${result.profile.seed_count}`);
87
+ if (result.profile.repo_fingerprint) {
88
+ lines.push(`Repo fingerprint: ${result.profile.repo_fingerprint}`);
89
+ }
90
+ if (result.profile.target) {
91
+ lines.push(`Target: ${result.profile.target}`);
92
+ }
93
+ if (result.reusedProfile) {
94
+ lines.push('Reused existing bootstrap profile.');
95
+ }
96
+ if (result.seeds.length > 0) {
97
+ lines.push('');
98
+ lines.push('Derived signals:');
99
+ for (const seed of result.seeds.slice(0, 10)) {
100
+ lines.push(`- [${seed.seed_kind}/${seed.confidence}] ${seed.text}`);
101
+ }
102
+ }
103
+ return lines.join('\n');
104
+ }
105
+ function buildBootstrapArtifacts(input) {
106
+ const sourcesScanned = [];
107
+ const seeds = [];
108
+ const readmePath = findFirstExisting(input.cwd, README_CANDIDATES);
109
+ if (readmePath) {
110
+ sourcesScanned.push('README');
111
+ seeds.push(...extractReadmeSeeds(readmePath, input.target));
112
+ }
113
+ const agentsPath = path.join(input.cwd, 'AGENTS.md');
114
+ const agentsPresent = fs.existsSync(agentsPath);
115
+ if (agentsPresent) {
116
+ sourcesScanned.push('AGENTS.md');
117
+ seeds.push(...extractAgentsSeeds(agentsPath, input.target));
118
+ }
119
+ const manifestResult = extractManifestSeeds(input.cwd, input.target);
120
+ if (manifestResult.seeds.length > 0) {
121
+ sourcesScanned.push(...manifestResult.sources);
122
+ seeds.push(...manifestResult.seeds);
123
+ }
124
+ const executionContext = compactExecutionContext(buildExecutionContext({ cwd: input.cwd }));
125
+ sourcesScanned.push('execution_context');
126
+ seeds.push(...extractExecutionContextSeeds(executionContext, input.target));
127
+ const agentTooling = buildAgentToolingContext({ cwd: input.cwd });
128
+ if (agentTooling.skills.length > 0) {
129
+ sourcesScanned.push('skills');
130
+ seeds.push(...extractSkillSeeds(agentTooling.skills, input.target));
131
+ }
132
+ if (agentTooling.mcp_servers.length > 0) {
133
+ sourcesScanned.push('local_mcp');
134
+ seeds.push(...extractMcpSeeds(agentTooling.mcp_servers, input.target));
135
+ }
136
+ const repoAnalysis = analyzeRepository(input.cwd);
137
+ sourcesScanned.push('repo-analysis');
138
+ seeds.push(...extractRepoAnalysisSeeds(repoAnalysis, input.target));
139
+ const gitProbe = probeGit(input.cwd, input.target);
140
+ if (gitProbe.available) {
141
+ sourcesScanned.push('git');
142
+ seeds.push(...gitProbe.hotspotSeeds);
143
+ }
144
+ const uniqueSeeds = dedupeSeeds(seeds);
145
+ const summary = buildSummary({
146
+ agentsPresent,
147
+ gitAvailable: gitProbe.available,
148
+ repoAnalysis,
149
+ seeds: uniqueSeeds,
150
+ target: input.target,
151
+ });
152
+ return {
153
+ profile: BootstrapProfileDocumentSchema.parse({
154
+ schema_version: DERIVED_SCHEMA_VERSION,
155
+ derived_at: nowISO(),
156
+ repo_fingerprint: gitProbe.repoFingerprint ?? input.repoFingerprint,
157
+ summary,
158
+ sources_scanned: [...new Set(sourcesScanned)],
159
+ git_available: gitProbe.available,
160
+ agents_md_present: agentsPresent,
161
+ seed_count: uniqueSeeds.length,
162
+ target: input.target,
163
+ }),
164
+ seeds: uniqueSeeds.map((seed) => MemorySeedDocumentSchema.parse({
165
+ ...seed,
166
+ schema_version: DERIVED_SCHEMA_VERSION,
167
+ })),
168
+ };
169
+ }
170
+ function extractReadmeSeeds(filepath, target) {
171
+ const raw = fs.readFileSync(filepath, 'utf-8');
172
+ const lines = raw.split(/\r?\n/);
173
+ const seeds = [];
174
+ const firstHeading = lines.find((line) => line.trim().startsWith('#'));
175
+ if (firstHeading) {
176
+ seeds.push(createSeed({
177
+ text: firstHeading.replace(/^#+\s*/, '').trim(),
178
+ seedKind: 'convention',
179
+ sourceKind: 'readme',
180
+ sourceRef: path.basename(filepath),
181
+ confidence: 'medium',
182
+ tags: ['bootstrap', 'readme'],
183
+ relatedPaths: target ? [target] : undefined,
184
+ }));
185
+ }
186
+ for (const section of ['setup', 'install', 'build', 'test', 'run']) {
187
+ const headingIndex = lines.findIndex((line) => line.trim().toLowerCase().includes(section));
188
+ if (headingIndex >= 0) {
189
+ const snippet = collectSectionSnippet(lines, headingIndex);
190
+ if (snippet) {
191
+ seeds.push(createSeed({
192
+ text: `${capitalize(section)} guidance: ${snippet}`,
193
+ seedKind: 'command',
194
+ sourceKind: 'readme',
195
+ sourceRef: path.basename(filepath),
196
+ confidence: 'medium',
197
+ tags: ['bootstrap', section],
198
+ }));
199
+ }
200
+ }
201
+ }
202
+ return seeds;
203
+ }
204
+ function extractAgentsSeeds(filepath, target) {
205
+ const raw = fs.readFileSync(filepath, 'utf-8');
206
+ const lines = raw.split(/\r?\n/);
207
+ const seeds = [];
208
+ const firstHeading = lines.find((line) => line.trim().startsWith('#'));
209
+ if (firstHeading) {
210
+ seeds.push(createSeed({
211
+ text: `Agent guide: ${firstHeading.replace(/^#+\s*/, '').trim()}`,
212
+ seedKind: 'agent_rule',
213
+ sourceKind: 'agents_md',
214
+ sourceRef: 'AGENTS.md',
215
+ confidence: 'high',
216
+ tags: ['bootstrap', 'agent'],
217
+ relatedPaths: target ? [target] : undefined,
218
+ }));
219
+ }
220
+ for (const line of lines) {
221
+ const trimmed = line.trim();
222
+ if (/^([-*]|\d+\.)\s+/.test(trimmed)) {
223
+ const text = trimmed.replace(/^([-*]|\d+\.)\s+/, '').trim();
224
+ if (text.length > 0) {
225
+ seeds.push(createSeed({
226
+ text,
227
+ seedKind: 'agent_rule',
228
+ sourceKind: 'agents_md',
229
+ sourceRef: 'AGENTS.md',
230
+ confidence: 'high',
231
+ tags: ['bootstrap', 'agent'],
232
+ relatedPaths: target ? [target] : undefined,
233
+ }));
234
+ }
235
+ }
236
+ }
237
+ return seeds;
238
+ }
239
+ function extractManifestSeeds(cwd, target) {
240
+ const sources = [];
241
+ const seeds = [];
242
+ const packageJsonPath = path.join(cwd, 'package.json');
243
+ if (fs.existsSync(packageJsonPath)) {
244
+ sources.push('package.json');
245
+ try {
246
+ const parsed = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
247
+ if (parsed.packageManager) {
248
+ seeds.push(createSeed({
249
+ text: `Package manager: ${parsed.packageManager}`,
250
+ seedKind: 'convention',
251
+ sourceKind: 'manifest',
252
+ sourceRef: 'package.json',
253
+ confidence: 'high',
254
+ tags: ['bootstrap', 'toolchain'],
255
+ }));
256
+ }
257
+ for (const scriptName of ['build', 'test', 'dev', 'start', 'lint']) {
258
+ const script = parsed.scripts?.[scriptName];
259
+ if (script) {
260
+ seeds.push(createSeed({
261
+ text: `Use "${scriptName}" script: ${script}`,
262
+ seedKind: 'command',
263
+ sourceKind: 'manifest',
264
+ sourceRef: `package.json#scripts.${scriptName}`,
265
+ confidence: 'high',
266
+ tags: ['bootstrap', scriptName],
267
+ }));
268
+ }
269
+ }
270
+ if (parsed.workspaces) {
271
+ seeds.push(createSeed({
272
+ text: 'Repository uses package workspaces.',
273
+ seedKind: 'convention',
274
+ sourceKind: 'manifest',
275
+ sourceRef: 'package.json#workspaces',
276
+ confidence: 'high',
277
+ tags: ['bootstrap', 'workspace'],
278
+ }));
279
+ }
280
+ }
281
+ catch {
282
+ seeds.push(createSeed({
283
+ text: 'package.json is present but could not be parsed reliably.',
284
+ seedKind: 'warning',
285
+ sourceKind: 'manifest',
286
+ sourceRef: 'package.json',
287
+ confidence: 'low',
288
+ tags: ['bootstrap', 'warning'],
289
+ }));
290
+ }
291
+ }
292
+ const makefilePath = path.join(cwd, MAKEFILE_NAME);
293
+ if (fs.existsSync(makefilePath)) {
294
+ sources.push(MAKEFILE_NAME);
295
+ const targets = fs.readFileSync(makefilePath, 'utf-8')
296
+ .split(/\r?\n/)
297
+ .map((line) => line.trim())
298
+ .filter((line) => /^[A-Za-z0-9_.-]+:\s*$/.test(line))
299
+ .map((line) => line.replace(/:\s*$/, ''))
300
+ .slice(0, 3);
301
+ for (const targetName of targets) {
302
+ seeds.push(createSeed({
303
+ text: `Make target available: ${targetName}`,
304
+ seedKind: 'command',
305
+ sourceKind: 'manifest',
306
+ sourceRef: `Makefile#${targetName}`,
307
+ confidence: 'medium',
308
+ tags: ['bootstrap', 'make'],
309
+ }));
310
+ }
311
+ }
312
+ for (const [filename, label] of [['pyproject.toml', 'Python toolchain detected'], ['Cargo.toml', 'Rust toolchain detected'], ['go.mod', 'Go module detected']]) {
313
+ if (fs.existsSync(path.join(cwd, filename))) {
314
+ sources.push(filename);
315
+ seeds.push(createSeed({
316
+ text: label,
317
+ seedKind: 'entrypoint',
318
+ sourceKind: 'manifest',
319
+ sourceRef: filename,
320
+ confidence: 'medium',
321
+ tags: ['bootstrap', 'toolchain'],
322
+ relatedPaths: target ? [target] : undefined,
323
+ }));
324
+ }
325
+ }
326
+ return { sources: [...new Set(sources)], seeds };
327
+ }
328
+ function extractRepoAnalysisSeeds(result, target) {
329
+ const seeds = [];
330
+ seeds.push(createSeed({
331
+ text: `Recommended project mode: ${result.recommendedMode}`,
332
+ seedKind: 'convention',
333
+ sourceKind: 'repo_analysis',
334
+ sourceRef: 'repo-analysis',
335
+ confidence: 'high',
336
+ tags: ['bootstrap', 'topology'],
337
+ relatedPaths: target ? [target] : undefined,
338
+ }));
339
+ for (const reason of result.reasons) {
340
+ seeds.push(createSeed({
341
+ text: reason,
342
+ seedKind: reason.toLowerCase().includes('workspace') ? 'warning' : 'convention',
343
+ sourceKind: 'repo_analysis',
344
+ sourceRef: 'repo-analysis',
345
+ confidence: 'medium',
346
+ tags: ['bootstrap', 'analysis'],
347
+ relatedPaths: target ? [target] : undefined,
348
+ }));
349
+ }
350
+ return seeds;
351
+ }
352
+ function extractExecutionContextSeeds(snapshot, target) {
353
+ const seeds = [];
354
+ if (snapshot.branch) {
355
+ seeds.push(createSeed({
356
+ text: `Current branch: ${snapshot.branch}`,
357
+ seedKind: 'environment',
358
+ sourceKind: 'machine',
359
+ sourceRef: 'git:branch',
360
+ confidence: 'high',
361
+ tags: ['bootstrap', 'execution', 'git'],
362
+ relatedPaths: target ? [target] : undefined,
363
+ }));
364
+ }
365
+ if (snapshot.git_status === 'dirty') {
366
+ seeds.push(createSeed({
367
+ text: 'Repository has uncommitted changes.',
368
+ seedKind: 'warning',
369
+ sourceKind: 'machine',
370
+ sourceRef: 'git:status',
371
+ confidence: 'high',
372
+ tags: ['bootstrap', 'execution', 'git'],
373
+ relatedPaths: target ? [target] : undefined,
374
+ }));
375
+ }
376
+ for (const tool of snapshot.toolchains.slice(0, 3)) {
377
+ seeds.push(createSeed({
378
+ text: `Toolchain available: ${tool.name}${tool.version ? ` ${tool.version}` : ''}`,
379
+ seedKind: 'tooling',
380
+ sourceKind: 'machine',
381
+ sourceRef: `tool:${tool.name}`,
382
+ confidence: 'medium',
383
+ tags: ['bootstrap', 'execution', 'toolchain'],
384
+ relatedPaths: target ? [target] : undefined,
385
+ }));
386
+ }
387
+ return seeds;
388
+ }
389
+ function extractSkillSeeds(skills, target) {
390
+ return skills.slice(0, 5).map((skill) => {
391
+ const capabilities = [];
392
+ if (skill.scripts_present)
393
+ capabilities.push('scripts');
394
+ if (skill.references_present)
395
+ capabilities.push('references');
396
+ if (skill.assets_present)
397
+ capabilities.push('assets');
398
+ const capabilityText = capabilities.length > 0 ? ` (${capabilities.join(', ')})` : '';
399
+ return createSeed({
400
+ text: `Skill available: ${skill.name}${skill.description ? ` - ${skill.description}` : ''}${capabilityText}`,
401
+ seedKind: 'tooling',
402
+ sourceKind: 'skill',
403
+ sourceRef: skill.source_path,
404
+ confidence: 'high',
405
+ tags: ['bootstrap', 'agent', 'skill'],
406
+ relatedPaths: target ? [target] : undefined,
407
+ });
408
+ });
409
+ }
410
+ function extractMcpSeeds(servers, target) {
411
+ return servers.slice(0, 5).map((server) => createSeed({
412
+ text: server.availability === 'missing_command'
413
+ ? `Local MCP server configured but unavailable: ${server.name} (${server.command ?? 'missing command'})`
414
+ : `Local MCP server configured: ${server.name} (${server.transport}, ${server.availability})`,
415
+ seedKind: server.availability === 'missing_command' ? 'warning' : 'tooling',
416
+ sourceKind: 'mcp',
417
+ sourceRef: server.config_path,
418
+ confidence: server.availability === 'missing_command' ? 'high' : 'high',
419
+ tags: ['bootstrap', 'agent', 'mcp'],
420
+ relatedPaths: target ? [target] : undefined,
421
+ }));
422
+ }
423
+ function probeGit(cwd, target) {
424
+ const headResult = spawnSync('git', ['rev-parse', 'HEAD'], {
425
+ cwd,
426
+ encoding: 'utf-8',
427
+ timeout: 5000,
428
+ });
429
+ if (headResult.status !== 0) {
430
+ return { available: false, hotspotSeeds: [] };
431
+ }
432
+ const repoFingerprint = headResult.stdout.trim();
433
+ const logResult = spawnSync('git', ['log', '--name-only', '--pretty=format:', '-n', '50'], {
434
+ cwd,
435
+ encoding: 'utf-8',
436
+ timeout: 5000,
437
+ });
438
+ const hotspotSeeds = [];
439
+ if (logResult.status === 0) {
440
+ const counts = new Map();
441
+ for (const line of logResult.stdout.split(/\r?\n/).map((entry) => entry.trim()).filter(Boolean)) {
442
+ if (line.startsWith('.brainclaw/')) {
443
+ continue;
444
+ }
445
+ counts.set(line, (counts.get(line) ?? 0) + 1);
446
+ }
447
+ const hotspots = [...counts.entries()]
448
+ .sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0]))
449
+ .slice(0, HOTSPOT_LIMIT);
450
+ for (const [filepath, count] of hotspots) {
451
+ hotspotSeeds.push(createSeed({
452
+ text: `Recent hotspot: ${filepath} (${count} recent touches)`,
453
+ seedKind: 'hotspot',
454
+ sourceKind: 'git',
455
+ sourceRef: filepath,
456
+ confidence: 'medium',
457
+ tags: ['bootstrap', 'git', 'hotspot'],
458
+ relatedPaths: [filepath, ...(target ? [target] : [])],
459
+ }));
460
+ }
461
+ }
462
+ return {
463
+ available: true,
464
+ repoFingerprint,
465
+ hotspotSeeds,
466
+ };
467
+ }
468
+ function persistBootstrapArtifacts(artifacts, cwd) {
469
+ ensureBootstrapDirs(cwd);
470
+ saveVersionedJsonFile('bootstrap_profile', bootstrapProfilePath(cwd), artifacts.profile);
471
+ const store = bootstrapSeedStore(cwd);
472
+ const existingIds = new Set(store.list().map((seed) => seed.id));
473
+ for (const seed of artifacts.seeds) {
474
+ store.save(seed);
475
+ existingIds.delete(seed.id);
476
+ }
477
+ for (const id of existingIds) {
478
+ store.delete(id);
479
+ }
480
+ }
481
+ function bootstrapSeedStore(cwd) {
482
+ return new JsonStore({
483
+ dirPath: bootstrapSeedsDir(cwd),
484
+ documentType: 'memory_seed',
485
+ getId: (seed) => seed.id,
486
+ sort: (a, b) => a.id.localeCompare(b.id),
487
+ });
488
+ }
489
+ function ensureBootstrapDirs(cwd) {
490
+ for (const dir of [bootstrapDir(cwd), bootstrapSeedsDir(cwd)]) {
491
+ if (!fs.existsSync(dir)) {
492
+ fs.mkdirSync(dir, { recursive: true });
493
+ }
494
+ }
495
+ }
496
+ function bootstrapDir(cwd) {
497
+ return resolveEntityDir('bootstrap', cwd ?? process.cwd(), 'read');
498
+ }
499
+ function bootstrapSeedsDir(cwd) {
500
+ return path.join(bootstrapDir(cwd), 'seeds');
501
+ }
502
+ function bootstrapProfilePath(cwd) {
503
+ return path.join(bootstrapDir(cwd), PROFILE_FILE);
504
+ }
505
+ function isProfileReusable(profile, target, currentFingerprint) {
506
+ if ((profile.target ?? undefined) !== target) {
507
+ return false;
508
+ }
509
+ if (profile.repo_fingerprint && currentFingerprint) {
510
+ return profile.repo_fingerprint === currentFingerprint;
511
+ }
512
+ return true;
513
+ }
514
+ function currentRepoFingerprint(cwd) {
515
+ const result = spawnSync('git', ['rev-parse', 'HEAD'], {
516
+ cwd,
517
+ encoding: 'utf-8',
518
+ timeout: 5000,
519
+ });
520
+ return result.status === 0 ? result.stdout.trim() : undefined;
521
+ }
522
+ function createSeed(input) {
523
+ return MemorySeedDocumentSchema.parse({
524
+ schema_version: DERIVED_SCHEMA_VERSION,
525
+ id: generateId('bootstrap_seeds'),
526
+ derived_at: nowISO(),
527
+ text: input.text,
528
+ seed_kind: input.seedKind,
529
+ source_kind: input.sourceKind,
530
+ source_ref: input.sourceRef,
531
+ confidence: input.confidence,
532
+ related_paths: input.relatedPaths,
533
+ tags: input.tags ?? [],
534
+ promotion_hint: input.promotionHint,
535
+ });
536
+ }
537
+ function dedupeSeeds(seeds) {
538
+ const byKey = new Map();
539
+ for (const seed of seeds) {
540
+ const key = `${seed.seed_kind}:${seed.source_kind}:${seed.source_ref}:${seed.text.toLowerCase()}`;
541
+ if (!byKey.has(key)) {
542
+ byKey.set(key, seed);
543
+ }
544
+ }
545
+ return [...byKey.values()];
546
+ }
547
+ function buildSummary(input) {
548
+ const parts = [];
549
+ parts.push(`Bootstrap summary${input.target ? ` for ${input.target}` : ''}: ${input.seeds.length} derived signal(s).`);
550
+ parts.push(`Repository mode looks ${input.repoAnalysis.recommendedMode}.`);
551
+ if (input.agentsPresent) {
552
+ parts.push('AGENTS.md detected and summarized.');
553
+ }
554
+ if (input.gitAvailable) {
555
+ parts.push('Git history available for hotspot detection.');
556
+ }
557
+ const commandCount = input.seeds.filter((seed) => seed.seed_kind === 'command').length;
558
+ if (commandCount > 0) {
559
+ parts.push(`${commandCount} command-oriented hint(s) found.`);
560
+ }
561
+ return parts.join(' ');
562
+ }
563
+ function scoreSeed(seed, target) {
564
+ let score = seedKindWeight(seed.seed_kind) + confidenceWeight(seed.confidence);
565
+ if (!target) {
566
+ return score;
567
+ }
568
+ const normalizedTarget = target.toLowerCase();
569
+ if (seed.related_paths?.some((relatedPath) => matchesPath(relatedPath, target))) {
570
+ score += 6;
571
+ }
572
+ if (seed.text.toLowerCase().includes(normalizedTarget)) {
573
+ score += 4;
574
+ }
575
+ const targetTerms = normalizedTarget.split(/[\\/.\s_-]+/).filter(Boolean);
576
+ if (targetTerms.some((term) => term.length > 1 && seed.text.toLowerCase().includes(term))) {
577
+ score += 2;
578
+ }
579
+ return score;
580
+ }
581
+ function seedKindWeight(kind) {
582
+ switch (kind) {
583
+ case 'agent_rule':
584
+ return 12;
585
+ case 'warning':
586
+ return 10;
587
+ case 'tooling':
588
+ return 9;
589
+ case 'entrypoint':
590
+ return 8;
591
+ case 'command':
592
+ return 7;
593
+ case 'environment':
594
+ return 7;
595
+ case 'convention':
596
+ return 6;
597
+ case 'hotspot':
598
+ return 4;
599
+ }
600
+ }
601
+ function confidenceWeight(confidence) {
602
+ if (confidence === 'high')
603
+ return 3;
604
+ if (confidence === 'medium')
605
+ return 2;
606
+ return 1;
607
+ }
608
+ function matchesPath(pattern, target) {
609
+ if (pattern === target)
610
+ return true;
611
+ const regexStr = '^' + pattern
612
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&')
613
+ .replace(/\*\*/g, '__GLOBSTAR__')
614
+ .replace(/\*/g, '__GLOB__')
615
+ .replace(/__GLOBSTAR__/g, '.*')
616
+ .replace(/__GLOB__/g, '[^/]*') + '$';
617
+ return new RegExp(regexStr).test(target);
618
+ }
619
+ function collectSectionSnippet(lines, headingIndex) {
620
+ const buffer = [];
621
+ for (const line of lines.slice(headingIndex + 1, headingIndex + 6)) {
622
+ const trimmed = line.trim();
623
+ if (!trimmed)
624
+ continue;
625
+ if (trimmed.startsWith('#'))
626
+ break;
627
+ buffer.push(trimmed.replace(/^[-*]\s+/, ''));
628
+ }
629
+ return buffer.length > 0 ? buffer.join(' ') : undefined;
630
+ }
631
+ function findFirstExisting(cwd, candidates) {
632
+ for (const candidate of candidates) {
633
+ const filepath = path.join(cwd, candidate);
634
+ if (fs.existsSync(filepath)) {
635
+ return filepath;
636
+ }
637
+ }
638
+ for (const hint of DOC_HINTS) {
639
+ const docDir = path.join(cwd, hint);
640
+ if (!fs.existsSync(docDir) || !fs.statSync(docDir).isDirectory()) {
641
+ continue;
642
+ }
643
+ for (const entry of fs.readdirSync(docDir).sort()) {
644
+ if (/^readme/i.test(entry)) {
645
+ return path.join(docDir, entry);
646
+ }
647
+ }
648
+ }
649
+ return undefined;
650
+ }
651
+ function capitalize(value) {
652
+ return value.slice(0, 1).toUpperCase() + value.slice(1);
653
+ }
654
+ function normalizeTarget(target) {
655
+ const trimmed = target?.trim();
656
+ return trimmed && trimmed.length > 0 ? trimmed : undefined;
657
+ }
658
+ //# sourceMappingURL=bootstrap.js.map