brainclaw 0.28.0 → 1.5.3
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 +193 -170
- package/dist/brainclaw-vscode.vsix +0 -0
- package/dist/cli.js +683 -23
- package/dist/commands/accept.js +3 -0
- package/dist/commands/add-step.js +11 -26
- package/dist/commands/agent-board.js +70 -3
- package/dist/commands/audit.js +19 -0
- package/dist/commands/check-policy.js +54 -0
- package/dist/commands/check-security-mcp.js +145 -0
- package/dist/commands/check-security.js +106 -0
- package/dist/commands/claim-resource.js +1 -0
- package/dist/commands/codev.js +672 -0
- package/dist/commands/compact.js +74 -0
- package/dist/commands/complete-step.js +16 -26
- package/dist/commands/constraint.js +8 -20
- package/dist/commands/decision.js +9 -20
- package/dist/commands/delete-plan.js +10 -12
- package/dist/commands/delete-step.js +16 -0
- package/dist/commands/dispatch.js +163 -0
- package/dist/commands/doctor.js +1122 -49
- package/dist/commands/enable-agent.js +1 -0
- package/dist/commands/export.js +280 -22
- package/dist/commands/handoff.js +33 -0
- package/dist/commands/harvest.js +189 -0
- package/dist/commands/hooks.js +82 -25
- package/dist/commands/inbox.js +169 -0
- package/dist/commands/init.js +38 -31
- package/dist/commands/install-hooks.js +71 -44
- package/dist/commands/link.js +89 -0
- package/dist/commands/list-claims.js +48 -3
- package/dist/commands/list-plans.js +129 -25
- package/dist/commands/loops-handlers.js +409 -0
- package/dist/commands/mcp-read-handlers.js +1628 -0
- package/dist/commands/mcp-schemas.generated.js +74 -0
- package/dist/commands/mcp.js +4244 -1475
- package/dist/commands/plan-resource.js +64 -0
- package/dist/commands/plan.js +12 -26
- package/dist/commands/prune.js +37 -2
- package/dist/commands/reflect.js +20 -7
- package/dist/commands/release-claim.js +11 -6
- package/dist/commands/release-notes.js +170 -0
- package/dist/commands/repair.js +210 -0
- package/dist/commands/run-profile.js +57 -0
- package/dist/commands/sequence.js +113 -0
- package/dist/commands/session-end.js +423 -14
- package/dist/commands/session-start.js +214 -41
- package/dist/commands/setup-security.js +103 -0
- package/dist/commands/setup.js +42 -4
- package/dist/commands/stale.js +109 -0
- package/dist/commands/switch.js +131 -10
- package/dist/commands/trap.js +14 -31
- package/dist/commands/update-handoff.js +63 -4
- package/dist/commands/update-plan.js +21 -28
- package/dist/commands/update-step.js +37 -0
- package/dist/commands/upgrade.js +313 -6
- package/dist/commands/usage.js +102 -0
- package/dist/commands/version.js +20 -0
- package/dist/commands/who.js +124 -0
- package/dist/commands/worktree.js +105 -0
- package/dist/core/actions.js +315 -0
- package/dist/core/agent-capability.js +610 -17
- package/dist/core/agent-context.js +7 -1
- package/dist/core/agent-files.js +1169 -85
- package/dist/core/agent-integrations.js +160 -5
- package/dist/core/agent-inventory.js +2 -0
- package/dist/core/agent-profiles.js +93 -0
- package/dist/core/agent-registry.js +162 -30
- package/dist/core/agentrun-reconciler.js +345 -0
- package/dist/core/agentruns.js +424 -0
- package/dist/core/ai-agent-detection.js +31 -10
- package/dist/core/archival.js +77 -0
- package/dist/core/assignment-sweeper.js +82 -0
- package/dist/core/assignments.js +367 -0
- package/dist/core/audit.js +30 -0
- package/dist/core/bootstrap.js +61 -10
- package/dist/core/brainclaw-version.js +94 -2
- package/dist/core/candidates.js +93 -2
- package/dist/core/claims.js +419 -0
- package/dist/core/codev-metrics.js +77 -0
- package/dist/core/codev-personas.js +31 -0
- package/dist/core/codev-plan-gen.js +35 -0
- package/dist/core/codev-prompts.js +74 -0
- package/dist/core/codev-responses.js +62 -0
- package/dist/core/codev-rounds.js +218 -0
- package/dist/core/config.js +4 -0
- package/dist/core/context.js +454 -34
- package/dist/core/coordination.js +201 -6
- package/dist/core/cross-project.js +230 -16
- package/dist/core/default-profiles/doctor.yaml +11 -0
- package/dist/core/default-profiles/janitor.yaml +11 -0
- package/dist/core/default-profiles/onboarder.yaml +11 -0
- package/dist/core/default-profiles/reviewer.yaml +13 -0
- package/dist/core/dispatcher.js +1189 -0
- package/dist/core/duplicates.js +2 -2
- package/dist/core/entity-operations.js +450 -0
- package/dist/core/entity-registry.js +344 -0
- package/dist/core/event-log.js +1 -0
- package/dist/core/events.js +106 -2
- package/dist/core/execution-adapters.js +154 -0
- package/dist/core/execution-context.js +63 -0
- package/dist/core/execution-profile.js +270 -0
- package/dist/core/execution.js +255 -0
- package/dist/core/facade-schema.js +81 -0
- package/dist/core/federation-cloud.js +99 -0
- package/dist/core/federation-message.js +52 -0
- package/dist/core/federation-transport.js +65 -0
- package/dist/core/gc-semantic.js +482 -0
- package/dist/core/governance.js +247 -0
- package/dist/core/guards.js +19 -0
- package/dist/core/ideation.js +72 -0
- package/dist/core/identity.js +252 -28
- package/dist/core/ids.js +6 -0
- package/dist/core/input-validation.js +2 -2
- package/dist/core/instruction-templates.js +344 -136
- package/dist/core/io.js +90 -11
- package/dist/core/lock.js +6 -2
- package/dist/core/loops/brief-assembly.js +213 -0
- package/dist/core/loops/facade-schema.js +148 -0
- package/dist/core/loops/index.js +7 -0
- package/dist/core/loops/iteration-engine.js +139 -0
- package/dist/core/loops/lock.js +385 -0
- package/dist/core/loops/store.js +201 -0
- package/dist/core/loops/types.js +403 -0
- package/dist/core/loops/verbs.js +534 -0
- package/dist/core/markdown.js +15 -3
- package/dist/core/memory-compactor.js +432 -0
- package/dist/core/memory-git.js +152 -8
- package/dist/core/messaging.js +278 -0
- package/dist/core/migration.js +32 -1
- package/dist/core/mutation-pipeline.js +4 -2
- package/dist/core/operations/memory-mutation.js +129 -0
- package/dist/core/operations/memory-write.js +78 -0
- package/dist/core/operations/plan.js +190 -0
- package/dist/core/policy.js +169 -0
- package/dist/core/repo-analysis.js +67 -0
- package/dist/core/reputation.js +9 -3
- package/dist/core/schema.js +546 -21
- package/dist/core/search.js +21 -2
- package/dist/core/security-cache.js +71 -0
- package/dist/core/security-guard.js +152 -0
- package/dist/core/security-scoring.js +86 -0
- package/dist/core/sequence.js +130 -0
- package/dist/core/socket-client.js +113 -0
- package/dist/core/staleness.js +246 -0
- package/dist/core/state.js +98 -22
- package/dist/core/store-resolution.js +54 -12
- package/dist/core/toml-writer.js +76 -0
- package/dist/core/upgrades/backup.js +232 -0
- package/dist/core/upgrades/health-check.js +169 -0
- package/dist/core/upgrades/patches/candidate-archive.js +145 -0
- package/dist/core/upgrades/patches/handoff-review-strip.js +128 -0
- package/dist/core/upgrades/patches/provenance-rollout.js +136 -0
- package/dist/core/upgrades/schema-version.js +97 -0
- package/dist/core/worktree.js +606 -0
- package/dist/facts.js +114 -0
- package/dist/facts.json +111 -0
- package/docs/architecture/project-refs.md +5 -1
- package/docs/cli.md +690 -43
- package/docs/concepts/ideation-loop.md +317 -0
- package/docs/concepts/loop-engine.md +456 -0
- package/docs/concepts/mcp-governance.md +268 -0
- package/docs/concepts/memory-staleness.md +122 -0
- package/docs/concepts/multi-agent-workflows.md +166 -0
- package/docs/concepts/plans-and-claims.md +31 -6
- package/docs/concepts/project-md-convention.md +35 -0
- package/docs/concepts/troubleshooting.md +220 -0
- package/docs/concepts/upgrade-cli.md +202 -0
- package/docs/concepts/upgrade-dogfood-procedure.md +114 -0
- package/docs/context-format-changelog.md +2 -2
- package/docs/context-format.md +2 -2
- package/docs/index.md +68 -0
- package/docs/integrations/agents.md +15 -16
- package/docs/integrations/cline.md +88 -0
- package/docs/integrations/codex.md +75 -23
- package/docs/integrations/continue.md +60 -0
- package/docs/integrations/copilot.md +67 -9
- package/docs/integrations/kilocode.md +72 -0
- package/docs/integrations/mcp.md +304 -21
- package/docs/integrations/mistral-vibe.md +122 -0
- package/docs/integrations/opencode.md +84 -0
- package/docs/integrations/overview.md +23 -8
- package/docs/integrations/roo.md +74 -0
- package/docs/integrations/windsurf.md +83 -0
- package/docs/mcp-schema-changelog.md +191 -1
- package/docs/playbooks/integration/index.md +121 -0
- package/docs/playbooks/productivity/index.md +102 -0
- package/docs/playbooks/team/index.md +122 -0
- package/docs/product/agent-first-model.md +184 -0
- package/docs/product/entity-model-audit.md +462 -0
- package/docs/quickstart-existing-project.md +135 -0
- package/docs/quickstart.md +124 -37
- package/docs/release-maintenance.md +79 -0
- package/docs/review.md +2 -0
- package/docs/server-operations.md +118 -0
- package/package.json +20 -12
- package/dist/commands/claude-desktop-extension.js +0 -18
- package/dist/commands/diff.js +0 -99
- package/dist/core/claude-desktop-extension.js +0 -224
|
@@ -0,0 +1,1628 @@
|
|
|
1
|
+
import { applyBootstrapImport, renderBootstrapInterview, renderBootstrapSummary, runBootstrapProfile, uninstallBootstrapImport } from '../core/bootstrap.js';
|
|
2
|
+
import { buildAgentToolingContext, renderAgentToolingSummary } from '../core/agent-context.js';
|
|
3
|
+
import { buildCoordinationSnapshot } from '../core/coordination.js';
|
|
4
|
+
import { scanDescendantPlans } from './list-plans.js';
|
|
5
|
+
import { buildContext } from '../core/context.js';
|
|
6
|
+
import { buildExecutionContext, renderExecutionContextSummary } from '../core/execution-context.js';
|
|
7
|
+
import { checkBrainclawInstallableUpdate, renderBrainclawInstallableUpdateNotice } from '../core/brainclaw-version.js';
|
|
8
|
+
import { loadConfig } from '../core/config.js';
|
|
9
|
+
import { loadAllSessions, loadCurrentSession, saveCurrentSession, gcStaleSessions } from '../core/identity.js';
|
|
10
|
+
import { loadState } from '../core/state.js';
|
|
11
|
+
import { listArchivedCandidates, listCandidates, resolvedSource } from '../core/candidates.js';
|
|
12
|
+
import { listClaims, assessClaimLiveness } from '../core/claims.js';
|
|
13
|
+
import { listAssignments } from '../core/assignments.js';
|
|
14
|
+
import { listAgentRuns } from '../core/agentruns.js';
|
|
15
|
+
import { reconcileAgentRun } from '../core/agentrun-reconciler.js';
|
|
16
|
+
import { listActionRequired } from '../core/actions.js';
|
|
17
|
+
import { queryRuntimeEvents } from '../core/events.js';
|
|
18
|
+
import { listSequences, getActiveSequence } from '../core/sequence.js';
|
|
19
|
+
import { resolveCurrentHostId } from '../core/host.js';
|
|
20
|
+
import { listAgentIdentities, resolveAgentScope, resolveCurrentAgentIdentity, resolveCurrentAgentName, } from '../core/agent-registry.js';
|
|
21
|
+
import { readAuditLog } from '../core/audit.js';
|
|
22
|
+
import { readInbox, getThread } from '../core/messaging.js';
|
|
23
|
+
import { analyzeSequence } from '../core/dispatcher.js';
|
|
24
|
+
import { checkPolicy } from '../core/policy.js';
|
|
25
|
+
import { buildGovernanceReport, renderGovernanceMarkdown } from '../core/governance.js';
|
|
26
|
+
import { inferProjectFromTarget, loadInstructions, resolveInstructions } from '../core/instructions.js';
|
|
27
|
+
import { buildReputationSnapshot, toPublicReputationSummary } from '../core/reputation.js';
|
|
28
|
+
import { search } from '../core/search.js';
|
|
29
|
+
import { buildEstimationReport } from './estimation-report.js';
|
|
30
|
+
import { runDoctor } from './doctor.js';
|
|
31
|
+
import { buildProjectDiscovery, saveDiscoveryProfile, loadDiscoveryProfile, renderDiscoverySummary } from '../core/project-discovery.js';
|
|
32
|
+
import { listCapabilities, listTools as listRegistryTools } from '../core/registries.js';
|
|
33
|
+
import { listAvailableProjects, switchProject } from './switch.js';
|
|
34
|
+
import { resolveEffectiveCwd } from '../core/store-resolution.js';
|
|
35
|
+
import { resolveProjectCwd } from '../core/cross-project.js';
|
|
36
|
+
import { readUnseenEvents, buildNotificationSummary } from '../core/event-log.js';
|
|
37
|
+
import { BootstrapInterviewAnswerSchema, AssignmentStatusSchema, AgentRunStatusSchema, AgentRunTransportSchema, ActionRequiredStatusSchema, ActionRequiredKindSchema } from '../core/schema.js';
|
|
38
|
+
import { SCHEMA_VERSION, createToolErrorResponse, normaliseFormat, renderContextForMcp, } from './mcp.js';
|
|
39
|
+
function normalizeBootstrapInterviewAnswersArg(value) {
|
|
40
|
+
if (!Array.isArray(value)) {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
return value.map((entry) => BootstrapInterviewAnswerSchema.parse(entry));
|
|
44
|
+
}
|
|
45
|
+
/** Validate a string enum filter or return undefined. Throws on invalid. */
|
|
46
|
+
function validateEnumFilter(value, schema, label) {
|
|
47
|
+
if (value === undefined || value === null || value === '')
|
|
48
|
+
return undefined;
|
|
49
|
+
const str = String(value);
|
|
50
|
+
const result = schema.safeParse(str);
|
|
51
|
+
if (!result.success) {
|
|
52
|
+
throw new Error(`Invalid ${label}: '${str}'`);
|
|
53
|
+
}
|
|
54
|
+
return str;
|
|
55
|
+
}
|
|
56
|
+
function normalizeBootstrapInterviewAudienceArg(value) {
|
|
57
|
+
if (value === 'cli' || value === 'ide_chat' || value === 'any') {
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
return 'any';
|
|
61
|
+
}
|
|
62
|
+
function getReviewAssignee(tags) {
|
|
63
|
+
for (const tag of tags) {
|
|
64
|
+
if (tag.startsWith('assignee:')) {
|
|
65
|
+
return tag.slice('assignee:'.length).trim() || undefined;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
export function handleMcpReadToolCall(name, args = {}, context = {}) {
|
|
71
|
+
let cwd = context.cwd ?? resolveEffectiveCwd();
|
|
72
|
+
// If a project param is provided, resolve it to an actual cwd override.
|
|
73
|
+
// resolveProjectCwd unifies cross_project_links (siblings/peers) AND
|
|
74
|
+
// workspace store-chain children. Throws on unknown project — surfaces
|
|
75
|
+
// visibly as a tool error rather than silently falling back to the
|
|
76
|
+
// current project, which would mislead the caller.
|
|
77
|
+
const projectArg = args.project;
|
|
78
|
+
if (projectArg) {
|
|
79
|
+
cwd = resolveProjectCwd(projectArg, cwd);
|
|
80
|
+
}
|
|
81
|
+
if (name === 'bclaw_get_context') {
|
|
82
|
+
const result = buildContext({
|
|
83
|
+
target: args.path,
|
|
84
|
+
project: projectArg,
|
|
85
|
+
agent: args.agent,
|
|
86
|
+
host: args.host,
|
|
87
|
+
allHosts: args.allHosts,
|
|
88
|
+
profile: args.profile,
|
|
89
|
+
includePending: args.includePending,
|
|
90
|
+
maxItems: args.maxItems,
|
|
91
|
+
maxChars: args.maxChars,
|
|
92
|
+
digest: args.digest,
|
|
93
|
+
sinceSession: args.since_session,
|
|
94
|
+
bootstrap: args.bootstrap,
|
|
95
|
+
refreshBootstrap: args.refreshBootstrap,
|
|
96
|
+
cwd,
|
|
97
|
+
});
|
|
98
|
+
// Load available capabilities and tools from dedicated registries
|
|
99
|
+
const capabilities = listCapabilities(cwd);
|
|
100
|
+
const tools = listRegistryTools(cwd);
|
|
101
|
+
const format = normaliseFormat(args.format);
|
|
102
|
+
const content = renderContextForMcp(result, format, {
|
|
103
|
+
explain: args.explain,
|
|
104
|
+
compactTemplate: args.compactTemplate,
|
|
105
|
+
});
|
|
106
|
+
// Add metadata discovery section to content
|
|
107
|
+
let enrichedContent = content;
|
|
108
|
+
if (capabilities.length > 0 || tools.length > 0) {
|
|
109
|
+
const suggestions = [];
|
|
110
|
+
if (capabilities.length > 0) {
|
|
111
|
+
suggestions.push(`\n## Available Capabilities (${capabilities.length})`);
|
|
112
|
+
capabilities.slice(0, 5).forEach((cap) => {
|
|
113
|
+
suggestions.push(`- [${cap.id}] ${cap.name} (${cap.category})`);
|
|
114
|
+
});
|
|
115
|
+
if (capabilities.length > 5) {
|
|
116
|
+
suggestions.push(`- ... and ${capabilities.length - 5} more`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (tools.length > 0) {
|
|
120
|
+
suggestions.push(`\n## Available Tools (${tools.length})`);
|
|
121
|
+
tools.slice(0, 5).forEach((tool) => {
|
|
122
|
+
suggestions.push(`- [${tool.id}] ${tool.name} (${tool.type})`);
|
|
123
|
+
});
|
|
124
|
+
if (tools.length > 5) {
|
|
125
|
+
suggestions.push(`- ... and ${tools.length - 5} more`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
suggestions.push('\n💡 Tip: Use bclaw_get_capabilities, bclaw_list_tools, or bclaw_search_tools for detailed discovery');
|
|
129
|
+
enrichedContent = content + suggestions.join('\n');
|
|
130
|
+
}
|
|
131
|
+
// Check for unseen events from other agents
|
|
132
|
+
const agentName = args.agent ?? resolveCurrentAgentName(cwd);
|
|
133
|
+
const unseenEvents = readUnseenEvents(agentName, cwd);
|
|
134
|
+
const notifications = buildNotificationSummary(unseenEvents);
|
|
135
|
+
return {
|
|
136
|
+
content: [{ type: 'text', text: enrichedContent || 'No relevant memory found.' }],
|
|
137
|
+
structuredContent: {
|
|
138
|
+
...result,
|
|
139
|
+
available_capabilities: capabilities.map((cap) => ({
|
|
140
|
+
id: cap.id,
|
|
141
|
+
name: cap.name,
|
|
142
|
+
category: cap.category,
|
|
143
|
+
})),
|
|
144
|
+
available_tools: tools.map((tool) => ({
|
|
145
|
+
id: tool.id,
|
|
146
|
+
name: tool.name,
|
|
147
|
+
type: tool.type,
|
|
148
|
+
})),
|
|
149
|
+
...(notifications ? { pending_notifications: notifications, unseen_event_count: unseenEvents.length } : {}),
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (name === 'bclaw_read_handoff') {
|
|
154
|
+
const handoffId = String(args.id ?? '').trim();
|
|
155
|
+
if (!handoffId) {
|
|
156
|
+
throw new Error('Missing required argument: id');
|
|
157
|
+
}
|
|
158
|
+
const state = loadState(cwd);
|
|
159
|
+
const handoff = state.open_handoffs.find((item) => item.id === handoffId || item.short_label === handoffId);
|
|
160
|
+
if (!handoff) {
|
|
161
|
+
return {
|
|
162
|
+
content: [{ type: 'text', text: `Handoff not found: ${handoffId}` }],
|
|
163
|
+
structuredContent: { handoff_id: handoffId, found: false, schema_version: SCHEMA_VERSION },
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
const lines = [
|
|
167
|
+
`Handoff ${handoff.short_label ?? handoff.id}`,
|
|
168
|
+
`From: ${handoff.from}`,
|
|
169
|
+
`To: ${handoff.to}`,
|
|
170
|
+
`Status: ${handoff.status}`,
|
|
171
|
+
`Created: ${handoff.created_at}`,
|
|
172
|
+
'',
|
|
173
|
+
handoff.text,
|
|
174
|
+
];
|
|
175
|
+
if (handoff.narrative) {
|
|
176
|
+
lines.push('', 'Narrative:', handoff.narrative);
|
|
177
|
+
}
|
|
178
|
+
if (handoff.review) {
|
|
179
|
+
lines.push('', 'Review:');
|
|
180
|
+
if (handoff.review.reviewer)
|
|
181
|
+
lines.push(`Reviewer: ${handoff.review.reviewer}`);
|
|
182
|
+
if (handoff.review.verdict)
|
|
183
|
+
lines.push(`Verdict: ${handoff.review.verdict}`);
|
|
184
|
+
if (handoff.review.reviewed_by)
|
|
185
|
+
lines.push(`Reviewed by: ${handoff.review.reviewed_by}`);
|
|
186
|
+
if (handoff.review.summary)
|
|
187
|
+
lines.push(`Summary: ${handoff.review.summary}`);
|
|
188
|
+
for (const issue of handoff.review.blocking_issues ?? []) {
|
|
189
|
+
lines.push(`Blocking issue: ${issue}`);
|
|
190
|
+
}
|
|
191
|
+
for (const suggestion of handoff.review.suggestions ?? []) {
|
|
192
|
+
lines.push(`Suggestion: ${suggestion}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (handoff.snapshot?.diff) {
|
|
196
|
+
lines.push('', 'Uncommitted Git Diff:', handoff.snapshot.diff);
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
200
|
+
structuredContent: { handoff, schema_version: SCHEMA_VERSION },
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
if (name === 'bclaw_bootstrap') {
|
|
204
|
+
const interviewAnswers = normalizeBootstrapInterviewAnswersArg(args.interviewAnswers);
|
|
205
|
+
if (args.apply && args.uninstall) {
|
|
206
|
+
throw new Error('bclaw_bootstrap does not allow apply and uninstall at the same time.');
|
|
207
|
+
}
|
|
208
|
+
if (args.uninstall) {
|
|
209
|
+
const result = uninstallBootstrapImport(cwd);
|
|
210
|
+
const text = !result.receipt
|
|
211
|
+
? 'No bootstrap import receipt found.'
|
|
212
|
+
: `Bootstrap uninstall completed: ${result.deactivatedCount} instruction(s) deactivated, ${result.deletedCount} artifact(s) deleted, ${result.skippedCount} artifact(s) skipped.`;
|
|
213
|
+
return {
|
|
214
|
+
content: [{ type: 'text', text }],
|
|
215
|
+
structuredContent: {
|
|
216
|
+
receipt: result.receipt,
|
|
217
|
+
deactivated_count: result.deactivatedCount,
|
|
218
|
+
deleted_count: result.deletedCount,
|
|
219
|
+
skipped_count: result.skippedCount,
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
if (args.apply) {
|
|
224
|
+
const applied = applyBootstrapImport({
|
|
225
|
+
target: args.target,
|
|
226
|
+
refresh: args.refresh,
|
|
227
|
+
interviewAnswers,
|
|
228
|
+
cwd,
|
|
229
|
+
});
|
|
230
|
+
return {
|
|
231
|
+
content: [{
|
|
232
|
+
type: 'text',
|
|
233
|
+
text: `Bootstrap import applied: ${applied.createdCount} item(s) created, ${applied.skippedCount} suggestion(s) skipped.`,
|
|
234
|
+
}],
|
|
235
|
+
structuredContent: {
|
|
236
|
+
created_count: applied.createdCount,
|
|
237
|
+
skipped_count: applied.skippedCount,
|
|
238
|
+
receipt: applied.receipt,
|
|
239
|
+
import_plan: applied.proposal,
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
const result = runBootstrapProfile({
|
|
244
|
+
target: args.target,
|
|
245
|
+
refresh: args.refresh,
|
|
246
|
+
interviewAnswers,
|
|
247
|
+
cwd,
|
|
248
|
+
});
|
|
249
|
+
const audience = normalizeBootstrapInterviewAudienceArg(args.audience);
|
|
250
|
+
const text = args.interview
|
|
251
|
+
? renderBootstrapInterview(result, audience)
|
|
252
|
+
: renderBootstrapSummary(result);
|
|
253
|
+
// Extract top-level suggested questions for conversational bootstrap (step 11)
|
|
254
|
+
const suggestedQuestions = result.importPlan.interview?.questions?.map((q) => ({
|
|
255
|
+
id: q.id,
|
|
256
|
+
prompt: q.prompt,
|
|
257
|
+
rationale: q.rationale,
|
|
258
|
+
priority: q.priority,
|
|
259
|
+
})) ?? [];
|
|
260
|
+
// Separate auto-imports (high confidence) from proposals (need discussion)
|
|
261
|
+
const autoImports = result.importPlan.suggestions.filter((s) => s.confidence === 'high');
|
|
262
|
+
const proposals = result.importPlan.suggestions.filter((s) => s.confidence !== 'high');
|
|
263
|
+
return {
|
|
264
|
+
content: [{ type: 'text', text }],
|
|
265
|
+
structuredContent: {
|
|
266
|
+
summary: result.profile.summary,
|
|
267
|
+
target: result.profile.target,
|
|
268
|
+
repo_fingerprint: result.profile.repo_fingerprint,
|
|
269
|
+
sources_scanned: result.profile.sources_scanned,
|
|
270
|
+
workspace_kind: result.profile.workspace_kind,
|
|
271
|
+
onboarding_mode: result.profile.onboarding_mode,
|
|
272
|
+
confidence: result.profile.confidence,
|
|
273
|
+
native_instruction_files: result.profile.native_instruction_files,
|
|
274
|
+
gaps: result.profile.gaps,
|
|
275
|
+
seed_count: result.profile.seed_count,
|
|
276
|
+
seeds: result.seeds,
|
|
277
|
+
import_plan: result.importPlan,
|
|
278
|
+
auto_imports: autoImports,
|
|
279
|
+
proposals,
|
|
280
|
+
suggested_questions: suggestedQuestions,
|
|
281
|
+
last_application: result.lastApplication,
|
|
282
|
+
reused_profile: result.reusedProfile,
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
if (name === 'bclaw_get_execution_context') {
|
|
287
|
+
const executionContext = buildExecutionContext({ cwd });
|
|
288
|
+
const config = loadConfig(cwd);
|
|
289
|
+
const installableUpdate = checkBrainclawInstallableUpdate(config, cwd, { useDefaultNpmSource: true });
|
|
290
|
+
const installableUpdateNotice = renderBrainclawInstallableUpdateNotice(installableUpdate);
|
|
291
|
+
const agentTooling = args.includeAgentTooling ? buildAgentToolingContext({ cwd }) : undefined;
|
|
292
|
+
const text = [
|
|
293
|
+
renderExecutionContextSummary(executionContext, true),
|
|
294
|
+
...(installableUpdateNotice ? ['', installableUpdateNotice] : []),
|
|
295
|
+
...(agentTooling ? ['', renderAgentToolingSummary(agentTooling)] : []),
|
|
296
|
+
].join('\n');
|
|
297
|
+
return {
|
|
298
|
+
content: [{ type: 'text', text }],
|
|
299
|
+
structuredContent: {
|
|
300
|
+
execution_context: executionContext,
|
|
301
|
+
installable_update: {
|
|
302
|
+
...installableUpdate,
|
|
303
|
+
...(installableUpdate.agent_release_notes
|
|
304
|
+
? { agent_release_notes: installableUpdate.agent_release_notes }
|
|
305
|
+
: {}),
|
|
306
|
+
},
|
|
307
|
+
...(agentTooling ? { agent_tooling: agentTooling } : {}),
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
if (name === 'bclaw_release_notes') {
|
|
312
|
+
const config = loadConfig(cwd);
|
|
313
|
+
const updateCheck = checkBrainclawInstallableUpdate(config, cwd, { useDefaultNpmSource: true });
|
|
314
|
+
const arn = updateCheck.agent_release_notes;
|
|
315
|
+
const lines = [];
|
|
316
|
+
if (arn) {
|
|
317
|
+
lines.push(`Version: ${updateCheck.latest_installable_version ?? 'unknown'}`);
|
|
318
|
+
lines.push(`Summary: ${arn.summary}`);
|
|
319
|
+
if (arn.agent_relevance)
|
|
320
|
+
lines.push(`Agent relevance: ${arn.agent_relevance}`);
|
|
321
|
+
lines.push(`Breaking risk: ${arn.breaking_risk ?? 'none'}`);
|
|
322
|
+
if (arn.recommended_for && arn.recommended_for.length > 0) {
|
|
323
|
+
lines.push(`Recommended for: ${arn.recommended_for.join(', ')}`);
|
|
324
|
+
}
|
|
325
|
+
if (arn.highlights && arn.highlights.length > 0) {
|
|
326
|
+
lines.push('Highlights:');
|
|
327
|
+
for (const h of arn.highlights)
|
|
328
|
+
lines.push(` • ${h}`);
|
|
329
|
+
}
|
|
330
|
+
if (arn.action_recommendation)
|
|
331
|
+
lines.push(`Action: ${arn.action_recommendation}`);
|
|
332
|
+
}
|
|
333
|
+
else if (updateCheck.release_notes) {
|
|
334
|
+
lines.push(`Version: ${updateCheck.latest_installable_version ?? 'unknown'}`);
|
|
335
|
+
lines.push(updateCheck.release_notes);
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
lines.push('No agent release notes available for the configured update source.');
|
|
339
|
+
if (updateCheck.status === 'not_configured') {
|
|
340
|
+
lines.push('Configure brainclaw_update_source in your project config to enable update checks.');
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return {
|
|
344
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
345
|
+
structuredContent: {
|
|
346
|
+
status: updateCheck.status,
|
|
347
|
+
latest_installable_version: updateCheck.latest_installable_version,
|
|
348
|
+
agent_release_notes: arn ?? null,
|
|
349
|
+
release_notes: updateCheck.release_notes ?? null,
|
|
350
|
+
},
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
if (name === 'bclaw_get_agent_board_summary') {
|
|
354
|
+
const config = loadConfig(cwd);
|
|
355
|
+
const state = loadState(cwd);
|
|
356
|
+
const agent = args.agent ?? resolveCurrentAgentName(cwd);
|
|
357
|
+
const currentHost = resolveCurrentHostId();
|
|
358
|
+
const activeClaims = listClaims(cwd).filter((c) => c.status === 'active');
|
|
359
|
+
const pendingActions = listActionRequired(cwd).filter((a) => a.status === 'pending');
|
|
360
|
+
const agents = listAgentIdentities(cwd);
|
|
361
|
+
const sessions = loadAllSessions(cwd);
|
|
362
|
+
const activeSequence = getActiveSequence(cwd);
|
|
363
|
+
const sequenceTodoCount = activeSequence
|
|
364
|
+
? activeSequence.items.filter((item) => {
|
|
365
|
+
const plan = state.plan_items.find((p) => p.id === item.planId || p.short_label === item.planId);
|
|
366
|
+
return !plan || plan.status === 'todo';
|
|
367
|
+
}).length
|
|
368
|
+
: 0;
|
|
369
|
+
const summary = {
|
|
370
|
+
project_id: config.project_id,
|
|
371
|
+
agent,
|
|
372
|
+
current_host: currentHost,
|
|
373
|
+
attention_required: pendingActions.length,
|
|
374
|
+
in_progress: activeClaims.length,
|
|
375
|
+
plans: {
|
|
376
|
+
in_progress: state.plan_items.filter((p) => p.status === 'in_progress').length,
|
|
377
|
+
todo: state.plan_items.filter((p) => p.status === 'todo').length,
|
|
378
|
+
},
|
|
379
|
+
traps: {
|
|
380
|
+
high: state.known_traps.filter((t) => t.severity === 'high').length,
|
|
381
|
+
total: state.known_traps.length,
|
|
382
|
+
},
|
|
383
|
+
agents: agents.length,
|
|
384
|
+
sessions: sessions.length,
|
|
385
|
+
sequences: {
|
|
386
|
+
active_name: activeSequence?.name ?? null,
|
|
387
|
+
todo_count: sequenceTodoCount,
|
|
388
|
+
},
|
|
389
|
+
};
|
|
390
|
+
return {
|
|
391
|
+
content: [{ type: 'text', text: JSON.stringify(summary, null, 2) }],
|
|
392
|
+
structuredContent: summary,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
if (name === 'bclaw_get_agent_board') {
|
|
396
|
+
const board = buildCoordinationSnapshot({
|
|
397
|
+
agent: args.agent,
|
|
398
|
+
project: args.project,
|
|
399
|
+
target: args.path,
|
|
400
|
+
host: args.host,
|
|
401
|
+
allHosts: args.allHosts,
|
|
402
|
+
includeReputation: args.includeReputation,
|
|
403
|
+
includeSessionMeta: args.includeSessionMeta,
|
|
404
|
+
autoAcknowledge: true,
|
|
405
|
+
cwd,
|
|
406
|
+
});
|
|
407
|
+
const lines = [];
|
|
408
|
+
lines.push(`Agent board${board.agent ? ` for ${board.agent}` : ''}${board.project ? ` (${board.project})` : ''}`);
|
|
409
|
+
lines.push('');
|
|
410
|
+
if (board.project_id)
|
|
411
|
+
lines.push(`Project ID: ${board.project_id}`);
|
|
412
|
+
if (board.agent && board.agent_id)
|
|
413
|
+
lines.push(`Agent ID: ${board.agent_id}`);
|
|
414
|
+
lines.push(`Current host: ${board.current_host}`);
|
|
415
|
+
if (board.all_hosts)
|
|
416
|
+
lines.push('Host filter: all-hosts');
|
|
417
|
+
else if (board.host_filter)
|
|
418
|
+
lines.push(`Host filter: ${board.host_filter}`);
|
|
419
|
+
if (args.includeReputation && board.reputation_summary) {
|
|
420
|
+
lines.push(`Reputation: tracked=${board.reputation_summary.tracked_agents}, avg_trust=${board.reputation_summary.avg_internal_trust}`);
|
|
421
|
+
if (board.agent_reputation) {
|
|
422
|
+
lines.push(`Agent trust: ${board.agent_reputation.internal_trust} (cq=${board.agent_reputation.contribution_quality}, rv=${board.agent_reputation.review_reliability}, ct=${board.agent_reputation.continuity_hygiene})`);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
lines.push(`Active plans: ${board.active_plans.length}`);
|
|
426
|
+
for (const plan of board.active_plans.slice(0, 10)) {
|
|
427
|
+
const claims = plan.claims.length ? ` claims=${plan.claims.map((claim) => claim.agent).join(',')}` : '';
|
|
428
|
+
lines.push(`- [${plan.id}] ${plan.text} (${plan.status}, ${plan.priority})${claims}`);
|
|
429
|
+
}
|
|
430
|
+
lines.push(`Active claims: ${board.active_claims.length}`);
|
|
431
|
+
for (const claim of board.active_claims.slice(0, 10)) {
|
|
432
|
+
const identity = claim.agent_id ? ` [${claim.agent_id}]` : '';
|
|
433
|
+
const session = claim.session_id ? ` session=${claim.session_id}` : '';
|
|
434
|
+
const liveness = assessClaimLiveness(claim).status;
|
|
435
|
+
const liveTag = liveness === 'live' || liveness === 'young' ? '' : ` [${liveness.toUpperCase()}]`;
|
|
436
|
+
lines.push(`- [${claim.id}] ${claim.agent}${identity} -> ${claim.scope}${claim.plan_id ? ` (plan ${claim.plan_id})` : ''}${session}${liveTag}`);
|
|
437
|
+
}
|
|
438
|
+
lines.push(`Active assignments: ${board.active_assignments.length}`);
|
|
439
|
+
for (const assignment of board.active_assignments.slice(0, 10)) {
|
|
440
|
+
const plan = assignment.plan_id ? ` plan=${assignment.plan_id}` : '';
|
|
441
|
+
const session = assignment.session_id ? ` session=${assignment.session_id}` : '';
|
|
442
|
+
lines.push(`- [${assignment.id}] ${assignment.agent} (${assignment.status}) -> ${assignment.scope}${plan}${session}`);
|
|
443
|
+
}
|
|
444
|
+
lines.push(`Active runs: ${board.active_runs.length}`);
|
|
445
|
+
for (const run of board.active_runs.slice(0, 10)) {
|
|
446
|
+
const assignment = run.assignment_id ? ` assignment=${run.assignment_id}` : '';
|
|
447
|
+
const attempt = ` attempt=${run.attempt_index}`;
|
|
448
|
+
const session = run.session_id ? ` session=${run.session_id}` : '';
|
|
449
|
+
lines.push(`- [${run.id}] ${run.agent} (${run.status}/${run.transport}) -> ${run.scope}${assignment}${attempt}${session}`);
|
|
450
|
+
}
|
|
451
|
+
lines.push(`Pending actions: ${board.active_actions.length}`);
|
|
452
|
+
for (const action of board.active_actions.slice(0, 10)) {
|
|
453
|
+
const run = action.run_id ? ` run=${action.run_id}` : '';
|
|
454
|
+
const session = action.session_id ? ` session=${action.session_id}` : '';
|
|
455
|
+
lines.push(`- [${action.id}] ${action.agent} (${action.kind}) -> ${action.title}${run}${session}`);
|
|
456
|
+
}
|
|
457
|
+
lines.push(`Active sequence: ${board.active_sequence ? `1 (${board.active_sequence.name})` : '0'}`);
|
|
458
|
+
if (board.active_sequence) {
|
|
459
|
+
lines.push(`- [${board.active_sequence.id}] ${board.active_sequence.name} (${board.active_sequence.status})`);
|
|
460
|
+
for (const item of board.active_sequence.items.slice(0, 10)) {
|
|
461
|
+
const lane = item.lane ? ` lane=${item.lane}` : '';
|
|
462
|
+
const hardAfter = item.hard_after.length ? ` hard_after=${item.hard_after.join(',')}` : '';
|
|
463
|
+
const softAfter = item.soft_after.length ? ` soft_after=${item.soft_after.join(',')}` : '';
|
|
464
|
+
lines.push(` #${item.rank} ${item.planId}${lane}${hardAfter}${softAfter}`);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
const sessionMetaHint = board.session_meta_hidden > 0 ? ` (+${board.session_meta_hidden} session lifecycle notes hidden — pass includeSessionMeta to show)` : '';
|
|
468
|
+
lines.push(`Runtime notes: ${board.runtime_notes.length}${sessionMetaHint}`);
|
|
469
|
+
for (const note of board.runtime_notes.slice(-10)) {
|
|
470
|
+
const scope = note.visibility === 'shared' ? 'shared' : `${note.visibility}:${note.host_id ?? 'unknown-host'}`;
|
|
471
|
+
const identity = note.agent_id ? ` [${note.agent_id}]` : '';
|
|
472
|
+
lines.push(`- [${note.id}] ${note.agent}${identity}: ${note.text}${note.plan_id ? ` (plan ${note.plan_id})` : ''} [${scope}]`);
|
|
473
|
+
}
|
|
474
|
+
lines.push(`Open handoffs: ${board.open_handoffs.length}`);
|
|
475
|
+
for (const handoff of board.open_handoffs.slice(0, 10)) {
|
|
476
|
+
const contractHint = handoff.contract ? ' [contract]' : '';
|
|
477
|
+
lines.push(`- [${handoff.id}] ${handoff.from} -> ${handoff.to}: ${handoff.text}${contractHint}`);
|
|
478
|
+
}
|
|
479
|
+
lines.push(`Resolved instructions: ${board.resolved_instructions.length}`);
|
|
480
|
+
for (const instruction of board.resolved_instructions.slice(0, 10)) {
|
|
481
|
+
lines.push(`- [${instruction.id}] <${instruction.layer}${instruction.scope ? `:${instruction.scope}` : ''}> ${instruction.text}`);
|
|
482
|
+
}
|
|
483
|
+
if (board.other_agents && board.other_agents.length > 0) {
|
|
484
|
+
lines.push(`Other agents: ${board.other_agents.length}`);
|
|
485
|
+
for (const other of board.other_agents) {
|
|
486
|
+
lines.push(`- ${other.name}: ${other.claim_count} claim(s) on ${other.scopes.join(', ')}`);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
return {
|
|
490
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
491
|
+
structuredContent: { ...board },
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
if (name === 'bclaw_search') {
|
|
495
|
+
const query = String(args.query ?? '');
|
|
496
|
+
if (!query) {
|
|
497
|
+
throw new Error('Missing required argument: query');
|
|
498
|
+
}
|
|
499
|
+
const offset = Math.max(0, Number(args.offset) || 0);
|
|
500
|
+
const limit = typeof args.limit === 'number' ? args.limit : 10;
|
|
501
|
+
const allResults = search({
|
|
502
|
+
query,
|
|
503
|
+
section: (args.section ?? args.type),
|
|
504
|
+
since: args.since,
|
|
505
|
+
maxResults: offset + limit,
|
|
506
|
+
cwd,
|
|
507
|
+
});
|
|
508
|
+
const total = allResults.length;
|
|
509
|
+
const page = allResults.slice(offset, offset + limit);
|
|
510
|
+
const lines = page.map((result) => `[${result.id}] (${result.section}) score=${result.score.toFixed(2)}: ${result.text.slice(0, 120)}`);
|
|
511
|
+
return {
|
|
512
|
+
content: [{ type: 'text', text: page.length > 0 ? lines.join('\n') : 'No results found.' }],
|
|
513
|
+
structuredContent: { total, offset, limit, results: page },
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
if (name === 'bclaw_estimation_report') {
|
|
517
|
+
const report = buildEstimationReport({ agent: args.agent, cwd });
|
|
518
|
+
const lines = [`Estimation Report — ${report.summary.total} completed plan(s)`];
|
|
519
|
+
if (report.summary.calibration_hint) {
|
|
520
|
+
lines.push(`Calibration: ${report.summary.calibration_hint}`);
|
|
521
|
+
lines.push(`Median ratio: ${report.summary.median_ratio}x · Mean: ${report.summary.mean_ratio}x`);
|
|
522
|
+
}
|
|
523
|
+
for (const e of report.entries) {
|
|
524
|
+
const est = e.estimated_minutes !== undefined ? `est:${e.estimated_minutes}min` : 'no estimate';
|
|
525
|
+
const act = e.elapsed_minutes !== undefined ? `actual:${e.elapsed_minutes}min` : 'no actual';
|
|
526
|
+
const ratio = e.ratio !== undefined ? ` ratio:${e.ratio}x` : '';
|
|
527
|
+
lines.push(`[${e.id.slice(0, 8)}] ${e.text.slice(0, 60)} — ${est} · ${act}${ratio}`);
|
|
528
|
+
}
|
|
529
|
+
return {
|
|
530
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
531
|
+
structuredContent: report,
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
if (name === 'bclaw_list_plans') {
|
|
535
|
+
let plans = loadState(cwd).plan_items;
|
|
536
|
+
// Direct lookup by ID
|
|
537
|
+
if (args.id) {
|
|
538
|
+
const plan = plans.find((p) => p.id === String(args.id) || p.short_label === String(args.id));
|
|
539
|
+
if (!plan) {
|
|
540
|
+
return { content: [{ type: 'text', text: `Plan '${args.id}' not found.` }], structuredContent: { total: 0, plans: [] } };
|
|
541
|
+
}
|
|
542
|
+
return {
|
|
543
|
+
content: [{ type: 'text', text: `[${plan.id}] ${plan.text} (${plan.status}, ${plan.priority})` }],
|
|
544
|
+
structuredContent: { total: 1, plans: [plan] },
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
// Filters
|
|
548
|
+
if (!args.all) {
|
|
549
|
+
plans = plans.filter((plan) => plan.status !== 'done' && plan.status !== 'dropped');
|
|
550
|
+
}
|
|
551
|
+
if (args.status) {
|
|
552
|
+
plans = plans.filter((plan) => plan.status === args.status);
|
|
553
|
+
}
|
|
554
|
+
if (args.type) {
|
|
555
|
+
plans = plans.filter((plan) => plan.type === args.type);
|
|
556
|
+
}
|
|
557
|
+
if (args.assignee) {
|
|
558
|
+
const assignee = String(args.assignee).toLowerCase();
|
|
559
|
+
plans = plans.filter((plan) => plan.assignee?.toLowerCase() === assignee);
|
|
560
|
+
}
|
|
561
|
+
if (args.project) {
|
|
562
|
+
const project = String(args.project).toLowerCase();
|
|
563
|
+
plans = plans.filter((plan) => plan.project?.toLowerCase() === project);
|
|
564
|
+
}
|
|
565
|
+
const totalFiltered = plans.length;
|
|
566
|
+
// Descendant discovery
|
|
567
|
+
const descendantGroups = args.recursive
|
|
568
|
+
? scanDescendantPlans(cwd, {
|
|
569
|
+
all: args.all,
|
|
570
|
+
status: args.status,
|
|
571
|
+
type: args.type,
|
|
572
|
+
assignee: args.assignee,
|
|
573
|
+
project: args.project,
|
|
574
|
+
})
|
|
575
|
+
: [];
|
|
576
|
+
const totalDescendantPlans = descendantGroups.reduce((sum, g) => sum + g.plans.length, 0);
|
|
577
|
+
// Pagination (local plans only)
|
|
578
|
+
const offset = Math.max(0, Number(args.offset) || 0);
|
|
579
|
+
const limit = Math.max(1, Number(args.limit) || 20);
|
|
580
|
+
const paginated = plans.slice(offset, offset + limit);
|
|
581
|
+
const lines = [];
|
|
582
|
+
if (args.recursive) {
|
|
583
|
+
lines.push(`── local (${totalFiltered} plans) ──`);
|
|
584
|
+
}
|
|
585
|
+
if (paginated.length === 0 && !args.recursive) {
|
|
586
|
+
lines.push('No plan items found.');
|
|
587
|
+
// Signal descendant plans when 0 local results
|
|
588
|
+
if (!args.recursive) {
|
|
589
|
+
const signalGroups = scanDescendantPlans(cwd, {
|
|
590
|
+
all: args.all,
|
|
591
|
+
status: args.status,
|
|
592
|
+
});
|
|
593
|
+
const signalTotal = signalGroups.reduce((sum, g) => sum + g.plans.length, 0);
|
|
594
|
+
if (signalTotal > 0) {
|
|
595
|
+
lines.push(`ℹ ${signalTotal} plan(s) found in ${signalGroups.length} descendant project(s) (use recursive: true to see all)`);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
else if (paginated.length > 0) {
|
|
600
|
+
if (!args.recursive) {
|
|
601
|
+
lines.push(`${totalFiltered} plan(s)${totalFiltered > paginated.length ? ` (showing ${offset + 1}-${offset + paginated.length})` : ''}:`);
|
|
602
|
+
}
|
|
603
|
+
for (const plan of paginated) {
|
|
604
|
+
const meta = [plan.type ?? 'feat', plan.status, plan.priority];
|
|
605
|
+
if (plan.assignee)
|
|
606
|
+
meta.push(`assignee ${plan.assignee}`);
|
|
607
|
+
if (plan.project)
|
|
608
|
+
meta.push(`project ${plan.project}`);
|
|
609
|
+
if (plan.depends_on.length > 0)
|
|
610
|
+
meta.push(`depends_on ${plan.depends_on.join(',')}`);
|
|
611
|
+
const tags = plan.tags.length ? ` [${plan.tags.join(', ')}]` : '';
|
|
612
|
+
lines.push(`[${plan.id}] ${plan.text} (${meta.join(' · ')})${tags}`);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
else {
|
|
616
|
+
lines.push(' (none)');
|
|
617
|
+
}
|
|
618
|
+
// Append descendant groups
|
|
619
|
+
for (const group of descendantGroups) {
|
|
620
|
+
const label = group.project_name ?? group.relative_path;
|
|
621
|
+
lines.push(`\n── ${label} (${group.plans.length} plans) ──`);
|
|
622
|
+
for (const plan of group.plans) {
|
|
623
|
+
const meta = [plan.type ?? 'feat', plan.status, plan.priority];
|
|
624
|
+
const tags = plan.tags.length ? ` [${plan.tags.join(', ')}]` : '';
|
|
625
|
+
lines.push(`[${plan.id}] ${plan.text} (${meta.join(' · ')})${tags}`);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
// Compact mode: strip heavy fields
|
|
629
|
+
const outputPlans = args.compact
|
|
630
|
+
? paginated.map(({ id, short_label, text, status, priority, tags, assignee, type }) => ({
|
|
631
|
+
id, short_label, text, status, priority, tags, assignee, type,
|
|
632
|
+
}))
|
|
633
|
+
: paginated;
|
|
634
|
+
return {
|
|
635
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
636
|
+
structuredContent: {
|
|
637
|
+
total: totalFiltered,
|
|
638
|
+
offset,
|
|
639
|
+
limit,
|
|
640
|
+
plans: outputPlans,
|
|
641
|
+
...(descendantGroups.length > 0 ? { descendants: descendantGroups, total_with_descendants: totalFiltered + totalDescendantPlans } : {}),
|
|
642
|
+
},
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
if (name === 'bclaw_list_sequences') {
|
|
646
|
+
const status = args.status;
|
|
647
|
+
const id = args.id;
|
|
648
|
+
const offset = Math.max(0, Number(args.offset) || 0);
|
|
649
|
+
const limit = Math.max(1, Number(args.limit) || 20);
|
|
650
|
+
let sequences = listSequences(cwd);
|
|
651
|
+
if (status) {
|
|
652
|
+
sequences = sequences.filter((sequence) => sequence.status === status);
|
|
653
|
+
}
|
|
654
|
+
if (id) {
|
|
655
|
+
sequences = sequences.filter((sequence) => sequence.id === id || sequence.short_label === id);
|
|
656
|
+
}
|
|
657
|
+
const total = sequences.length;
|
|
658
|
+
const page = sequences.slice(offset, offset + limit);
|
|
659
|
+
const compact = args.compact === true;
|
|
660
|
+
const lines = page.length === 0
|
|
661
|
+
? ['No sequences found.']
|
|
662
|
+
: [
|
|
663
|
+
`${total} sequence(s)${total > limit ? ` (showing ${offset + 1}-${offset + page.length})` : ''}:`,
|
|
664
|
+
...page.map((sequence) => compact
|
|
665
|
+
? `[${sequence.id}] ${sequence.name} (${sequence.status})`
|
|
666
|
+
: `[${sequence.id}] ${sequence.name} (${sequence.status}, items=${sequence.items.length})`),
|
|
667
|
+
];
|
|
668
|
+
return {
|
|
669
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
670
|
+
structuredContent: { total, offset, limit, sequences: page },
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
if (name === 'bclaw_list_claims') {
|
|
674
|
+
let claims = listClaims(cwd);
|
|
675
|
+
if (!args.all) {
|
|
676
|
+
claims = claims.filter((claim) => claim.status === 'active');
|
|
677
|
+
}
|
|
678
|
+
if (args.project) {
|
|
679
|
+
claims = claims.filter((claim) => claim.project === args.project);
|
|
680
|
+
}
|
|
681
|
+
if (args.plan) {
|
|
682
|
+
claims = claims.filter((claim) => claim.plan_id === args.plan);
|
|
683
|
+
}
|
|
684
|
+
if (args.agent) {
|
|
685
|
+
claims = claims.filter((claim) => claim.agent === args.agent);
|
|
686
|
+
}
|
|
687
|
+
const total = claims.length;
|
|
688
|
+
const offset = Math.max(0, Number(args.offset) || 0);
|
|
689
|
+
const limit = Math.max(1, Number(args.limit) || 20);
|
|
690
|
+
const page = claims.slice(offset, offset + limit);
|
|
691
|
+
const label = args.all ? 'claim(s)' : 'active claim(s)';
|
|
692
|
+
const lines = page.length === 0
|
|
693
|
+
? ['No active claims.']
|
|
694
|
+
: [
|
|
695
|
+
`${total} ${label}${total > limit ? ` (showing ${offset + 1}-${offset + page.length})` : ''}:`,
|
|
696
|
+
...page.map((claim) => {
|
|
697
|
+
const status = claim.status !== 'active' ? ` (${claim.status})` : '';
|
|
698
|
+
const extras = [];
|
|
699
|
+
if (claim.session_id)
|
|
700
|
+
extras.push(`session ${claim.session_id.slice(-8)}`);
|
|
701
|
+
if (claim.plan_id)
|
|
702
|
+
extras.push(`plan ${claim.plan_id}`);
|
|
703
|
+
if (claim.project)
|
|
704
|
+
extras.push(`project ${claim.project}`);
|
|
705
|
+
const suffix = extras.length ? ` [${extras.join(', ')}]` : '';
|
|
706
|
+
return `[${claim.id}] ${claim.agent} -> ${claim.scope}: ${claim.description}${suffix}${status}`;
|
|
707
|
+
}),
|
|
708
|
+
];
|
|
709
|
+
return {
|
|
710
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
711
|
+
structuredContent: { total, offset, limit, claims: page },
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
if (name === 'bclaw_list_assignments') {
|
|
715
|
+
const status = validateEnumFilter(args.status, AssignmentStatusSchema, 'assignment status');
|
|
716
|
+
const id = args.id;
|
|
717
|
+
const claimId = args.claimId;
|
|
718
|
+
const planId = args.planId;
|
|
719
|
+
const sequenceId = args.sequenceId;
|
|
720
|
+
const agent = args.agent;
|
|
721
|
+
const offset = Math.max(0, Number(args.offset) || 0);
|
|
722
|
+
const limit = Math.max(1, Number(args.limit) || 20);
|
|
723
|
+
let assignments = listAssignments(cwd, {
|
|
724
|
+
...(status ? { status } : {}),
|
|
725
|
+
...(agent ? { agent } : {}),
|
|
726
|
+
...(claimId ? { claim_id: claimId } : {}),
|
|
727
|
+
...(planId ? { plan_id: planId } : {}),
|
|
728
|
+
...(sequenceId ? { sequence_id: sequenceId } : {}),
|
|
729
|
+
});
|
|
730
|
+
if (id) {
|
|
731
|
+
assignments = assignments.filter((assignment) => assignment.id === id || assignment.short_label === id);
|
|
732
|
+
}
|
|
733
|
+
const total = assignments.length;
|
|
734
|
+
const page = assignments.slice(offset, offset + limit);
|
|
735
|
+
const compact = args.compact === true;
|
|
736
|
+
const lines = page.length === 0
|
|
737
|
+
? ['No assignments found.']
|
|
738
|
+
: [
|
|
739
|
+
`${total} assignment(s)${total > limit ? ` (showing ${offset + 1}-${offset + page.length})` : ''}:`,
|
|
740
|
+
...page.map((assignment) => {
|
|
741
|
+
if (compact) {
|
|
742
|
+
return `[${assignment.id}] ${assignment.agent} (${assignment.status}) -> ${assignment.scope}`;
|
|
743
|
+
}
|
|
744
|
+
const refs = [];
|
|
745
|
+
if (assignment.claim_id)
|
|
746
|
+
refs.push(`claim ${assignment.claim_id}`);
|
|
747
|
+
if (assignment.plan_id)
|
|
748
|
+
refs.push(`plan ${assignment.plan_id}`);
|
|
749
|
+
if (assignment.sequence_id)
|
|
750
|
+
refs.push(`sequence ${assignment.sequence_id}`);
|
|
751
|
+
if (assignment.session_id)
|
|
752
|
+
refs.push(`session ${assignment.session_id.slice(-8)}`);
|
|
753
|
+
const suffix = refs.length ? ` [${refs.join(', ')}]` : '';
|
|
754
|
+
return `[${assignment.id}] ${assignment.agent} (${assignment.status}) -> ${assignment.scope}: ${assignment.description}${suffix}`;
|
|
755
|
+
}),
|
|
756
|
+
];
|
|
757
|
+
const outputAssignments = compact
|
|
758
|
+
? page.map((assignment) => ({
|
|
759
|
+
id: assignment.id,
|
|
760
|
+
short_label: assignment.short_label,
|
|
761
|
+
agent: assignment.agent,
|
|
762
|
+
status: assignment.status,
|
|
763
|
+
scope: assignment.scope,
|
|
764
|
+
claim_id: assignment.claim_id,
|
|
765
|
+
plan_id: assignment.plan_id,
|
|
766
|
+
sequence_id: assignment.sequence_id,
|
|
767
|
+
updated_at: assignment.updated_at,
|
|
768
|
+
last_heartbeat_at: assignment.last_heartbeat_at,
|
|
769
|
+
}))
|
|
770
|
+
: page;
|
|
771
|
+
return {
|
|
772
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
773
|
+
structuredContent: { total, offset, limit, assignments: outputAssignments },
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
if (name === 'bclaw_list_runs') {
|
|
777
|
+
const status = validateEnumFilter(args.status, AgentRunStatusSchema, 'run status');
|
|
778
|
+
const transport = validateEnumFilter(args.transport, AgentRunTransportSchema, 'run transport');
|
|
779
|
+
const id = args.id;
|
|
780
|
+
const assignmentId = args.assignmentId;
|
|
781
|
+
const claimId = args.claimId;
|
|
782
|
+
const planId = args.planId;
|
|
783
|
+
const sequenceId = args.sequenceId;
|
|
784
|
+
const agent = args.agent;
|
|
785
|
+
const offset = Math.max(0, Number(args.offset) || 0);
|
|
786
|
+
const limit = Math.max(1, Number(args.limit) || 20);
|
|
787
|
+
let runs = listAgentRuns(cwd, {
|
|
788
|
+
...(status ? { status } : {}),
|
|
789
|
+
...(transport ? { transport } : {}),
|
|
790
|
+
...(agent ? { agent } : {}),
|
|
791
|
+
...(assignmentId ? { assignment_id: assignmentId } : {}),
|
|
792
|
+
...(claimId ? { claim_id: claimId } : {}),
|
|
793
|
+
...(planId ? { plan_id: planId } : {}),
|
|
794
|
+
...(sequenceId ? { sequence_id: sequenceId } : {}),
|
|
795
|
+
});
|
|
796
|
+
if (id) {
|
|
797
|
+
runs = runs.filter((run) => run.id === id || run.short_label === id);
|
|
798
|
+
}
|
|
799
|
+
const total = runs.length;
|
|
800
|
+
const page = runs.slice(offset, offset + limit);
|
|
801
|
+
const compact = args.compact === true;
|
|
802
|
+
const lines = page.length === 0
|
|
803
|
+
? ['No runs found.']
|
|
804
|
+
: [
|
|
805
|
+
`${total} run(s)${total > limit ? ` (showing ${offset + 1}-${offset + page.length})` : ''}:`,
|
|
806
|
+
...page.map((run) => {
|
|
807
|
+
if (compact) {
|
|
808
|
+
return `[${run.id}] ${run.agent} (${run.status}/${run.transport}) -> ${run.assignment_id}`;
|
|
809
|
+
}
|
|
810
|
+
const refs = [`assignment ${run.assignment_id}`, `attempt ${run.attempt_index}`];
|
|
811
|
+
if (run.claim_id)
|
|
812
|
+
refs.push(`claim ${run.claim_id}`);
|
|
813
|
+
if (run.plan_id)
|
|
814
|
+
refs.push(`plan ${run.plan_id}`);
|
|
815
|
+
if (run.session_id)
|
|
816
|
+
refs.push(`session ${run.session_id.slice(-8)}`);
|
|
817
|
+
const suffix = refs.length ? ` [${refs.join(', ')}]` : '';
|
|
818
|
+
return `[${run.id}] ${run.agent} (${run.status}/${run.transport}) -> ${run.scope}: ${run.description}${suffix}`;
|
|
819
|
+
}),
|
|
820
|
+
];
|
|
821
|
+
const outputRuns = compact
|
|
822
|
+
? page.map((run) => ({
|
|
823
|
+
id: run.id,
|
|
824
|
+
short_label: run.short_label,
|
|
825
|
+
agent: run.agent,
|
|
826
|
+
status: run.status,
|
|
827
|
+
transport: run.transport,
|
|
828
|
+
assignment_id: run.assignment_id,
|
|
829
|
+
claim_id: run.claim_id,
|
|
830
|
+
plan_id: run.plan_id,
|
|
831
|
+
sequence_id: run.sequence_id,
|
|
832
|
+
attempt_index: run.attempt_index,
|
|
833
|
+
updated_at: run.updated_at,
|
|
834
|
+
last_event_at: run.last_event_at,
|
|
835
|
+
}))
|
|
836
|
+
: page;
|
|
837
|
+
return {
|
|
838
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
839
|
+
structuredContent: { total, offset, limit, runs: outputRuns },
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
if (name === 'bclaw_assignment_events') {
|
|
843
|
+
const id = args.id;
|
|
844
|
+
const assignmentId = args.assignmentId;
|
|
845
|
+
const runId = args.runId;
|
|
846
|
+
const claimId = args.claimId;
|
|
847
|
+
const sessionId = args.sessionId;
|
|
848
|
+
const agent = args.agent;
|
|
849
|
+
const eventType = args.eventType;
|
|
850
|
+
const offset = Math.max(0, Number(args.offset) || 0);
|
|
851
|
+
const limit = Math.max(1, Number(args.limit) || 20);
|
|
852
|
+
const compact = args.compact === true;
|
|
853
|
+
// pln#496 Phase 2 (steps stp_344f99b3 + stp_e2b4429c): reconcile any
|
|
854
|
+
// non-terminal runs in the query scope before reading events. Targeted —
|
|
855
|
+
// we only reconcile when the caller is asking about a specific run /
|
|
856
|
+
// assignment / claim, never in the broad-list-all case (too aggressive
|
|
857
|
+
// and rarely actionable). This converges silent-completion cases (codex
|
|
858
|
+
// committed but never called bclaw_assignment_update) and surfaces
|
|
859
|
+
// delivered_but_unverified for spawns past the 60s grace with no
|
|
860
|
+
// life-sign — see runtime_note run_77e65e77 for the empirical case.
|
|
861
|
+
try {
|
|
862
|
+
if (runId) {
|
|
863
|
+
reconcileAgentRun(runId, cwd);
|
|
864
|
+
}
|
|
865
|
+
else if (assignmentId) {
|
|
866
|
+
for (const run of listAgentRuns(cwd, { assignment_id: assignmentId })) {
|
|
867
|
+
reconcileAgentRun(run.id, cwd);
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
else if (claimId) {
|
|
871
|
+
for (const run of listAgentRuns(cwd, { claim_id: claimId })) {
|
|
872
|
+
reconcileAgentRun(run.id, cwd);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
catch { /* defensive: never block events query on reconcile failure */ }
|
|
877
|
+
let events = queryRuntimeEvents({
|
|
878
|
+
...(id ? { id } : {}),
|
|
879
|
+
...(assignmentId ? { assignment_id: assignmentId } : {}),
|
|
880
|
+
...(runId ? { run_id: runId } : {}),
|
|
881
|
+
...(claimId ? { claim_id: claimId } : {}),
|
|
882
|
+
...(sessionId ? { session_id: sessionId } : {}),
|
|
883
|
+
...(agent ? { agent } : {}),
|
|
884
|
+
...(eventType ? { event_type: eventType } : {}),
|
|
885
|
+
}, cwd);
|
|
886
|
+
const total = events.length;
|
|
887
|
+
const page = events.slice(offset, offset + limit);
|
|
888
|
+
const lines = page.length === 0
|
|
889
|
+
? ['No runtime events found.']
|
|
890
|
+
: [
|
|
891
|
+
`${total} runtime event(s)${total > limit ? ` (showing ${offset + 1}-${offset + page.length})` : ''}:`,
|
|
892
|
+
...page.map((event) => {
|
|
893
|
+
if (compact) {
|
|
894
|
+
return `[${event.id}] ${event.event_type} ${event.agent}${event.assignment_id ? ` assignment=${event.assignment_id}` : ''}${event.run_id ? ` run=${event.run_id}` : ''}`;
|
|
895
|
+
}
|
|
896
|
+
const refs = [];
|
|
897
|
+
if (event.assignment_id)
|
|
898
|
+
refs.push(`assignment ${event.assignment_id}`);
|
|
899
|
+
if (event.run_id)
|
|
900
|
+
refs.push(`run ${event.run_id}`);
|
|
901
|
+
if (event.claim_id)
|
|
902
|
+
refs.push(`claim ${event.claim_id}`);
|
|
903
|
+
if (event.session_id)
|
|
904
|
+
refs.push(`session ${event.session_id.slice(-8)}`);
|
|
905
|
+
const suffix = refs.length ? ` [${refs.join(', ')}]` : '';
|
|
906
|
+
return `[${event.id}] ${event.event_type} ${event.agent}: ${event.text}${suffix}`;
|
|
907
|
+
}),
|
|
908
|
+
];
|
|
909
|
+
const outputEvents = compact
|
|
910
|
+
? page.map((event) => ({
|
|
911
|
+
id: event.id,
|
|
912
|
+
created_at: event.created_at,
|
|
913
|
+
event_type: event.event_type,
|
|
914
|
+
agent: event.agent,
|
|
915
|
+
assignment_id: event.assignment_id,
|
|
916
|
+
run_id: event.run_id,
|
|
917
|
+
claim_id: event.claim_id,
|
|
918
|
+
session_id: event.session_id,
|
|
919
|
+
status: event.status,
|
|
920
|
+
status_reason: event.status_reason,
|
|
921
|
+
}))
|
|
922
|
+
: page;
|
|
923
|
+
return {
|
|
924
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
925
|
+
structuredContent: { total, offset, limit, events: outputEvents },
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
if (name === 'bclaw_list_actions') {
|
|
929
|
+
const status = validateEnumFilter(args.status, ActionRequiredStatusSchema, 'action status');
|
|
930
|
+
const kind = validateEnumFilter(args.kind, ActionRequiredKindSchema, 'action kind');
|
|
931
|
+
const id = args.id;
|
|
932
|
+
const assignmentId = args.assignmentId;
|
|
933
|
+
const runId = args.runId;
|
|
934
|
+
const claimId = args.claimId;
|
|
935
|
+
const agent = args.agent;
|
|
936
|
+
const offset = Math.max(0, Number(args.offset) || 0);
|
|
937
|
+
const limit = Math.max(1, Number(args.limit) || 20);
|
|
938
|
+
const compact = args.compact === true;
|
|
939
|
+
let actions = listActionRequired(cwd, {
|
|
940
|
+
...(status ? { status } : {}),
|
|
941
|
+
...(kind ? { kind } : {}),
|
|
942
|
+
...(agent ? { agent } : {}),
|
|
943
|
+
...(assignmentId ? { assignment_id: assignmentId } : {}),
|
|
944
|
+
...(runId ? { run_id: runId } : {}),
|
|
945
|
+
...(claimId ? { claim_id: claimId } : {}),
|
|
946
|
+
});
|
|
947
|
+
if (id) {
|
|
948
|
+
actions = actions.filter((action) => action.id === id || action.short_label === id);
|
|
949
|
+
}
|
|
950
|
+
const total = actions.length;
|
|
951
|
+
const page = actions.slice(offset, offset + limit);
|
|
952
|
+
const lines = page.length === 0
|
|
953
|
+
? ['No actions found.']
|
|
954
|
+
: [
|
|
955
|
+
`${total} action(s)${total > limit ? ` (showing ${offset + 1}-${offset + page.length})` : ''}:`,
|
|
956
|
+
...page.map((action) => {
|
|
957
|
+
if (compact) {
|
|
958
|
+
return `[${action.id}] ${action.kind} (${action.status}) -> ${action.assignment_id}`;
|
|
959
|
+
}
|
|
960
|
+
const refs = [`assignment ${action.assignment_id}`];
|
|
961
|
+
if (action.run_id)
|
|
962
|
+
refs.push(`run ${action.run_id}`);
|
|
963
|
+
if (action.claim_id)
|
|
964
|
+
refs.push(`claim ${action.claim_id}`);
|
|
965
|
+
if (action.session_id)
|
|
966
|
+
refs.push(`session ${action.session_id.slice(-8)}`);
|
|
967
|
+
const suffix = refs.length ? ` [${refs.join(', ')}]` : '';
|
|
968
|
+
return `[${action.id}] ${action.kind} (${action.status}) ${action.title}: ${action.prompt}${suffix}`;
|
|
969
|
+
}),
|
|
970
|
+
];
|
|
971
|
+
const outputActions = compact
|
|
972
|
+
? page.map((action) => ({
|
|
973
|
+
id: action.id,
|
|
974
|
+
kind: action.kind,
|
|
975
|
+
status: action.status,
|
|
976
|
+
assignment_id: action.assignment_id,
|
|
977
|
+
run_id: action.run_id,
|
|
978
|
+
claim_id: action.claim_id,
|
|
979
|
+
title: action.title,
|
|
980
|
+
updated_at: action.updated_at,
|
|
981
|
+
resolved_at: action.resolved_at,
|
|
982
|
+
}))
|
|
983
|
+
: page;
|
|
984
|
+
return {
|
|
985
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
986
|
+
structuredContent: { total, offset, limit, actions: outputActions },
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
if (name === 'bclaw_list_agents') {
|
|
990
|
+
const agents = listAgentIdentities(cwd);
|
|
991
|
+
const current = resolveCurrentAgentIdentity(cwd);
|
|
992
|
+
const reputation = args.includeReputation ? buildReputationSnapshot(cwd) : undefined;
|
|
993
|
+
const reputationById = new Map((reputation?.agents ?? []).map((agent) => [agent.agent_id ?? agent.key, toPublicReputationSummary(agent)]));
|
|
994
|
+
const structuredAgents = args.includeReputation
|
|
995
|
+
? agents.map((agent) => ({
|
|
996
|
+
...agent,
|
|
997
|
+
reputation: reputationById.get(agent.agent_id),
|
|
998
|
+
}))
|
|
999
|
+
: agents;
|
|
1000
|
+
const lines = structuredAgents.length === 0
|
|
1001
|
+
? ['No registered agents.']
|
|
1002
|
+
: [
|
|
1003
|
+
`${structuredAgents.length} registered agent(s):`,
|
|
1004
|
+
...structuredAgents.map((agent) => {
|
|
1005
|
+
const reputation = agent.reputation;
|
|
1006
|
+
const currentLabel = current?.agent_id === agent.agent_id ? ' [current]' : '';
|
|
1007
|
+
const capabilitiesLabel = agent.capabilities.length > 0 ? ` caps=${agent.capabilities.join(',')}` : '';
|
|
1008
|
+
const fingerprintLabel = agent.identity_key ? ` fp=${agent.identity_key.fingerprint.slice(0, 12)}` : '';
|
|
1009
|
+
const reputationLabel = reputation
|
|
1010
|
+
? ` trust=${reputation.internal_trust} cq=${reputation.contribution_quality} rv=${reputation.review_reliability} ct=${reputation.continuity_hygiene}`
|
|
1011
|
+
: '';
|
|
1012
|
+
return `- ${agent.agent_name} (${agent.agent_id}, kind=${agent.kind})${currentLabel}${reputationLabel}${capabilitiesLabel}${fingerprintLabel}`;
|
|
1013
|
+
}),
|
|
1014
|
+
];
|
|
1015
|
+
return {
|
|
1016
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
1017
|
+
structuredContent: {
|
|
1018
|
+
current_agent_id: current?.agent_id,
|
|
1019
|
+
current_agent: current?.agent_name,
|
|
1020
|
+
agents: structuredAgents,
|
|
1021
|
+
},
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1024
|
+
if (name === 'bclaw_list_instructions') {
|
|
1025
|
+
const config = loadConfig(cwd);
|
|
1026
|
+
const project = args.project;
|
|
1027
|
+
const inferredProject = project ?? inferProjectFromTarget(args.path, config);
|
|
1028
|
+
const resolvedAgent = args.resolved ? resolveAgentScope(args.agent) : args.agent;
|
|
1029
|
+
const source = args.resolved
|
|
1030
|
+
? resolveInstructions(loadInstructions(cwd), { project: inferredProject, agent: resolvedAgent })
|
|
1031
|
+
: loadInstructions(cwd);
|
|
1032
|
+
let entries = source;
|
|
1033
|
+
if (args.active) {
|
|
1034
|
+
entries = entries.filter((entry) => entry.active);
|
|
1035
|
+
}
|
|
1036
|
+
if (args.layer) {
|
|
1037
|
+
entries = entries.filter((entry) => entry.layer === args.layer);
|
|
1038
|
+
}
|
|
1039
|
+
if (inferredProject) {
|
|
1040
|
+
entries = entries.filter((entry) => entry.layer !== 'project' || entry.scope === inferredProject);
|
|
1041
|
+
}
|
|
1042
|
+
if (args.agent) {
|
|
1043
|
+
entries = entries.filter((entry) => entry.layer !== 'agent' || entry.scope === args.agent);
|
|
1044
|
+
}
|
|
1045
|
+
const total = entries.length;
|
|
1046
|
+
const offset = Math.max(0, Number(args.offset) || 0);
|
|
1047
|
+
const limit = Math.max(1, Number(args.limit) || 20);
|
|
1048
|
+
const page = entries.slice(offset, offset + limit);
|
|
1049
|
+
const lines = page.length === 0
|
|
1050
|
+
? ['No instructions found.']
|
|
1051
|
+
: [
|
|
1052
|
+
`${total} instruction(s)${total > limit ? ` (showing ${offset + 1}-${offset + page.length})` : ''}:`,
|
|
1053
|
+
...page.map((entry) => {
|
|
1054
|
+
const scope = entry.scope ? `:${entry.scope}` : '';
|
|
1055
|
+
const flags = [entry.layer];
|
|
1056
|
+
if (!entry.active)
|
|
1057
|
+
flags.push('inactive');
|
|
1058
|
+
if (entry.supersedes)
|
|
1059
|
+
flags.push(`supersedes ${entry.supersedes}`);
|
|
1060
|
+
const tags = entry.tags.length ? ` [${entry.tags.join(', ')}]` : '';
|
|
1061
|
+
return `[${entry.id}] <${entry.layer}${scope}> ${entry.text} (${flags.join(' · ')})${tags}`;
|
|
1062
|
+
}),
|
|
1063
|
+
];
|
|
1064
|
+
return {
|
|
1065
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
1066
|
+
structuredContent: { total, offset, limit, instructions: page },
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1069
|
+
if (name === 'bclaw_list_candidates') {
|
|
1070
|
+
const status = String(args.status ?? 'pending').toLowerCase();
|
|
1071
|
+
let candidates = status === 'accepted'
|
|
1072
|
+
? listArchivedCandidates('accepted', cwd)
|
|
1073
|
+
: status === 'rejected'
|
|
1074
|
+
? listArchivedCandidates('rejected', cwd)
|
|
1075
|
+
: status === 'all'
|
|
1076
|
+
? [
|
|
1077
|
+
...listCandidates('pending', cwd),
|
|
1078
|
+
...listArchivedCandidates('accepted', cwd),
|
|
1079
|
+
...listArchivedCandidates('rejected', cwd),
|
|
1080
|
+
]
|
|
1081
|
+
: listCandidates('pending', cwd);
|
|
1082
|
+
// source / auto_generated filters — resolved source defaults missing field to 'human' (backward compat)
|
|
1083
|
+
if (args.source !== undefined) {
|
|
1084
|
+
const validSources = ['auto', 'agent', 'human'];
|
|
1085
|
+
const sourceArg = String(args.source);
|
|
1086
|
+
if (validSources.includes(sourceArg)) {
|
|
1087
|
+
candidates = candidates.filter((c) => resolvedSource(c) === sourceArg);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
if (args.auto_generated === false) {
|
|
1091
|
+
candidates = candidates.filter((c) => resolvedSource(c) !== 'auto');
|
|
1092
|
+
}
|
|
1093
|
+
else if (args.auto_generated === true) {
|
|
1094
|
+
candidates = candidates.filter((c) => resolvedSource(c) === 'auto');
|
|
1095
|
+
}
|
|
1096
|
+
if (args.type) {
|
|
1097
|
+
candidates = candidates.filter((candidate) => candidate.type === args.type);
|
|
1098
|
+
}
|
|
1099
|
+
if (args.assignee) {
|
|
1100
|
+
const assignee = String(args.assignee).toLowerCase();
|
|
1101
|
+
candidates = candidates.filter((candidate) => getReviewAssignee(candidate.tags)?.toLowerCase() === assignee);
|
|
1102
|
+
}
|
|
1103
|
+
const total = candidates.length;
|
|
1104
|
+
const offset = Math.max(0, Number(args.offset) || 0);
|
|
1105
|
+
const limit = Math.max(1, Number(args.limit) || 20);
|
|
1106
|
+
const page = candidates.slice(offset, offset + limit);
|
|
1107
|
+
const isCompact = args.compact === true;
|
|
1108
|
+
const lines = page.length === 0
|
|
1109
|
+
? ['No candidates found.']
|
|
1110
|
+
: [
|
|
1111
|
+
`${total} candidate(s)${total > limit ? ` (showing ${offset + 1}-${offset + page.length})` : ''}:`,
|
|
1112
|
+
...page.map((candidate) => {
|
|
1113
|
+
if (isCompact) {
|
|
1114
|
+
return `[${candidate.id}] ${candidate.type}/${candidate.status}: ${candidate.text.slice(0, 120)}${candidate.text.length > 120 ? '…' : ''}`;
|
|
1115
|
+
}
|
|
1116
|
+
const assignee = getReviewAssignee(candidate.tags);
|
|
1117
|
+
const tags = candidate.tags.length ? ` [${candidate.tags.join(', ')}]` : '';
|
|
1118
|
+
const assigneeLabel = assignee ? ` assignee=${assignee}` : '';
|
|
1119
|
+
return `[${candidate.id}] ${candidate.type}/${candidate.status}${assigneeLabel}: ${candidate.text}${tags}`;
|
|
1120
|
+
}),
|
|
1121
|
+
];
|
|
1122
|
+
return {
|
|
1123
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
1124
|
+
structuredContent: { total, offset, limit, candidates: page },
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
if (name === 'bclaw_get_capabilities') {
|
|
1128
|
+
const allCapabilities = listCapabilities(cwd);
|
|
1129
|
+
const filtered = allCapabilities.filter((cap) => {
|
|
1130
|
+
const categoryFilter = args.category;
|
|
1131
|
+
const tagsFilter = args.tags;
|
|
1132
|
+
if (categoryFilter && cap.category !== categoryFilter)
|
|
1133
|
+
return false;
|
|
1134
|
+
if (tagsFilter && tagsFilter.length > 0) {
|
|
1135
|
+
if (!tagsFilter.every((tag) => cap.tags.includes(tag)))
|
|
1136
|
+
return false;
|
|
1137
|
+
}
|
|
1138
|
+
return true;
|
|
1139
|
+
});
|
|
1140
|
+
const lines = [`Capabilities (${filtered.length}):`];
|
|
1141
|
+
filtered.forEach((cap) => {
|
|
1142
|
+
lines.push(`\n[${cap.id}] ${cap.name}`);
|
|
1143
|
+
lines.push(` Category: ${cap.category}`);
|
|
1144
|
+
lines.push(` Author: ${cap.author}`);
|
|
1145
|
+
if (cap.tags.length > 0) {
|
|
1146
|
+
lines.push(` Tags: ${cap.tags.join(', ')}`);
|
|
1147
|
+
}
|
|
1148
|
+
});
|
|
1149
|
+
return {
|
|
1150
|
+
content: [{ type: 'text', text: lines.join('\n') || 'No capabilities found.' }],
|
|
1151
|
+
structuredContent: { total: filtered.length, capabilities: filtered },
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
if (name === 'bclaw_list_tools') {
|
|
1155
|
+
const allTools = listRegistryTools(cwd);
|
|
1156
|
+
const filtered = allTools.filter((tool) => {
|
|
1157
|
+
const typeFilter = args.type;
|
|
1158
|
+
const tagsFilter = args.tags;
|
|
1159
|
+
if (typeFilter && tool.type !== typeFilter)
|
|
1160
|
+
return false;
|
|
1161
|
+
if (tagsFilter && tagsFilter.length > 0) {
|
|
1162
|
+
if (!tagsFilter.every((tag) => tool.tags.includes(tag)))
|
|
1163
|
+
return false;
|
|
1164
|
+
}
|
|
1165
|
+
return true;
|
|
1166
|
+
});
|
|
1167
|
+
const lines = [`Tools (${filtered.length}):`];
|
|
1168
|
+
filtered.forEach((tool) => {
|
|
1169
|
+
lines.push(`\n[${tool.id}] ${tool.name}`);
|
|
1170
|
+
lines.push(` Type: ${tool.type}`);
|
|
1171
|
+
lines.push(` Author: ${tool.author}`);
|
|
1172
|
+
if (tool.tags.length > 0) {
|
|
1173
|
+
lines.push(` Tags: ${tool.tags.join(', ')}`);
|
|
1174
|
+
}
|
|
1175
|
+
});
|
|
1176
|
+
return {
|
|
1177
|
+
content: [{ type: 'text', text: lines.join('\n') || 'No tools found.' }],
|
|
1178
|
+
structuredContent: { total: filtered.length, tools: filtered },
|
|
1179
|
+
};
|
|
1180
|
+
}
|
|
1181
|
+
if (name === 'bclaw_search_tools') {
|
|
1182
|
+
const query = String(args.query ?? '');
|
|
1183
|
+
if (!query) {
|
|
1184
|
+
throw new Error('Missing required argument: query');
|
|
1185
|
+
}
|
|
1186
|
+
const allTools = listRegistryTools(cwd);
|
|
1187
|
+
const queryLower = query.toLowerCase();
|
|
1188
|
+
const filtered = allTools.filter((tool) => {
|
|
1189
|
+
const typeFilter = args.type;
|
|
1190
|
+
const tagsFilter = args.tags;
|
|
1191
|
+
if (typeFilter && tool.type !== typeFilter)
|
|
1192
|
+
return false;
|
|
1193
|
+
if (tagsFilter && tagsFilter.length > 0) {
|
|
1194
|
+
if (!tagsFilter.every((tag) => tool.tags.includes(tag)))
|
|
1195
|
+
return false;
|
|
1196
|
+
}
|
|
1197
|
+
return (tool.name.toLowerCase().includes(queryLower) ||
|
|
1198
|
+
tool.description.toLowerCase().includes(queryLower) ||
|
|
1199
|
+
tool.tags.some((tag) => tag.toLowerCase().includes(queryLower)));
|
|
1200
|
+
});
|
|
1201
|
+
const lines = [`Search results for '${query}' (${filtered.length} tool(s)):`];
|
|
1202
|
+
filtered.forEach((tool) => {
|
|
1203
|
+
lines.push(`\n[${tool.id}] ${tool.name}`);
|
|
1204
|
+
lines.push(` Type: ${tool.type}`);
|
|
1205
|
+
});
|
|
1206
|
+
return {
|
|
1207
|
+
content: [{ type: 'text', text: lines.join('\n') || 'No tools found.' }],
|
|
1208
|
+
structuredContent: { query, total: filtered.length, tools: filtered },
|
|
1209
|
+
};
|
|
1210
|
+
}
|
|
1211
|
+
if (name === 'bclaw_get_discovery') {
|
|
1212
|
+
const refresh = args.refresh !== false; // default: true
|
|
1213
|
+
const noSave = args.noSave;
|
|
1214
|
+
let profile;
|
|
1215
|
+
if (!refresh) {
|
|
1216
|
+
profile = loadDiscoveryProfile(cwd);
|
|
1217
|
+
}
|
|
1218
|
+
if (!profile) {
|
|
1219
|
+
profile = buildProjectDiscovery({ cwd });
|
|
1220
|
+
if (!noSave) {
|
|
1221
|
+
saveDiscoveryProfile(profile, cwd);
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
return {
|
|
1225
|
+
content: [{ type: 'text', text: renderDiscoverySummary(profile) }],
|
|
1226
|
+
structuredContent: { ...profile, schema_version: SCHEMA_VERSION },
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
1229
|
+
if (name === 'bclaw_conflict_check') {
|
|
1230
|
+
const agentNameArg = args.agent;
|
|
1231
|
+
const agentIdArg = args.agentId;
|
|
1232
|
+
const currentAgentName = agentNameArg ?? resolveCurrentAgentName(cwd);
|
|
1233
|
+
const allClaimsForCheck = listClaims(cwd).filter((c) => c.status === 'active');
|
|
1234
|
+
const myClaimsForCheck = allClaimsForCheck.filter((c) => agentIdArg ? c.agent_id === agentIdArg : c.agent === currentAgentName);
|
|
1235
|
+
const otherClaimsForCheck = allClaimsForCheck.filter((c) => agentIdArg ? c.agent_id !== agentIdArg : c.agent !== currentAgentName);
|
|
1236
|
+
const conflicts = [];
|
|
1237
|
+
for (const mine of myClaimsForCheck) {
|
|
1238
|
+
const myScopes = mine.scope.replace(/\\/g, '/').split(/\s+/);
|
|
1239
|
+
for (const other of otherClaimsForCheck) {
|
|
1240
|
+
const otherScopes = other.scope.replace(/\\/g, '/').split(/\s+/);
|
|
1241
|
+
for (const ms of myScopes) {
|
|
1242
|
+
for (const os of otherScopes) {
|
|
1243
|
+
if (ms === os || ms.startsWith(os + '/') || os.startsWith(ms + '/')) {
|
|
1244
|
+
conflicts.push({
|
|
1245
|
+
my_claim: mine.id, my_scope: mine.scope,
|
|
1246
|
+
other_claim: other.id, other_agent: other.agent, other_scope: other.scope,
|
|
1247
|
+
reason: ms === os ? `exact: ${ms}` : `overlap: ${ms} ↔ ${os}`,
|
|
1248
|
+
});
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
const text = conflicts.length === 0
|
|
1255
|
+
? `No claim conflicts for ${currentAgentName}.`
|
|
1256
|
+
: `${conflicts.length} conflict(s) found:\n${conflicts.map((c) => ` ${c.my_scope} ↔ ${c.other_agent}:${c.other_scope} (${c.reason})`).join('\n')}`;
|
|
1257
|
+
return {
|
|
1258
|
+
content: [{ type: 'text', text }],
|
|
1259
|
+
structuredContent: { agent: currentAgentName, conflicts, total: conflicts.length, schema_version: SCHEMA_VERSION },
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
if (name === 'bclaw_switch') {
|
|
1263
|
+
if (args.list === true) {
|
|
1264
|
+
try {
|
|
1265
|
+
const result = listAvailableProjects(cwd);
|
|
1266
|
+
const lines = result.projects.map(p => {
|
|
1267
|
+
const marker = p.active ? '→' : ' ';
|
|
1268
|
+
const label = p.name ? `${p.name} (${p.relative_path})` : p.relative_path;
|
|
1269
|
+
return `${marker} ${label}`;
|
|
1270
|
+
});
|
|
1271
|
+
return {
|
|
1272
|
+
content: [{ type: 'text', text: lines.length > 0 ? `Projects in workspace:\n${lines.join('\n')}` : 'No projects found.' }],
|
|
1273
|
+
structuredContent: { ...result, schema_version: SCHEMA_VERSION },
|
|
1274
|
+
};
|
|
1275
|
+
}
|
|
1276
|
+
catch (err) {
|
|
1277
|
+
return createToolErrorResponse('switch_error', err instanceof Error ? err.message : String(err));
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
if (args.clear === true) {
|
|
1281
|
+
try {
|
|
1282
|
+
const session = loadCurrentSession(cwd);
|
|
1283
|
+
if (session?.active_project) {
|
|
1284
|
+
const { active_project: _removed, ...rest } = session;
|
|
1285
|
+
saveCurrentSession(rest, cwd);
|
|
1286
|
+
}
|
|
1287
|
+
return {
|
|
1288
|
+
content: [{ type: 'text', text: '✔ Active project cleared. Commands will use workspace root.' }],
|
|
1289
|
+
structuredContent: { cleared: true, schema_version: SCHEMA_VERSION },
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
catch (err) {
|
|
1293
|
+
return createToolErrorResponse('switch_error', err instanceof Error ? err.message : String(err));
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
const projectRef = args.project;
|
|
1297
|
+
if (!projectRef) {
|
|
1298
|
+
return createToolErrorResponse('validation_error', 'Missing required argument: project (or use list=true / clear=true)');
|
|
1299
|
+
}
|
|
1300
|
+
try {
|
|
1301
|
+
const result = switchProject(projectRef, { cwd, sessionOnly: true });
|
|
1302
|
+
const text = `✔ Switched to ${result.name ? `"${result.name}"` : result.path} (${result.scope}-scoped)`;
|
|
1303
|
+
return {
|
|
1304
|
+
content: [{ type: 'text', text }],
|
|
1305
|
+
structuredContent: { ...result, schema_version: SCHEMA_VERSION },
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
1308
|
+
catch (err) {
|
|
1309
|
+
return createToolErrorResponse('switch_error', err instanceof Error ? err.message : String(err));
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
if (name === 'bclaw_who') {
|
|
1313
|
+
// loadAllSessions and gcStaleSessions imported at top of file
|
|
1314
|
+
const doGc = args.gc === true;
|
|
1315
|
+
const showAll = args.all === true;
|
|
1316
|
+
if (doGc) {
|
|
1317
|
+
const removed = gcStaleSessions(cwd);
|
|
1318
|
+
return {
|
|
1319
|
+
content: [{ type: 'text', text: `✔ Removed ${removed} stale session(s).` }],
|
|
1320
|
+
structuredContent: { gc: true, removed, schema_version: SCHEMA_VERSION },
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
const allSessions = loadAllSessions(cwd);
|
|
1324
|
+
const ttlMs = 4 * 60 * 60 * 1000;
|
|
1325
|
+
const now = Date.now();
|
|
1326
|
+
const sessions = showAll
|
|
1327
|
+
? allSessions
|
|
1328
|
+
: allSessions.filter((s) => (now - Date.parse(s.last_seen_at)) <= ttlMs);
|
|
1329
|
+
const activeClaims = listClaims(cwd).filter((c) => c.status === 'active');
|
|
1330
|
+
const output = sessions.map((s) => ({
|
|
1331
|
+
session_id: s.session_id,
|
|
1332
|
+
user: s.user ?? 'unknown',
|
|
1333
|
+
agent: s.agent,
|
|
1334
|
+
agent_id: s.agent_id,
|
|
1335
|
+
project: s.active_project?.name ?? s.active_project?.path ?? null,
|
|
1336
|
+
claims: activeClaims.filter((c) => c.agent_id === s.agent_id).length,
|
|
1337
|
+
last_seen_at: s.last_seen_at,
|
|
1338
|
+
stale: (now - Date.parse(s.last_seen_at)) > ttlMs,
|
|
1339
|
+
}));
|
|
1340
|
+
const lines = sessions.length === 0
|
|
1341
|
+
? 'No active sessions.'
|
|
1342
|
+
: output.map((s) => `${s.user} | ${s.agent} | ${s.project ?? '(root)'} | ${s.claims} claims | ${s.stale ? 'stale' : 'active'}`).join('\n');
|
|
1343
|
+
return {
|
|
1344
|
+
content: [{ type: 'text', text: lines }],
|
|
1345
|
+
structuredContent: { sessions: output, total: output.length, schema_version: SCHEMA_VERSION },
|
|
1346
|
+
};
|
|
1347
|
+
}
|
|
1348
|
+
if (name === 'bclaw_check_policy') {
|
|
1349
|
+
const scope = String(args.scope ?? '').trim();
|
|
1350
|
+
if (!scope) {
|
|
1351
|
+
return { content: [{ type: 'text', text: 'Error: missing required argument: scope' }] };
|
|
1352
|
+
}
|
|
1353
|
+
const result = checkPolicy({
|
|
1354
|
+
scope,
|
|
1355
|
+
agent: args.agent ?? resolveCurrentAgentName(cwd),
|
|
1356
|
+
agentId: args.agentId,
|
|
1357
|
+
action: args.action,
|
|
1358
|
+
cwd,
|
|
1359
|
+
});
|
|
1360
|
+
const parts = [];
|
|
1361
|
+
const status = result.allowed ? '✔ ALLOWED' : '✘ BLOCKED';
|
|
1362
|
+
parts.push(`Policy check for "${scope}": ${status}`);
|
|
1363
|
+
if (result.blocks.length > 0) {
|
|
1364
|
+
parts.push('');
|
|
1365
|
+
parts.push('Blocks:');
|
|
1366
|
+
for (const b of result.blocks) {
|
|
1367
|
+
parts.push(` ✘ [${b.kind}] ${b.message}`);
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
if (result.warnings.length > 0) {
|
|
1371
|
+
parts.push('');
|
|
1372
|
+
parts.push('Warnings:');
|
|
1373
|
+
for (const w of result.warnings) {
|
|
1374
|
+
const idLabel = w.id ? ` (${w.id})` : '';
|
|
1375
|
+
parts.push(` ⚠ [${w.kind}]${idLabel} ${w.message}`);
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
if (result.governance_context.active_instructions.length > 0) {
|
|
1379
|
+
parts.push('');
|
|
1380
|
+
parts.push(`Governance: ${result.governance_context.active_instructions.length} active instruction(s)`);
|
|
1381
|
+
for (const ins of result.governance_context.active_instructions) {
|
|
1382
|
+
const layerLabel = ins.layer === 'global' ? '[global]' : `[${ins.layer}:${ins.scope ?? '*'}]`;
|
|
1383
|
+
parts.push(` ${layerLabel} ${ins.text.slice(0, 150)}${ins.text.length > 150 ? '…' : ''}`);
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
return {
|
|
1387
|
+
content: [{ type: 'text', text: parts.join('\n') }],
|
|
1388
|
+
structuredContent: {
|
|
1389
|
+
allowed: result.allowed,
|
|
1390
|
+
blocks: result.blocks,
|
|
1391
|
+
warnings: result.warnings,
|
|
1392
|
+
governance_context: {
|
|
1393
|
+
active_instructions_count: result.governance_context.active_instructions.length,
|
|
1394
|
+
matching_constraints_count: result.governance_context.matching_constraints.length,
|
|
1395
|
+
matching_traps_count: result.governance_context.matching_traps.length,
|
|
1396
|
+
active_claims_on_scope: result.governance_context.active_claims_on_scope.map(c => ({
|
|
1397
|
+
id: c.id, agent: c.agent, scope: c.scope, description: c.description,
|
|
1398
|
+
})),
|
|
1399
|
+
},
|
|
1400
|
+
schema_version: SCHEMA_VERSION,
|
|
1401
|
+
},
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
if (name === 'bclaw_doctor') {
|
|
1405
|
+
// Capture doctor JSON output by redirecting console.log
|
|
1406
|
+
const captured = [];
|
|
1407
|
+
const origLog = console.log;
|
|
1408
|
+
const origWarn = console.warn;
|
|
1409
|
+
const origError = console.error;
|
|
1410
|
+
console.log = (...a) => captured.push(a.join(' '));
|
|
1411
|
+
console.warn = (...a) => captured.push(a.join(' '));
|
|
1412
|
+
console.error = (...a) => captured.push(a.join(' '));
|
|
1413
|
+
try {
|
|
1414
|
+
runDoctor({ json: true, cwd, migrationCheck: args.migrationCheck });
|
|
1415
|
+
}
|
|
1416
|
+
finally {
|
|
1417
|
+
console.log = origLog;
|
|
1418
|
+
console.warn = origWarn;
|
|
1419
|
+
console.error = origError;
|
|
1420
|
+
}
|
|
1421
|
+
const jsonStr = captured.join('\n');
|
|
1422
|
+
let structured = {};
|
|
1423
|
+
try {
|
|
1424
|
+
structured = JSON.parse(jsonStr);
|
|
1425
|
+
}
|
|
1426
|
+
catch { /* non-JSON fallback */ }
|
|
1427
|
+
const ok = structured.ok;
|
|
1428
|
+
const checks = structured.checks ?? [];
|
|
1429
|
+
const errors = checks.filter(c => c.status === 'error');
|
|
1430
|
+
const warns = checks.filter(c => c.status === 'warn');
|
|
1431
|
+
const summary = ok
|
|
1432
|
+
? `✔ All ${checks.length} checks passed.`
|
|
1433
|
+
: `${errors.length} error(s), ${warns.length} warning(s) out of ${checks.length} checks.`;
|
|
1434
|
+
return {
|
|
1435
|
+
content: [{ type: 'text', text: summary }],
|
|
1436
|
+
structuredContent: { ...structured, schema_version: SCHEMA_VERSION },
|
|
1437
|
+
};
|
|
1438
|
+
}
|
|
1439
|
+
if (name === 'bclaw_history') {
|
|
1440
|
+
const id = String(args.id ?? '').trim();
|
|
1441
|
+
if (!id)
|
|
1442
|
+
throw new Error('Missing required argument: id');
|
|
1443
|
+
const entries = readAuditLog({ itemId: id }, cwd);
|
|
1444
|
+
const lines = [`History for ${id} — ${entries.length} event(s):`];
|
|
1445
|
+
for (const e of entries) {
|
|
1446
|
+
const reason = e.reason ? ` | ${e.reason}` : '';
|
|
1447
|
+
lines.push(` ${e.timestamp} [${e.actor}] ${e.action}${reason}`);
|
|
1448
|
+
}
|
|
1449
|
+
return {
|
|
1450
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
1451
|
+
structuredContent: { id, total: entries.length, entries, schema_version: SCHEMA_VERSION },
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1454
|
+
if (name === 'bclaw_audit') {
|
|
1455
|
+
// Governance mode
|
|
1456
|
+
if (args.governance === true) {
|
|
1457
|
+
const report = buildGovernanceReport({
|
|
1458
|
+
scope: args.scope,
|
|
1459
|
+
agent: args.actor,
|
|
1460
|
+
since: args.since,
|
|
1461
|
+
cwd,
|
|
1462
|
+
});
|
|
1463
|
+
const markdown = renderGovernanceMarkdown(report);
|
|
1464
|
+
return {
|
|
1465
|
+
content: [{ type: 'text', text: markdown }],
|
|
1466
|
+
structuredContent: { ...report, schema_version: SCHEMA_VERSION },
|
|
1467
|
+
};
|
|
1468
|
+
}
|
|
1469
|
+
// Chronological mode (default)
|
|
1470
|
+
const limit = args.limit ?? 20;
|
|
1471
|
+
const entries = readAuditLog({
|
|
1472
|
+
since: args.since,
|
|
1473
|
+
actor: args.actor,
|
|
1474
|
+
action: args.action,
|
|
1475
|
+
}, cwd);
|
|
1476
|
+
const sliced = entries.slice(-limit);
|
|
1477
|
+
const lines = [`Audit log — showing ${sliced.length} of ${entries.length} entries:`];
|
|
1478
|
+
for (const e of sliced) {
|
|
1479
|
+
const itemInfo = e.item_id ? ` → ${e.item_id}` : '';
|
|
1480
|
+
const typeInfo = e.item_type ? ` (${e.item_type})` : '';
|
|
1481
|
+
const scopeInfo = e.scope ? ` scope:${e.scope}` : '';
|
|
1482
|
+
const reason = e.reason ? ` | ${e.reason}` : '';
|
|
1483
|
+
lines.push(` ${e.timestamp} [${e.actor}] ${e.action}${itemInfo}${typeInfo}${scopeInfo}${reason}`);
|
|
1484
|
+
}
|
|
1485
|
+
return {
|
|
1486
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
1487
|
+
structuredContent: { total: entries.length, returned: sliced.length, entries: sliced, schema_version: SCHEMA_VERSION },
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1490
|
+
if (name === 'bclaw_dispatch_analysis') {
|
|
1491
|
+
const analysis = analyzeSequence(cwd);
|
|
1492
|
+
if (!analysis) {
|
|
1493
|
+
return {
|
|
1494
|
+
content: [{ type: 'text', text: 'No active sequence found.' }],
|
|
1495
|
+
structuredContent: { active_sequence: false, schema_version: SCHEMA_VERSION },
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
const lanesFilter = args.lanes;
|
|
1499
|
+
const lines = [`Dispatch analysis — Sequence: ${analysis.sequence.name}`];
|
|
1500
|
+
lines.push('');
|
|
1501
|
+
// Ready lanes
|
|
1502
|
+
let ready = analysis.ready;
|
|
1503
|
+
if (lanesFilter?.length)
|
|
1504
|
+
ready = ready.filter(r => r.lane && lanesFilter.includes(r.lane));
|
|
1505
|
+
lines.push(`🟢 Ready (${ready.length}):`);
|
|
1506
|
+
for (const r of ready) {
|
|
1507
|
+
const lane = r.lane ? ` [${r.lane}]` : '';
|
|
1508
|
+
const assignee = r.plan.assignee ? ` → ${r.plan.assignee}` : '';
|
|
1509
|
+
lines.push(` ${r.plan.short_label ?? r.plan.id}${lane}${assignee} — ${r.plan.text.slice(0, 80)}`);
|
|
1510
|
+
lines.push(` ${r.reason}`);
|
|
1511
|
+
}
|
|
1512
|
+
// Active lanes
|
|
1513
|
+
let active = analysis.active;
|
|
1514
|
+
if (lanesFilter?.length)
|
|
1515
|
+
active = active.filter(a => a.lane && lanesFilter.includes(a.lane));
|
|
1516
|
+
if (active.length > 0) {
|
|
1517
|
+
lines.push('');
|
|
1518
|
+
lines.push(`🔵 Active (${active.length}):`);
|
|
1519
|
+
for (const a of active) {
|
|
1520
|
+
const lane = a.lane ? ` [${a.lane}]` : '';
|
|
1521
|
+
// Mirror dispatch.ts: pre-adoption lanes render as "pending adoption",
|
|
1522
|
+
// degraded liveness states get a [TAG] suffix.
|
|
1523
|
+
let status = 'working';
|
|
1524
|
+
let livenessTag = '';
|
|
1525
|
+
if (a.liveness === 'young' && !a.claim.session_id) {
|
|
1526
|
+
status = 'pending adoption';
|
|
1527
|
+
}
|
|
1528
|
+
else if (a.liveness && a.liveness !== 'live' && a.liveness !== 'young') {
|
|
1529
|
+
livenessTag = ` [${a.liveness.toUpperCase()}]`;
|
|
1530
|
+
}
|
|
1531
|
+
lines.push(` ${a.plan.short_label ?? a.plan.id}${lane} — ${a.agent} ${status}${livenessTag}`);
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
// Blocked lanes
|
|
1535
|
+
let blocked = analysis.blocked;
|
|
1536
|
+
if (lanesFilter?.length)
|
|
1537
|
+
blocked = blocked.filter(b => b.lane && lanesFilter.includes(b.lane));
|
|
1538
|
+
if (blocked.length > 0) {
|
|
1539
|
+
lines.push('');
|
|
1540
|
+
lines.push(`🔴 Blocked (${blocked.length}):`);
|
|
1541
|
+
for (const b of blocked) {
|
|
1542
|
+
const lane = b.lane ? ` [${b.lane}]` : '';
|
|
1543
|
+
lines.push(` ${b.item.planId}${lane} — ${b.reason}`);
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
// Done
|
|
1547
|
+
if (analysis.done.length > 0) {
|
|
1548
|
+
lines.push('');
|
|
1549
|
+
lines.push(`✅ Done (${analysis.done.length})`);
|
|
1550
|
+
}
|
|
1551
|
+
// Available agents
|
|
1552
|
+
lines.push('');
|
|
1553
|
+
lines.push(`Available agents: ${analysis.available_agents.length > 0 ? analysis.available_agents.join(', ') : '(none)'}`);
|
|
1554
|
+
return {
|
|
1555
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
1556
|
+
structuredContent: { ...analysis, schema_version: SCHEMA_VERSION },
|
|
1557
|
+
};
|
|
1558
|
+
}
|
|
1559
|
+
if (name === 'bclaw_read_inbox') {
|
|
1560
|
+
const agentName = args.agent ?? resolveCurrentAgentName(cwd);
|
|
1561
|
+
const markAsRead = args.markAsRead === true; // default: false — reading doesn't imply processing
|
|
1562
|
+
const result = readInbox({
|
|
1563
|
+
agent: agentName,
|
|
1564
|
+
status: args.status,
|
|
1565
|
+
type: args.type,
|
|
1566
|
+
thread_id: args.thread_id,
|
|
1567
|
+
limit: args.limit,
|
|
1568
|
+
offset: args.offset,
|
|
1569
|
+
markAsRead,
|
|
1570
|
+
}, cwd);
|
|
1571
|
+
const lines = [`Inbox for ${agentName} — ${result.total} message(s):`];
|
|
1572
|
+
for (const msg of result.messages) {
|
|
1573
|
+
const ack = msg.requires_ack ? ' [ACK required]' : '';
|
|
1574
|
+
const thread = msg.thread_id ? ` thread:${msg.thread_id}` : '';
|
|
1575
|
+
lines.push(` [${msg.short_label ?? msg.id}] ${msg.type} from ${msg.from} (${msg.status})${ack}${thread}`);
|
|
1576
|
+
lines.push(` ${msg.text.slice(0, 200)}${msg.text.length > 200 ? '...' : ''}`);
|
|
1577
|
+
}
|
|
1578
|
+
if (result.messages.length === 0) {
|
|
1579
|
+
lines.push(' (no messages)');
|
|
1580
|
+
}
|
|
1581
|
+
return {
|
|
1582
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
1583
|
+
structuredContent: { ...result, schema_version: SCHEMA_VERSION },
|
|
1584
|
+
};
|
|
1585
|
+
}
|
|
1586
|
+
if (name === 'bclaw_context') {
|
|
1587
|
+
// Phase 3 slice 3c — unified dispatcher. See docs/concepts/mcp-governance.md
|
|
1588
|
+
// for the stability contract of the advanced tier.
|
|
1589
|
+
const kind = String(args.kind ?? '');
|
|
1590
|
+
switch (kind) {
|
|
1591
|
+
case 'memory':
|
|
1592
|
+
return handleMcpReadToolCall('bclaw_get_context', args, context);
|
|
1593
|
+
case 'execution':
|
|
1594
|
+
return handleMcpReadToolCall('bclaw_get_execution_context', args, context);
|
|
1595
|
+
case 'board':
|
|
1596
|
+
return handleMcpReadToolCall('bclaw_get_agent_board', args, context);
|
|
1597
|
+
case 'board_summary':
|
|
1598
|
+
return handleMcpReadToolCall('bclaw_get_agent_board_summary', args, context);
|
|
1599
|
+
case 'delta': {
|
|
1600
|
+
const since = args.since;
|
|
1601
|
+
if (typeof since !== 'string' || !since) {
|
|
1602
|
+
throw new Error('bclaw_context(kind="delta") requires `since` (session_id).');
|
|
1603
|
+
}
|
|
1604
|
+
return handleMcpReadToolCall('bclaw_get_context', { ...args, since_session: since }, context);
|
|
1605
|
+
}
|
|
1606
|
+
default:
|
|
1607
|
+
throw new Error(`bclaw_context: unknown kind '${kind}'. Expected memory | execution | board | board_summary | delta.`);
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
if (name === 'bclaw_get_thread') {
|
|
1611
|
+
const threadId = String(args.thread_id ?? '');
|
|
1612
|
+
if (!threadId) {
|
|
1613
|
+
throw new Error('Missing required argument: thread_id');
|
|
1614
|
+
}
|
|
1615
|
+
const messages = getThread(threadId, cwd);
|
|
1616
|
+
const lines = [`Thread ${threadId} — ${messages.length} message(s):`];
|
|
1617
|
+
for (const msg of messages) {
|
|
1618
|
+
lines.push(` [${msg.short_label ?? msg.id}] ${msg.from} → ${msg.to} (${msg.type}, ${msg.status})`);
|
|
1619
|
+
lines.push(` ${msg.text.slice(0, 200)}${msg.text.length > 200 ? '...' : ''}`);
|
|
1620
|
+
}
|
|
1621
|
+
return {
|
|
1622
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
1623
|
+
structuredContent: { thread_id: threadId, total: messages.length, messages, schema_version: SCHEMA_VERSION },
|
|
1624
|
+
};
|
|
1625
|
+
}
|
|
1626
|
+
throw new Error(`Unknown read tool: ${name}`);
|
|
1627
|
+
}
|
|
1628
|
+
//# sourceMappingURL=mcp-read-handlers.js.map
|