brainclaw 0.29.2 → 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 +673 -24
- 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 +4221 -1501
- 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 +100 -2
- 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 +33 -5
- 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/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 +381 -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/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 +110 -25
- 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/reputation.js +9 -3
- package/dist/core/schema.js +491 -6
- 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 +43 -11
- 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
|
@@ -2,6 +2,9 @@ import { runPlan } from './plan.js';
|
|
|
2
2
|
import { runListPlans } from './list-plans.js';
|
|
3
3
|
import { runUpdatePlan } from './update-plan.js';
|
|
4
4
|
import { runDeletePlan } from './delete-plan.js';
|
|
5
|
+
import { loadState } from '../core/state.js';
|
|
6
|
+
import { memoryExists } from '../core/io.js';
|
|
7
|
+
const KNOWN_SUBCOMMANDS = new Set(['create', 'list', 'ls', 'update', 'delete', 'show', 'get']);
|
|
5
8
|
export function runPlanResource(subcommand, args, options = {}) {
|
|
6
9
|
const normalized = subcommand.trim().toLowerCase();
|
|
7
10
|
if (normalized === 'create') {
|
|
@@ -21,9 +24,21 @@ export function runPlanResource(subcommand, args, options = {}) {
|
|
|
21
24
|
assignee: options.assignee,
|
|
22
25
|
project: options.project,
|
|
23
26
|
all: options.all,
|
|
27
|
+
recursive: options.recursive,
|
|
28
|
+
localOnly: options.localOnly,
|
|
24
29
|
});
|
|
25
30
|
return;
|
|
26
31
|
}
|
|
32
|
+
if (normalized === 'show' || normalized === 'get') {
|
|
33
|
+
const id = args[0];
|
|
34
|
+
if (!id) {
|
|
35
|
+
console.error(`Error: plan ${normalized} requires <id>.`);
|
|
36
|
+
console.error(` Usage: brainclaw plan ${normalized} <id>`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
runShowPlan(id, options);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
27
42
|
if (normalized === 'update') {
|
|
28
43
|
const id = args[0];
|
|
29
44
|
if (!id) {
|
|
@@ -51,6 +66,12 @@ export function runPlanResource(subcommand, args, options = {}) {
|
|
|
51
66
|
runDeletePlan(id, { cwd: options.cwd });
|
|
52
67
|
return;
|
|
53
68
|
}
|
|
69
|
+
// Reject known-looking subcommands to prevent accidental plan creation
|
|
70
|
+
if (normalized.startsWith('pln_') || KNOWN_SUBCOMMANDS.has(normalized)) {
|
|
71
|
+
console.error(`Error: unknown plan subcommand "${subcommand}".`);
|
|
72
|
+
console.error(' Available: create, list, show, get, update, delete');
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
54
75
|
// Compatibility path: `brainclaw plan "text"` still creates a plan.
|
|
55
76
|
const legacyText = [subcommand, ...args].join(' ').trim();
|
|
56
77
|
if (!legacyText) {
|
|
@@ -59,4 +80,47 @@ export function runPlanResource(subcommand, args, options = {}) {
|
|
|
59
80
|
}
|
|
60
81
|
runPlan(legacyText, options);
|
|
61
82
|
}
|
|
83
|
+
function runShowPlan(id, options) {
|
|
84
|
+
if (!memoryExists(options.cwd)) {
|
|
85
|
+
console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
const state = loadState(options.cwd);
|
|
89
|
+
const plan = state.plan_items.find(p => p.id === id);
|
|
90
|
+
if (!plan) {
|
|
91
|
+
console.error(`Error: plan not found: ${id}`);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
if (options.json) {
|
|
95
|
+
console.log(JSON.stringify(plan, null, 2));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
console.log(`Plan: ${plan.id}`);
|
|
99
|
+
console.log(` Text: ${plan.text}`);
|
|
100
|
+
console.log(` Status: ${plan.status}`);
|
|
101
|
+
if (plan.type)
|
|
102
|
+
console.log(` Type: ${plan.type}`);
|
|
103
|
+
if (plan.priority)
|
|
104
|
+
console.log(` Priority: ${plan.priority}`);
|
|
105
|
+
if (plan.assignee)
|
|
106
|
+
console.log(` Assignee: ${plan.assignee}`);
|
|
107
|
+
if (plan.project)
|
|
108
|
+
console.log(` Project: ${plan.project}`);
|
|
109
|
+
if (plan.estimated_effort)
|
|
110
|
+
console.log(` Estimate: ${plan.estimated_effort}min`);
|
|
111
|
+
if (plan.actual_effort)
|
|
112
|
+
console.log(` Actual: ${plan.actual_effort}`);
|
|
113
|
+
if (plan.tags && plan.tags.length > 0)
|
|
114
|
+
console.log(` Tags: ${plan.tags.join(', ')}`);
|
|
115
|
+
console.log(` Created: ${plan.created_at}`);
|
|
116
|
+
if (plan.updated_at)
|
|
117
|
+
console.log(` Updated: ${plan.updated_at}`);
|
|
118
|
+
if (plan.steps && plan.steps.length > 0) {
|
|
119
|
+
console.log(' Steps:');
|
|
120
|
+
for (const step of plan.steps) {
|
|
121
|
+
const check = step.status === 'done' ? '✔' : '○';
|
|
122
|
+
console.log(` ${check} ${step.text}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
62
126
|
//# sourceMappingURL=plan-resource.js.map
|
package/dist/commands/plan.js
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
|
-
import { loadState, persistState } from '../core/state.js';
|
|
2
1
|
import { resolveCurrentAgentName } from '../core/agent-registry.js';
|
|
3
2
|
import { loadConfig } from '../core/config.js';
|
|
4
|
-
import { generateIdWithLabel, nowISO } from '../core/ids.js';
|
|
5
3
|
import { scanText } from '../core/security.js';
|
|
6
|
-
import {
|
|
4
|
+
import { requireInitialized } from '../core/guards.js';
|
|
7
5
|
import { validateCliInput } from '../core/input-validation.js';
|
|
8
6
|
import { runListPlans } from './list-plans.js';
|
|
9
7
|
import { resolveTargetStore } from '../core/store-resolution.js';
|
|
8
|
+
import { createPlan } from '../core/operations/plan.js';
|
|
10
9
|
// Known plan subcommands that should not be accepted as plan text
|
|
11
10
|
const PLAN_SUBCOMMAND_ALIASES = new Set(['list', 'ls']);
|
|
12
11
|
const PLAN_SUBCOMMAND_ERRORS = new Set(['update']);
|
|
13
12
|
export function runPlan(text, options = {}) {
|
|
14
13
|
const cwd = resolveTargetStore(options.cwd ?? process.cwd(), options.store ?? 'local');
|
|
15
|
-
|
|
16
|
-
console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
|
|
17
|
-
process.exit(1);
|
|
18
|
-
}
|
|
14
|
+
requireInitialized(cwd);
|
|
19
15
|
const normalized = text.trim().toLowerCase();
|
|
20
16
|
if (PLAN_SUBCOMMAND_ALIASES.has(normalized)) {
|
|
21
17
|
runListPlans({});
|
|
@@ -46,29 +42,19 @@ export function runPlan(text, options = {}) {
|
|
|
46
42
|
}
|
|
47
43
|
estimatedEffort = n;
|
|
48
44
|
}
|
|
49
|
-
const
|
|
50
|
-
const { id, short_label } = generateIdWithLabel('plan_items');
|
|
51
|
-
const timestamp = nowISO();
|
|
52
|
-
const entry = {
|
|
53
|
-
id,
|
|
54
|
-
short_label,
|
|
45
|
+
const result = createPlan({
|
|
55
46
|
text,
|
|
56
|
-
type: options.type,
|
|
57
|
-
created_at: timestamp,
|
|
58
|
-
updated_at: timestamp,
|
|
59
47
|
author: options.author ?? resolveCurrentAgentName(cwd),
|
|
60
|
-
|
|
61
|
-
priority: options.priority
|
|
48
|
+
type: options.type,
|
|
49
|
+
priority: options.priority,
|
|
62
50
|
assignee: options.assignee,
|
|
63
51
|
project: options.project,
|
|
64
|
-
tags: options.tag
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
};
|
|
69
|
-
state.plan_items.push(entry);
|
|
70
|
-
persistState(state, cwd);
|
|
52
|
+
tags: options.tag,
|
|
53
|
+
relatedPaths: options.path,
|
|
54
|
+
dependsOn: options.dependsOn,
|
|
55
|
+
estimatedEffort,
|
|
56
|
+
}, cwd);
|
|
71
57
|
const storeLabel = options.store && options.store !== 'local' ? ` [store:${options.store}]` : '';
|
|
72
|
-
console.log(`✔ Plan item added: [${id}] ${text}${storeLabel}`);
|
|
58
|
+
console.log(`✔ Plan item added: [${result.id}] ${text}${storeLabel}`);
|
|
73
59
|
}
|
|
74
60
|
//# sourceMappingURL=plan.js.map
|
package/dist/commands/prune.js
CHANGED
|
@@ -4,12 +4,36 @@ import { mutate } from '../core/mutation-pipeline.js';
|
|
|
4
4
|
import { rebuildProjectMd } from '../core/markdown.js';
|
|
5
5
|
import { deleteRuntimeNote, listRuntimeNotes } from '../core/runtime.js';
|
|
6
6
|
import { expireStaleActiveClaims } from '../core/claims.js';
|
|
7
|
+
import { archiveStalePlansAndHandoffs } from '../core/archival.js';
|
|
8
|
+
import { rotateAuditLogIfNeeded } from '../core/audit.js';
|
|
9
|
+
import { analyzeMemory, analyzeAndApply, formatReport } from '../core/memory-compactor.js';
|
|
7
10
|
export function runPrune(options = {}) {
|
|
8
11
|
const cwd = process.cwd();
|
|
9
12
|
if (!memoryExists(cwd)) {
|
|
10
13
|
console.error('Project memory not initialized. Run `brainclaw init` first.');
|
|
11
14
|
process.exit(1);
|
|
12
15
|
}
|
|
16
|
+
// Semantic compaction mode
|
|
17
|
+
if (options.semantic) {
|
|
18
|
+
if (options.dryRun) {
|
|
19
|
+
// Dry-run: read-only analysis, no lock needed
|
|
20
|
+
const state = loadState(cwd);
|
|
21
|
+
const report = analyzeMemory(state, { cwd });
|
|
22
|
+
console.log(formatReport(report));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
// Apply: analyze + apply atomically under a single mutation lock
|
|
26
|
+
const { report, result } = analyzeAndApply({ cwd });
|
|
27
|
+
if (report.archivableCount === 0) {
|
|
28
|
+
console.log('No compaction opportunities found.');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
console.log(formatReport(report));
|
|
32
|
+
console.log('');
|
|
33
|
+
console.log(`✔ Compaction applied: ${result.archivedCount} items archived (${result.mergedClusters} clusters merged, ${result.staleArchived} stale items).`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
// Original prune logic
|
|
13
37
|
const now = new Date().toISOString();
|
|
14
38
|
let prunedCount = 0;
|
|
15
39
|
let expiredClaimsCount = 0;
|
|
@@ -41,11 +65,22 @@ export function runPrune(options = {}) {
|
|
|
41
65
|
}
|
|
42
66
|
rebuildProjectMd(loadState(cwd), cwd);
|
|
43
67
|
});
|
|
68
|
+
// Archive and rotate outside the mutation lock (they manage their own IO)
|
|
69
|
+
let archiveMsg = '';
|
|
70
|
+
if (options.archive) {
|
|
71
|
+
const archiveResults = archiveStalePlansAndHandoffs(cwd);
|
|
72
|
+
if (archiveResults.length > 0) {
|
|
73
|
+
const parts = archiveResults.map(r => `${r.archived} ${r.entity}`);
|
|
74
|
+
archiveMsg = `, archived ${parts.join(' + ')} to cold storage`;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const rotated = rotateAuditLogIfNeeded(cwd);
|
|
78
|
+
const rotateMsg = rotated ? ', rotated audit.log' : '';
|
|
44
79
|
if (options.expired) {
|
|
45
|
-
console.log(`✔ Pruned ${prunedCount} expired constraints, ${expiredNotesCount} expired runtime notes, ${expiredClaimsCount} expired claims.`);
|
|
80
|
+
console.log(`✔ Pruned ${prunedCount} expired constraints, ${expiredNotesCount} expired runtime notes, ${expiredClaimsCount} expired claims${archiveMsg}${rotateMsg}.`);
|
|
46
81
|
}
|
|
47
82
|
else {
|
|
48
|
-
console.log(`✔ Pruned ${prunedCount} expired constraints, ${expiredClaimsCount} expired claims.`);
|
|
83
|
+
console.log(`✔ Pruned ${prunedCount} expired constraints, ${expiredClaimsCount} expired claims${archiveMsg}${rotateMsg}.`);
|
|
49
84
|
}
|
|
50
85
|
}
|
|
51
86
|
//# sourceMappingURL=prune.js.map
|
package/dist/commands/reflect.js
CHANGED
|
@@ -8,8 +8,8 @@ import { scanText } from '../core/security.js';
|
|
|
8
8
|
import { nowISO, generateIdWithLabel } from '../core/ids.js';
|
|
9
9
|
import { saveCandidate, generateCandidateIdWithLabel, listCandidates } from '../core/candidates.js';
|
|
10
10
|
import { detectDuplicates } from '../core/duplicates.js';
|
|
11
|
-
import { RuntimeEventSchema } from '../core/schema.js';
|
|
12
|
-
import { listRuntimeEventsBySession } from '../core/events.js';
|
|
11
|
+
import { CandidateSourceSchema, RuntimeEventSchema } from '../core/schema.js';
|
|
12
|
+
import { isReflectableRuntimeEvent, listRuntimeEventsBySession } from '../core/events.js';
|
|
13
13
|
import { agentCanWriteDirect, requireMinimumTrustLevel, requireRegisteredAgentIdentity } from '../core/agent-registry.js';
|
|
14
14
|
import { appendAuditEntry } from '../core/audit.js';
|
|
15
15
|
import { generateTrapIdWithLabel } from '../core/traps.js';
|
|
@@ -56,6 +56,9 @@ function runReflectBatchFromFile(filepath, baseOptions) {
|
|
|
56
56
|
for (const rawEvent of rawEvents) {
|
|
57
57
|
try {
|
|
58
58
|
const event = RuntimeEventSchema.parse(rawEvent);
|
|
59
|
+
if (!isReflectableRuntimeEvent(event)) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
59
62
|
const candidateType = event.candidate_type ?? mapEventTypeToCandidateType(event.event_type);
|
|
60
63
|
createCandidateFromInput(event.text, candidateType, {
|
|
61
64
|
...baseOptions,
|
|
@@ -87,6 +90,9 @@ function runReflectBatchFromSession(session, baseOptions) {
|
|
|
87
90
|
}
|
|
88
91
|
let created = 0;
|
|
89
92
|
for (const event of events) {
|
|
93
|
+
if (!isReflectableRuntimeEvent(event)) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
90
96
|
const candidateType = event.candidate_type ?? mapEventTypeToCandidateType(event.event_type);
|
|
91
97
|
createCandidateFromInput(event.text, candidateType, {
|
|
92
98
|
...baseOptions,
|
|
@@ -158,7 +164,14 @@ export function createCandidateFromInput(text, type, options, printSuccess = tru
|
|
|
158
164
|
project_id: options.projectId ?? actorIdentity.project_id,
|
|
159
165
|
host_id: options.hostId ?? actorIdentity.host_id,
|
|
160
166
|
session_id: options.sessionId ?? actorIdentity.session_id,
|
|
161
|
-
source
|
|
167
|
+
// Normalize source to the enum when possible; otherwise infer from the
|
|
168
|
+
// free-text origin value below. Preserves legacy provenance patterns
|
|
169
|
+
// (runtime-note:*, mcp:*, session-end:*, cross-project:*) in `origin`
|
|
170
|
+
// while keeping `source` strictly enum-constrained.
|
|
171
|
+
source: CandidateSourceSchema.safeParse(options.source).data,
|
|
172
|
+
origin: typeof options.source === 'string' && !CandidateSourceSchema.safeParse(options.source).success
|
|
173
|
+
? options.source
|
|
174
|
+
: undefined,
|
|
162
175
|
tags: options.tag ?? [],
|
|
163
176
|
status: 'pending',
|
|
164
177
|
severity: type === 'trap' ? options.severity ?? 'medium' : undefined,
|
|
@@ -238,28 +251,28 @@ function promoteCandidateToState(candidate, cwd) {
|
|
|
238
251
|
switch (candidate.type) {
|
|
239
252
|
case 'constraint': {
|
|
240
253
|
const { id: cId, short_label } = generateIdWithLabel('active_constraints', cwd);
|
|
241
|
-
const entry = { id: cId, short_label, text: candidate.text, created_at: candidate.created_at, author: candidate.author, author_id: candidate.author_id, project_id: candidate.project_id, host_id: candidate.host_id, session_id: candidate.session_id, status: 'active', tags: candidate.tags };
|
|
254
|
+
const entry = { id: cId, short_label, text: candidate.text, created_at: candidate.created_at, author: candidate.author, author_id: candidate.author_id, project_id: candidate.project_id, host_id: candidate.host_id, session_id: candidate.session_id, status: 'active', tags: candidate.tags, plan_id: candidate.plan_id };
|
|
242
255
|
state.active_constraints.push(entry);
|
|
243
256
|
promotedItemId = entry.id;
|
|
244
257
|
break;
|
|
245
258
|
}
|
|
246
259
|
case 'decision': {
|
|
247
260
|
const { id: dId, short_label } = generateIdWithLabel('recent_decisions', cwd);
|
|
248
|
-
const entry = { id: dId, short_label, text: candidate.text, created_at: candidate.created_at, author: candidate.author, author_id: candidate.author_id, project_id: candidate.project_id, host_id: candidate.host_id, session_id: candidate.session_id, related_paths: candidate.related_paths, tags: candidate.tags };
|
|
261
|
+
const entry = { id: dId, short_label, text: candidate.text, created_at: candidate.created_at, author: candidate.author, author_id: candidate.author_id, project_id: candidate.project_id, host_id: candidate.host_id, session_id: candidate.session_id, related_paths: candidate.related_paths, plan_id: candidate.plan_id, tags: candidate.tags };
|
|
249
262
|
state.recent_decisions.push(entry);
|
|
250
263
|
promotedItemId = entry.id;
|
|
251
264
|
break;
|
|
252
265
|
}
|
|
253
266
|
case 'trap': {
|
|
254
267
|
const { id: tId, short_label } = generateTrapIdWithLabel(cwd);
|
|
255
|
-
const entry = { id: tId, short_label, text: candidate.text, created_at: candidate.created_at, author: candidate.author, author_id: candidate.author_id, project_id: candidate.project_id, host_id: candidate.host_id, session_id: candidate.session_id, status: 'active', severity: candidate.severity ?? 'medium', tags: candidate.tags, visibility: 'shared' };
|
|
268
|
+
const entry = { id: tId, short_label, text: candidate.text, created_at: candidate.created_at, author: candidate.author, author_id: candidate.author_id, project_id: candidate.project_id, host_id: candidate.host_id, session_id: candidate.session_id, status: 'active', severity: candidate.severity ?? 'medium', tags: candidate.tags, plan_id: candidate.plan_id, visibility: 'shared' };
|
|
256
269
|
state.known_traps.push(entry);
|
|
257
270
|
promotedItemId = entry.id;
|
|
258
271
|
break;
|
|
259
272
|
}
|
|
260
273
|
case 'handoff': {
|
|
261
274
|
const { id: hId, short_label } = generateIdWithLabel('open_handoffs', cwd);
|
|
262
|
-
const entry = { id: hId, short_label, text: candidate.text, created_at: candidate.created_at, author: candidate.author, author_id: candidate.author_id, project_id: candidate.project_id, host_id: candidate.host_id, session_id: candidate.session_id, from: candidate.from ?? '', to: candidate.to ?? '', status: 'open', tags: candidate.tags, related_paths: candidate.related_paths };
|
|
275
|
+
const entry = { id: hId, short_label, text: candidate.text, created_at: candidate.created_at, author: candidate.author, author_id: candidate.author_id, project_id: candidate.project_id, host_id: candidate.host_id, session_id: candidate.session_id, from: candidate.from ?? '', to: candidate.to ?? '', status: 'open', tags: candidate.tags, plan_id: candidate.plan_id, narrative: candidate.narrative, related_paths: candidate.related_paths };
|
|
263
276
|
state.open_handoffs.push(entry);
|
|
264
277
|
promotedItemId = entry.id;
|
|
265
278
|
break;
|
|
@@ -2,7 +2,7 @@ import { memoryExists } from '../core/io.js';
|
|
|
2
2
|
import { mutate } from '../core/mutation-pipeline.js';
|
|
3
3
|
import { loadClaim, listClaims, releaseClaim } from '../core/claims.js';
|
|
4
4
|
import { rebuildProjectMd } from '../core/markdown.js';
|
|
5
|
-
import { loadState,
|
|
5
|
+
import { loadState, mutateState } from '../core/state.js';
|
|
6
6
|
export function runReleaseClaim(id, options = {}) {
|
|
7
7
|
if (!memoryExists(options.cwd)) {
|
|
8
8
|
console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
|
|
@@ -13,10 +13,12 @@ export function runReleaseClaim(id, options = {}) {
|
|
|
13
13
|
mutate({ cwd: options.cwd }, () => {
|
|
14
14
|
const existing = loadClaim(id, options.cwd);
|
|
15
15
|
claim = releaseClaim(id, options.cwd);
|
|
16
|
-
let state = loadState(options.cwd);
|
|
17
16
|
if (existing.plan_id) {
|
|
18
|
-
const
|
|
19
|
-
|
|
17
|
+
const updated = mutateState((state) => {
|
|
18
|
+
const plan = state.plan_items.find((item) => item.id === existing.plan_id);
|
|
19
|
+
if (!plan) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
20
22
|
const otherActiveClaims = listClaims(options.cwd).filter((item) => item.status === 'active' && item.plan_id === existing.plan_id);
|
|
21
23
|
if (options.planStatus) {
|
|
22
24
|
plan.status = options.planStatus;
|
|
@@ -28,10 +30,13 @@ export function runReleaseClaim(id, options = {}) {
|
|
|
28
30
|
plan.assignee = undefined;
|
|
29
31
|
}
|
|
30
32
|
plan.updated_at = new Date().toISOString();
|
|
31
|
-
|
|
33
|
+
return true;
|
|
34
|
+
}, options.cwd, { writeProjectMarkdown: false });
|
|
35
|
+
if (updated) {
|
|
36
|
+
const state = loadState(options.cwd);
|
|
37
|
+
rebuildProjectMd(state, options.cwd);
|
|
32
38
|
}
|
|
33
39
|
}
|
|
34
|
-
rebuildProjectMd(state, options.cwd);
|
|
35
40
|
});
|
|
36
41
|
console.log(`✔ Claim [${id}] released (was: ${claim.agent} → ${claim.scope})`);
|
|
37
42
|
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { loadConfig } from '../core/config.js';
|
|
3
|
+
import { memoryExists } from '../core/io.js';
|
|
4
|
+
import { checkBrainclawInstallableUpdate, getInstalledBrainclawVersion, } from '../core/brainclaw-version.js';
|
|
5
|
+
/**
|
|
6
|
+
* Generate agent-first release notes from git log since a given ref.
|
|
7
|
+
* Returns structured notes suitable for --agent-release-notes.
|
|
8
|
+
*/
|
|
9
|
+
export function generateAgentReleaseNotes(cwd, since) {
|
|
10
|
+
const version = getInstalledBrainclawVersion();
|
|
11
|
+
const baseRef = since ?? findLastVersionTag(cwd) ?? 'HEAD~20';
|
|
12
|
+
let commits;
|
|
13
|
+
try {
|
|
14
|
+
const raw = execSync(`git log ${baseRef}..HEAD --oneline --no-decorate`, {
|
|
15
|
+
cwd,
|
|
16
|
+
encoding: 'utf-8',
|
|
17
|
+
timeout: 10000,
|
|
18
|
+
});
|
|
19
|
+
commits = raw.split('\n').map((l) => l.trim()).filter(Boolean);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
commits = [];
|
|
23
|
+
}
|
|
24
|
+
if (commits.length === 0) {
|
|
25
|
+
return {
|
|
26
|
+
summary: `Brainclaw ${version} — no changes since last release.`,
|
|
27
|
+
breaking_risk: 'none',
|
|
28
|
+
action_recommendation: 'No update needed.',
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const highlights = categorizeCommits(commits);
|
|
32
|
+
const hasBreaking = commits.some((c) => /breaking|BREAKING/i.test(c));
|
|
33
|
+
const hasFix = commits.some((c) => /^[a-f0-9]+ fix/i.test(c));
|
|
34
|
+
const hasFeat = commits.some((c) => /^[a-f0-9]+ feat/i.test(c));
|
|
35
|
+
const summaryParts = [];
|
|
36
|
+
if (hasFeat)
|
|
37
|
+
summaryParts.push('new features');
|
|
38
|
+
if (hasFix)
|
|
39
|
+
summaryParts.push('bug fixes');
|
|
40
|
+
if (!hasFeat && !hasFix)
|
|
41
|
+
summaryParts.push('improvements');
|
|
42
|
+
const summary = `Brainclaw ${version} — ${summaryParts.join(' and ')} (${commits.length} commits).`;
|
|
43
|
+
const breakingRisk = hasBreaking ? 'high' : hasFeat ? 'low' : 'none';
|
|
44
|
+
return {
|
|
45
|
+
summary,
|
|
46
|
+
agent_relevance: hasFeat
|
|
47
|
+
? 'New capabilities available — review highlights for agent workflow improvements.'
|
|
48
|
+
: hasFix
|
|
49
|
+
? 'Bug fixes that may affect agent reliability.'
|
|
50
|
+
: 'Maintenance release.',
|
|
51
|
+
breaking_risk: breakingRisk,
|
|
52
|
+
recommended_for: hasBreaking ? ['operators'] : ['all'],
|
|
53
|
+
highlights: highlights.slice(0, 5),
|
|
54
|
+
action_recommendation: hasBreaking
|
|
55
|
+
? 'Review breaking changes before updating. Operator confirmation recommended.'
|
|
56
|
+
: 'Safe to update. No operator confirmation needed.',
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function findLastVersionTag(cwd) {
|
|
60
|
+
try {
|
|
61
|
+
const tag = execSync('git describe --tags --abbrev=0 HEAD', {
|
|
62
|
+
cwd,
|
|
63
|
+
encoding: 'utf-8',
|
|
64
|
+
timeout: 5000,
|
|
65
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
66
|
+
}).trim();
|
|
67
|
+
return tag.length > 0 ? tag : undefined;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function categorizeCommits(commits) {
|
|
74
|
+
const highlights = [];
|
|
75
|
+
const seen = new Set();
|
|
76
|
+
for (const commit of commits) {
|
|
77
|
+
// Strip hash prefix
|
|
78
|
+
const msg = commit.replace(/^[a-f0-9]+\s+/, '');
|
|
79
|
+
const category = extractCategory(msg);
|
|
80
|
+
if (category && !seen.has(category)) {
|
|
81
|
+
seen.add(category);
|
|
82
|
+
highlights.push(msg);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// If not enough categorized, add uncategorized ones
|
|
86
|
+
if (highlights.length < 5) {
|
|
87
|
+
for (const commit of commits) {
|
|
88
|
+
const msg = commit.replace(/^[a-f0-9]+\s+/, '');
|
|
89
|
+
if (!highlights.includes(msg)) {
|
|
90
|
+
highlights.push(msg);
|
|
91
|
+
if (highlights.length >= 5)
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return highlights;
|
|
97
|
+
}
|
|
98
|
+
function extractCategory(msg) {
|
|
99
|
+
const match = /^(feat|fix|chore|test|docs|refactor|perf|ci|build)\b/i.exec(msg);
|
|
100
|
+
return match?.[1]?.toLowerCase();
|
|
101
|
+
}
|
|
102
|
+
export function runReleaseNotes(options = {}) {
|
|
103
|
+
const cwd = options.cwd ?? process.cwd();
|
|
104
|
+
if (options.generate) {
|
|
105
|
+
const notes = generateAgentReleaseNotes(cwd, options.since);
|
|
106
|
+
if (options.json) {
|
|
107
|
+
console.log(JSON.stringify(notes, null, 2));
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
console.log(`Summary: ${notes.summary}`);
|
|
111
|
+
if (notes.agent_relevance)
|
|
112
|
+
console.log(`Agent relevance: ${notes.agent_relevance}`);
|
|
113
|
+
console.log(`Breaking risk: ${notes.breaking_risk ?? 'none'}`);
|
|
114
|
+
if (notes.recommended_for?.length)
|
|
115
|
+
console.log(`Recommended for: ${notes.recommended_for.join(', ')}`);
|
|
116
|
+
if (notes.highlights?.length) {
|
|
117
|
+
console.log('Highlights:');
|
|
118
|
+
for (const h of notes.highlights)
|
|
119
|
+
console.log(` • ${h}`);
|
|
120
|
+
}
|
|
121
|
+
if (notes.action_recommendation)
|
|
122
|
+
console.log(`Action: ${notes.action_recommendation}`);
|
|
123
|
+
}
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// Show current release notes from configured update source
|
|
127
|
+
if (!memoryExists(cwd)) {
|
|
128
|
+
console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
const config = loadConfig(cwd);
|
|
132
|
+
const updateCheck = checkBrainclawInstallableUpdate(config, cwd, { useDefaultNpmSource: true });
|
|
133
|
+
const arn = updateCheck.agent_release_notes;
|
|
134
|
+
if (options.json) {
|
|
135
|
+
console.log(JSON.stringify({
|
|
136
|
+
status: updateCheck.status,
|
|
137
|
+
latest_installable_version: updateCheck.latest_installable_version,
|
|
138
|
+
agent_release_notes: arn ?? null,
|
|
139
|
+
release_notes: updateCheck.release_notes ?? null,
|
|
140
|
+
}, null, 2));
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (arn) {
|
|
144
|
+
console.log(`Version: ${updateCheck.latest_installable_version ?? 'unknown'}`);
|
|
145
|
+
console.log(`Summary: ${arn.summary}`);
|
|
146
|
+
if (arn.agent_relevance)
|
|
147
|
+
console.log(`Agent relevance: ${arn.agent_relevance}`);
|
|
148
|
+
console.log(`Breaking risk: ${arn.breaking_risk ?? 'none'}`);
|
|
149
|
+
if (arn.recommended_for?.length)
|
|
150
|
+
console.log(`Recommended for: ${arn.recommended_for.join(', ')}`);
|
|
151
|
+
if (arn.highlights?.length) {
|
|
152
|
+
console.log('Highlights:');
|
|
153
|
+
for (const h of arn.highlights)
|
|
154
|
+
console.log(` • ${h}`);
|
|
155
|
+
}
|
|
156
|
+
if (arn.action_recommendation)
|
|
157
|
+
console.log(`Action: ${arn.action_recommendation}`);
|
|
158
|
+
}
|
|
159
|
+
else if (updateCheck.release_notes) {
|
|
160
|
+
console.log(`Version: ${updateCheck.latest_installable_version ?? 'unknown'}`);
|
|
161
|
+
console.log(updateCheck.release_notes);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
console.log('No agent release notes available for the configured update source.');
|
|
165
|
+
if (updateCheck.status === 'not_configured') {
|
|
166
|
+
console.log('Configure brainclaw_update_source in your project config to enable update checks.');
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=release-notes.js.map
|