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
package/dist/commands/upgrade.js
CHANGED
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
import { ensureMemoryDir, memoryDir, memoryExists, storeLockPath } from '../core/io.js';
|
|
4
5
|
import { loadState, persistState } from '../core/state.js';
|
|
5
6
|
import { scanMigrationStatus } from '../core/migration.js';
|
|
6
7
|
import { commitMemoryChange, initMemoryRepo } from '../core/memory-git.js';
|
|
7
|
-
import { BRAINCLAW_SECTION_END, BRAINCLAW_SECTION_START, buildBrainclawSection, buildClaudeCodeCommandText, ensureClaudeCodeCommand, hasBrainclawSection, } from '../core/agent-files.js';
|
|
8
|
+
import { BRAINCLAW_SECTION_END, BRAINCLAW_SECTION_START, buildBrainclawSection, buildClaudeCodeCommandText, ensureClaudeCodeCommand, hasBrainclawSection, patchAllMcpConfigs, } from '../core/agent-files.js';
|
|
8
9
|
import { loadConfig } from '../core/config.js';
|
|
10
|
+
import { checkBrainclawInstallableUpdate, getInstalledBrainclawVersion } from '../core/brainclaw-version.js';
|
|
11
|
+
import { resolvePrimaryStore } from '../core/store-resolution.js';
|
|
12
|
+
import { BackupError, createBackup, listBackups, restoreBackup, } from '../core/upgrades/backup.js';
|
|
13
|
+
import { archivePendingCandidates, } from '../core/upgrades/patches/candidate-archive.js';
|
|
14
|
+
import { stripHandoffReview, } from '../core/upgrades/patches/handoff-review-strip.js';
|
|
15
|
+
import { rolloutProvenance, } from '../core/upgrades/patches/provenance-rollout.js';
|
|
16
|
+
import { IMPLICIT_BASELINE_VERSION, KNOWN_STORE_SCHEMA_VERSIONS, V1_TARGET_SCHEMA_VERSION, bumpSchemaVersion, readSchemaVersion, } from '../core/upgrades/schema-version.js';
|
|
17
|
+
import { withLock } from '../core/lock.js';
|
|
9
18
|
import { renderAgentExportForAgent, writeAgentExportForAgent } from './export.js';
|
|
10
19
|
import { generateCursorHook, writeHook } from './hooks.js';
|
|
20
|
+
/** Schema target versions supported by `brainclaw upgrade --to=<version>`. */
|
|
21
|
+
export const SUPPORTED_SCHEMA_TARGETS = ['1.0'];
|
|
11
22
|
/**
|
|
12
23
|
* Entity directory layout mapping: legacy flat name → entity-aligned path.
|
|
13
24
|
* Must match ENTITY_DIR_MAP in io.ts.
|
|
@@ -18,6 +29,7 @@ const ENTITY_DIRS = [
|
|
|
18
29
|
{ legacy: 'traps', entity: 'memory/traps' },
|
|
19
30
|
{ legacy: 'instructions', entity: 'memory/instructions' },
|
|
20
31
|
{ legacy: 'plans', entity: 'coordination/plans' },
|
|
32
|
+
{ legacy: 'sequences', entity: 'coordination/sequences' },
|
|
21
33
|
{ legacy: 'claims', entity: 'coordination/claims' },
|
|
22
34
|
{ legacy: 'handoffs', entity: 'coordination/handoffs' },
|
|
23
35
|
{ legacy: 'sessions', entity: 'coordination/sessions' },
|
|
@@ -35,15 +47,137 @@ const WORKSPACE_EXPORT_REFRESH_AGENTS = [
|
|
|
35
47
|
];
|
|
36
48
|
export function runUpgrade(options = {}) {
|
|
37
49
|
const cwd = options.cwd ?? process.cwd();
|
|
38
|
-
|
|
39
|
-
|
|
50
|
+
// Take the common store-wide mutation lock for the whole
|
|
51
|
+
// upgrade/rollback cycle so normal writers cannot race the park/swap
|
|
52
|
+
// window. The lock target lives alongside `.brainclaw/`, not inside
|
|
53
|
+
// it, so it survives rename-based swaps.
|
|
54
|
+
const lockPath = storeLockPath(cwd);
|
|
55
|
+
try {
|
|
56
|
+
withLock(lockPath, () => {
|
|
57
|
+
if (!memoryExists(cwd)) {
|
|
58
|
+
console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
runUpgradeInner(cwd, options);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
const message = error.message;
|
|
66
|
+
if (message.startsWith('Could not acquire lock')) {
|
|
67
|
+
console.error('Error: another brainclaw store mutation or upgrade is in progress. Retry once it completes.');
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function runUpgradeInner(cwd, options) {
|
|
74
|
+
// Rollback short-circuit: restore the most recent backup, park the
|
|
75
|
+
// current live store, and exit. Runs before any other upgrade work.
|
|
76
|
+
if (options.rollback) {
|
|
77
|
+
runRollback(cwd, options);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
// Validate --to target before touching anything.
|
|
81
|
+
if (options.to && !SUPPORTED_SCHEMA_TARGETS.includes(options.to)) {
|
|
82
|
+
console.error(`Error: unsupported schema target "${options.to}". Supported: ${SUPPORTED_SCHEMA_TARGETS.join(', ')}`);
|
|
40
83
|
process.exit(1);
|
|
41
84
|
}
|
|
85
|
+
if (options.to && options.backup === false && !options.dryRun) {
|
|
86
|
+
console.error(`Error: --to=${options.to} requires a backup. Remove --no-backup or re-run with --dry-run.`);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
// Backup pass: create a timestamped backup before any modification.
|
|
90
|
+
// Explicit --backup always triggers it; schema-targeted upgrades are
|
|
91
|
+
// forced to carry a recovery point on real runs.
|
|
92
|
+
const backupRequested = options.to !== undefined || options.backup === true;
|
|
93
|
+
let backupHandle;
|
|
94
|
+
if (backupRequested && !options.dryRun) {
|
|
95
|
+
try {
|
|
96
|
+
backupHandle = createUpgradeBackup(cwd, options);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
const message = error instanceof BackupError ? error.message : error.message;
|
|
100
|
+
console.error(`Error: backup failed — ${message}`);
|
|
101
|
+
console.error('Aborting upgrade. No changes made.');
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
if (!options.json) {
|
|
105
|
+
console.log(`✔ Backup created at ${backupHandle.backupPath}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else if (backupRequested && options.dryRun && !options.json) {
|
|
109
|
+
console.log('(dry run — would create backup before upgrade)');
|
|
110
|
+
}
|
|
111
|
+
// One-shot v1.0 schema patches. Runs only when --to=<version> is set.
|
|
112
|
+
// Each patch is idempotent — re-running after success is a no-op.
|
|
113
|
+
const schemaPatchResults = {};
|
|
114
|
+
if (options.to === '1.0') {
|
|
115
|
+
schemaPatchResults.candidateArchive = runCandidateArchivePatch(cwd, options);
|
|
116
|
+
schemaPatchResults.handoffReviewStrip = runHandoffReviewStripPatch(cwd, options);
|
|
117
|
+
schemaPatchResults.provenanceRollout = runProvenanceRolloutPatch(cwd, options);
|
|
118
|
+
// Schema-version bump runs last, only after every patch succeeded.
|
|
119
|
+
// If an earlier patch threw, we never reach here — the store keeps
|
|
120
|
+
// its prior version and the backup remains the recovery path.
|
|
121
|
+
schemaPatchResults.schemaVersionBump = runSchemaVersionBump(cwd, options);
|
|
122
|
+
}
|
|
123
|
+
// Self-update: install a newer brainclaw version from npm/local-pack before upgrading memory
|
|
124
|
+
if (options.selfUpdate && !options.dryRun) {
|
|
125
|
+
const config = loadConfig(cwd);
|
|
126
|
+
const updateCheck = checkBrainclawInstallableUpdate(config, cwd, { useDefaultNpmSource: true });
|
|
127
|
+
if (updateCheck.status === 'update_available' && updateCheck.install_command) {
|
|
128
|
+
const installedVersion = getInstalledBrainclawVersion();
|
|
129
|
+
if (!options.json) {
|
|
130
|
+
console.log(`📦 New version available: ${updateCheck.latest_installable_version} (current: ${installedVersion})`);
|
|
131
|
+
console.log(` Running: ${updateCheck.install_command}`);
|
|
132
|
+
}
|
|
133
|
+
if (!options.dryRun) {
|
|
134
|
+
const parts = updateCheck.install_command.split(' ');
|
|
135
|
+
const result = spawnSync(parts[0], parts.slice(1), { stdio: 'inherit', encoding: 'utf-8' });
|
|
136
|
+
if (result.status !== 0) {
|
|
137
|
+
console.error('Error: install command failed. Check output above.');
|
|
138
|
+
if (!options.json) {
|
|
139
|
+
console.error(` Re-run manually: ${updateCheck.install_command}`);
|
|
140
|
+
}
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
if (!options.json) {
|
|
144
|
+
console.log(`✔ Brainclaw updated to ${updateCheck.latest_installable_version}`);
|
|
145
|
+
console.log(' MCP servers using this installation will need a restart.');
|
|
146
|
+
console.log(' In Claude Code: use /restart or restart the MCP session.');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
if (!options.json) {
|
|
151
|
+
console.log(` (dry run — would run: ${updateCheck.install_command})`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else if (updateCheck.status === 'up_to_date') {
|
|
156
|
+
if (!options.json) {
|
|
157
|
+
console.log(`✔ Brainclaw is already up to date (${getInstalledBrainclawVersion()})`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
if (!options.json) {
|
|
162
|
+
console.log(`ℹ No installable update found (${updateCheck.status})`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (!options.json)
|
|
166
|
+
console.log('');
|
|
167
|
+
}
|
|
168
|
+
else if (options.selfUpdate && options.dryRun && !options.json) {
|
|
169
|
+
console.log('(dry run — skipping self-update check)');
|
|
170
|
+
console.log('');
|
|
171
|
+
}
|
|
42
172
|
const base = memoryDir(cwd);
|
|
43
173
|
const actions = [];
|
|
44
174
|
let movedFiles = 0;
|
|
45
|
-
// Phase 1: Ensure entity-aligned directories exist
|
|
46
|
-
|
|
175
|
+
// Phase 1: Ensure entity-aligned directories exist. Skipped in
|
|
176
|
+
// dry-run so the command is truly side-effect free — without this
|
|
177
|
+
// gate, ensureMemoryDir creates entity subdirs on first pass.
|
|
178
|
+
if (!options.dryRun) {
|
|
179
|
+
ensureMemoryDir(cwd);
|
|
180
|
+
}
|
|
47
181
|
// Phase 2: Detect and plan file migrations (legacy → entity)
|
|
48
182
|
for (const { legacy, entity } of ENTITY_DIRS) {
|
|
49
183
|
const legacyDir = path.join(base, legacy);
|
|
@@ -117,6 +251,8 @@ export function runUpgrade(options = {}) {
|
|
|
117
251
|
persistState(state, cwd);
|
|
118
252
|
}
|
|
119
253
|
const refreshedAgentFiles = refreshManagedWorkspaceAgentFiles(cwd);
|
|
254
|
+
// Patch all MCP config files to use the newly resolved brainclaw binary
|
|
255
|
+
const patchedMcpConfigs = patchAllMcpConfigs(cwd);
|
|
120
256
|
// Clean up empty legacy directories (recursively removes empty subdirs first)
|
|
121
257
|
let removedDirs = 0;
|
|
122
258
|
for (const { legacy } of ENTITY_DIRS) {
|
|
@@ -133,6 +269,8 @@ export function runUpgrade(options = {}) {
|
|
|
133
269
|
`${outdated.length} schema(s) migrated`,
|
|
134
270
|
`${refreshedAgentFiles.length} managed agent file(s) refreshed`,
|
|
135
271
|
];
|
|
272
|
+
if (patchedMcpConfigs.length > 0)
|
|
273
|
+
parts.push(`${patchedMcpConfigs.length} MCP config(s) patched`);
|
|
136
274
|
if (removedDirs > 0)
|
|
137
275
|
parts.push(`${removedDirs} empty legacy dir(s) removed`);
|
|
138
276
|
console.log(`✔ Upgrade complete: ${parts.join(', ')}.`);
|
|
@@ -379,4 +517,173 @@ function outputJson(actions, dryRun) {
|
|
|
379
517
|
actions,
|
|
380
518
|
}, null, 2));
|
|
381
519
|
}
|
|
520
|
+
function runSchemaVersionBump(cwd, options) {
|
|
521
|
+
const store = resolvePrimaryStore(cwd);
|
|
522
|
+
if (!store) {
|
|
523
|
+
console.error(`Error: no .brainclaw/ store resolved from ${cwd}`);
|
|
524
|
+
process.exit(1);
|
|
525
|
+
}
|
|
526
|
+
const result = bumpSchemaVersion({
|
|
527
|
+
storePath: store.storePath,
|
|
528
|
+
to: V1_TARGET_SCHEMA_VERSION,
|
|
529
|
+
patches: ['candidate_archive', 'handoff_review_strip', 'provenance_rollout'],
|
|
530
|
+
reason: 'brainclaw upgrade --to=1.0 migration complete',
|
|
531
|
+
dryRun: options.dryRun ?? false,
|
|
532
|
+
});
|
|
533
|
+
if (!options.json) {
|
|
534
|
+
if (result.status === 'noop') {
|
|
535
|
+
console.log(`✔ Schema version: already at ${result.to} (no bump needed).`);
|
|
536
|
+
}
|
|
537
|
+
else if (result.status === 'planned') {
|
|
538
|
+
console.log(`(dry run — would bump schema ${result.from} → ${result.to})`);
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
console.log(`✔ Schema version: bumped ${result.from} → ${result.to}. Marker at ${result.filePath}`);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
return result;
|
|
545
|
+
}
|
|
546
|
+
function runProvenanceRolloutPatch(cwd, options) {
|
|
547
|
+
const store = resolvePrimaryStore(cwd);
|
|
548
|
+
if (!store) {
|
|
549
|
+
console.error(`Error: no .brainclaw/ store resolved from ${cwd}`);
|
|
550
|
+
process.exit(1);
|
|
551
|
+
}
|
|
552
|
+
const result = rolloutProvenance({
|
|
553
|
+
storePath: store.storePath,
|
|
554
|
+
dryRun: options.dryRun ?? false,
|
|
555
|
+
});
|
|
556
|
+
if (!options.json) {
|
|
557
|
+
if (result.status === 'noop') {
|
|
558
|
+
console.log(`✔ Provenance rollout (P6.3): ${result.scanned} record(s) scanned, all already carry provenance.`);
|
|
559
|
+
}
|
|
560
|
+
else if (result.status === 'planned') {
|
|
561
|
+
console.log(`(dry run — would stamp legacy provenance on ${result.stamped.length} of ${result.scanned} record(s))`);
|
|
562
|
+
}
|
|
563
|
+
else {
|
|
564
|
+
const breakdown = Object.entries(result.countsByKind)
|
|
565
|
+
.map(([k, c]) => `${k}:${c}`)
|
|
566
|
+
.join(', ');
|
|
567
|
+
console.log(`✔ Provenance rollout (P6.3): ${result.stamped.length} of ${result.scanned} record(s) stamped legacy (${breakdown}). Log at ${result.logPath}`);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
return result;
|
|
571
|
+
}
|
|
572
|
+
function runHandoffReviewStripPatch(cwd, options) {
|
|
573
|
+
const store = resolvePrimaryStore(cwd);
|
|
574
|
+
if (!store) {
|
|
575
|
+
console.error(`Error: no .brainclaw/ store resolved from ${cwd}`);
|
|
576
|
+
process.exit(1);
|
|
577
|
+
}
|
|
578
|
+
const result = stripHandoffReview({
|
|
579
|
+
storePath: store.storePath,
|
|
580
|
+
dryRun: options.dryRun ?? false,
|
|
581
|
+
});
|
|
582
|
+
if (!options.json) {
|
|
583
|
+
if (result.status === 'noop') {
|
|
584
|
+
console.log(`✔ Handoff review-strip (P6.1): ${result.scanned} handoff(s) scanned, none carry a review block.`);
|
|
585
|
+
}
|
|
586
|
+
else if (result.status === 'planned') {
|
|
587
|
+
console.log(`(dry run — would strip review from ${result.stripped.length} handoff(s) out of ${result.scanned} scanned)`);
|
|
588
|
+
}
|
|
589
|
+
else {
|
|
590
|
+
console.log(`✔ Handoff review-strip (P6.1): ${result.stripped.length} of ${result.scanned} handoff(s) rewritten. Log at ${result.logPath}`);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return result;
|
|
594
|
+
}
|
|
595
|
+
function runCandidateArchivePatch(cwd, options) {
|
|
596
|
+
const store = resolvePrimaryStore(cwd);
|
|
597
|
+
if (!store) {
|
|
598
|
+
console.error(`Error: no .brainclaw/ store resolved from ${cwd}`);
|
|
599
|
+
process.exit(1);
|
|
600
|
+
}
|
|
601
|
+
const result = archivePendingCandidates({
|
|
602
|
+
storePath: store.storePath,
|
|
603
|
+
dryRun: options.dryRun ?? false,
|
|
604
|
+
});
|
|
605
|
+
if (!options.json) {
|
|
606
|
+
if (result.status === 'noop') {
|
|
607
|
+
console.log('✔ Candidate archive (P6.6): no pending candidates to archive.');
|
|
608
|
+
}
|
|
609
|
+
else if (result.status === 'planned') {
|
|
610
|
+
console.log(`(dry run — would archive ${result.moved.length} pending candidate(s) to ${result.archiveDir})`);
|
|
611
|
+
}
|
|
612
|
+
else {
|
|
613
|
+
console.log(`✔ Candidate archive (P6.6): ${result.moved.length} pending candidate(s) archived to ${result.archiveDir}`);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
return result;
|
|
617
|
+
}
|
|
618
|
+
function createUpgradeBackup(cwd, options) {
|
|
619
|
+
const store = resolvePrimaryStore(cwd);
|
|
620
|
+
if (!store) {
|
|
621
|
+
throw new BackupError('no_store', `No .brainclaw/ store resolved from ${cwd}`);
|
|
622
|
+
}
|
|
623
|
+
const note = options.to
|
|
624
|
+
? `brainclaw upgrade --to=${options.to}`
|
|
625
|
+
: 'brainclaw upgrade';
|
|
626
|
+
return createBackup({
|
|
627
|
+
storePath: store.storePath,
|
|
628
|
+
note,
|
|
629
|
+
storeSchemaVersion: readSchemaVersion(store.storePath).current,
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
function runRollback(cwd, options) {
|
|
633
|
+
const store = resolvePrimaryStore(cwd);
|
|
634
|
+
if (!store) {
|
|
635
|
+
console.error(`Error: no .brainclaw/ store resolved from ${cwd}`);
|
|
636
|
+
process.exit(1);
|
|
637
|
+
}
|
|
638
|
+
const backups = listBackups(store.storePath);
|
|
639
|
+
if (backups.length === 0) {
|
|
640
|
+
if (options.json) {
|
|
641
|
+
console.log(JSON.stringify({ status: 'noop', reason: 'no_backups', store_path: store.storePath }, null, 2));
|
|
642
|
+
}
|
|
643
|
+
else {
|
|
644
|
+
console.error(`Error: no backups found next to ${store.storePath}. Nothing to roll back.`);
|
|
645
|
+
}
|
|
646
|
+
process.exit(1);
|
|
647
|
+
}
|
|
648
|
+
const target = backups[0];
|
|
649
|
+
if (options.dryRun) {
|
|
650
|
+
if (options.json) {
|
|
651
|
+
console.log(JSON.stringify({
|
|
652
|
+
status: 'dry_run',
|
|
653
|
+
backup_path: target.backupPath,
|
|
654
|
+
created_at: target.manifest.created_at,
|
|
655
|
+
}, null, 2));
|
|
656
|
+
}
|
|
657
|
+
else {
|
|
658
|
+
console.log(`(dry run — would restore ${target.backupPath} created ${target.manifest.created_at})`);
|
|
659
|
+
}
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
try {
|
|
663
|
+
const result = restoreBackup({
|
|
664
|
+
storePath: store.storePath,
|
|
665
|
+
backupPath: target.backupPath,
|
|
666
|
+
acceptSchemaVersions: [...KNOWN_STORE_SCHEMA_VERSIONS],
|
|
667
|
+
});
|
|
668
|
+
if (options.json) {
|
|
669
|
+
console.log(JSON.stringify({
|
|
670
|
+
status: 'rolled_back',
|
|
671
|
+
store_path: store.storePath,
|
|
672
|
+
backup_path: target.backupPath,
|
|
673
|
+
parked_path: result.parkedPath,
|
|
674
|
+
created_at: target.manifest.created_at,
|
|
675
|
+
}, null, 2));
|
|
676
|
+
}
|
|
677
|
+
else {
|
|
678
|
+
console.log(`✔ Rolled back to ${target.backupPath} (created ${target.manifest.created_at})`);
|
|
679
|
+
console.log(` Restored schema version ${target.manifest.store_schema_version ?? IMPLICIT_BASELINE_VERSION}`);
|
|
680
|
+
console.log(` Previous live store parked at ${result.parkedPath}`);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
catch (error) {
|
|
684
|
+
const message = error instanceof BackupError ? error.message : error.message;
|
|
685
|
+
console.error(`Error: rollback failed — ${message}`);
|
|
686
|
+
process.exit(1);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
382
689
|
//# sourceMappingURL=upgrade.js.map
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { memoryDir } from '../core/io.js';
|
|
4
|
+
export function runUsage(options = {}) {
|
|
5
|
+
const cwd = options.cwd ?? process.cwd();
|
|
6
|
+
const usagePath = path.join(memoryDir(cwd), 'usage.jsonl');
|
|
7
|
+
if (!fs.existsSync(usagePath)) {
|
|
8
|
+
if (options.json) {
|
|
9
|
+
console.log(JSON.stringify({ records: 0, message: 'No usage data yet. MCP tool calls will be tracked automatically.' }));
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
console.log('No usage data yet. MCP tool calls will be tracked automatically.');
|
|
13
|
+
}
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const lines = fs.readFileSync(usagePath, 'utf-8').split('\n').filter(Boolean);
|
|
17
|
+
const cutoff = options.days ? Date.now() - options.days * 86_400_000 : 0;
|
|
18
|
+
const records = [];
|
|
19
|
+
for (const line of lines) {
|
|
20
|
+
try {
|
|
21
|
+
const r = JSON.parse(line);
|
|
22
|
+
if (cutoff && Date.parse(r.ts) < cutoff)
|
|
23
|
+
continue;
|
|
24
|
+
if (options.agent && r.agent !== options.agent)
|
|
25
|
+
continue;
|
|
26
|
+
if (options.tool && r.tool !== options.tool)
|
|
27
|
+
continue;
|
|
28
|
+
records.push(r);
|
|
29
|
+
}
|
|
30
|
+
catch { /* skip malformed */ }
|
|
31
|
+
}
|
|
32
|
+
if (records.length === 0) {
|
|
33
|
+
if (options.json) {
|
|
34
|
+
console.log(JSON.stringify({ records: 0, filters: { agent: options.agent, tool: options.tool, days: options.days } }));
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
console.log('No matching usage records.');
|
|
38
|
+
}
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
// Aggregate by agent → tool
|
|
42
|
+
const byAgent = {};
|
|
43
|
+
for (const r of records) {
|
|
44
|
+
const agentKey = r.agent ?? 'unknown';
|
|
45
|
+
if (!byAgent[agentKey]) {
|
|
46
|
+
byAgent[agentKey] = { calls: 0, total_chars: 0, total_tokens_est: 0, tools: {} };
|
|
47
|
+
}
|
|
48
|
+
const agent = byAgent[agentKey];
|
|
49
|
+
agent.calls++;
|
|
50
|
+
agent.total_chars += r.chars;
|
|
51
|
+
agent.total_tokens_est += r.tokens_est;
|
|
52
|
+
if (!agent.tools[r.tool]) {
|
|
53
|
+
agent.tools[r.tool] = { calls: 0, total_chars: 0, total_tokens_est: 0, errors: 0 };
|
|
54
|
+
}
|
|
55
|
+
const tool = agent.tools[r.tool];
|
|
56
|
+
tool.calls++;
|
|
57
|
+
tool.total_chars += r.chars;
|
|
58
|
+
tool.total_tokens_est += r.tokens_est;
|
|
59
|
+
if (r.is_error)
|
|
60
|
+
tool.errors++;
|
|
61
|
+
}
|
|
62
|
+
const totalChars = records.reduce((s, r) => s + r.chars, 0);
|
|
63
|
+
const totalTokens = records.reduce((s, r) => s + r.tokens_est, 0);
|
|
64
|
+
if (options.json) {
|
|
65
|
+
console.log(JSON.stringify({
|
|
66
|
+
records: records.length,
|
|
67
|
+
total_chars: totalChars,
|
|
68
|
+
total_tokens_est: totalTokens,
|
|
69
|
+
by_agent: byAgent,
|
|
70
|
+
filters: { agent: options.agent, tool: options.tool, days: options.days },
|
|
71
|
+
}, null, 2));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// Render table
|
|
75
|
+
const periodLabel = options.days ? `last ${options.days} day(s)` : 'all time';
|
|
76
|
+
console.log(`Brainclaw context usage (${periodLabel}) — ${records.length} MCP call(s)\n`);
|
|
77
|
+
console.log(` Total: ${formatChars(totalChars)} (~${formatTokens(totalTokens)} tokens)\n`);
|
|
78
|
+
for (const [agentName, stats] of Object.entries(byAgent).sort((a, b) => b[1].total_tokens_est - a[1].total_tokens_est)) {
|
|
79
|
+
console.log(` ${agentName}: ${stats.calls} calls, ${formatChars(stats.total_chars)} (~${formatTokens(stats.total_tokens_est)} tokens)`);
|
|
80
|
+
const sortedTools = Object.entries(stats.tools).sort((a, b) => b[1].total_tokens_est - a[1].total_tokens_est);
|
|
81
|
+
for (const [toolName, toolStats] of sortedTools) {
|
|
82
|
+
const errSuffix = toolStats.errors > 0 ? ` (${toolStats.errors} errors)` : '';
|
|
83
|
+
const shortName = toolName.replace(/^bclaw_/, '');
|
|
84
|
+
console.log(` ${shortName}: ${toolStats.calls}× ${formatChars(toolStats.total_chars)}${errSuffix}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function formatChars(chars) {
|
|
89
|
+
if (chars < 1000)
|
|
90
|
+
return `${chars} chars`;
|
|
91
|
+
if (chars < 1_000_000)
|
|
92
|
+
return `${(chars / 1000).toFixed(1)}K chars`;
|
|
93
|
+
return `${(chars / 1_000_000).toFixed(2)}M chars`;
|
|
94
|
+
}
|
|
95
|
+
function formatTokens(tokens) {
|
|
96
|
+
if (tokens < 1000)
|
|
97
|
+
return `${tokens}`;
|
|
98
|
+
if (tokens < 1_000_000)
|
|
99
|
+
return `${(tokens / 1000).toFixed(1)}K`;
|
|
100
|
+
return `${(tokens / 1_000_000).toFixed(2)}M`;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=usage.js.map
|
package/dist/commands/version.js
CHANGED
|
@@ -3,6 +3,8 @@ import path from 'node:path';
|
|
|
3
3
|
import { loadConfig, saveConfig } from '../core/config.js';
|
|
4
4
|
import { memoryExists } from '../core/io.js';
|
|
5
5
|
import { assessBrainclawVersion, checkBrainclawInstallableUpdate, DEFAULT_LOCAL_RELEASE_MANIFEST_PATH, publishLocalBrainclawRelease, } from '../core/brainclaw-version.js';
|
|
6
|
+
import { AgentReleaseNotesSchema } from '../core/schema.js';
|
|
7
|
+
import { generateAgentReleaseNotes } from './release-notes.js';
|
|
6
8
|
export function runVersion(options = {}) {
|
|
7
9
|
const cwd = options.cwd ?? process.cwd();
|
|
8
10
|
const initialized = memoryExists(cwd);
|
|
@@ -13,9 +15,27 @@ export function runVersion(options = {}) {
|
|
|
13
15
|
console.error('Error: local release publishing requires an initialized Brainclaw project.');
|
|
14
16
|
process.exit(1);
|
|
15
17
|
}
|
|
18
|
+
let parsedAgentNotes;
|
|
19
|
+
if (options.autoReleaseNotes && !options.agentReleaseNotes) {
|
|
20
|
+
parsedAgentNotes = generateAgentReleaseNotes(cwd);
|
|
21
|
+
}
|
|
22
|
+
if (options.agentReleaseNotes) {
|
|
23
|
+
try {
|
|
24
|
+
const raw = typeof options.agentReleaseNotes === 'string'
|
|
25
|
+
? JSON.parse(options.agentReleaseNotes)
|
|
26
|
+
: options.agentReleaseNotes;
|
|
27
|
+
parsedAgentNotes = AgentReleaseNotesSchema.parse(raw);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
31
|
+
console.error(`Error: --agent-release-notes is not valid JSON or does not match the expected schema: ${message}`);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
16
35
|
try {
|
|
17
36
|
publishedLocalRelease = publishLocalBrainclawRelease(cwd, {
|
|
18
37
|
releaseNotes: options.releaseNotes,
|
|
38
|
+
agentReleaseNotes: parsedAgentNotes,
|
|
19
39
|
});
|
|
20
40
|
config.brainclaw_update_source = {
|
|
21
41
|
type: 'local-pack',
|
package/dist/commands/who.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { loadAllSessions, gcStaleSessions } from '../core/identity.js';
|
|
2
2
|
import { listClaims } from '../core/claims.js';
|
|
3
|
+
import { resolveStoreChain } from '../core/store-resolution.js';
|
|
3
4
|
export function runWho(options = {}) {
|
|
4
5
|
const cwd = options.cwd ?? process.cwd();
|
|
5
6
|
if (options.gc) {
|
|
@@ -18,7 +19,29 @@ export function runWho(options = {}) {
|
|
|
18
19
|
const sessions = options.all
|
|
19
20
|
? allSessions
|
|
20
21
|
: allSessions.filter(s => (now - Date.parse(s.last_seen_at)) <= ttlMs);
|
|
21
|
-
|
|
22
|
+
let activeClaims;
|
|
23
|
+
if (options.localOnly) {
|
|
24
|
+
activeClaims = listClaims(cwd).filter(c => c.status === 'active');
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
const chain = resolveStoreChain(cwd);
|
|
28
|
+
const seenIds = new Set();
|
|
29
|
+
activeClaims = [];
|
|
30
|
+
for (const store of chain) {
|
|
31
|
+
try {
|
|
32
|
+
for (const claim of listClaims(store.cwd)) {
|
|
33
|
+
if (claim.status === 'active' && !seenIds.has(claim.id)) {
|
|
34
|
+
seenIds.add(claim.id);
|
|
35
|
+
activeClaims.push(claim);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch { /* skip unreadable stores */ }
|
|
40
|
+
}
|
|
41
|
+
if (activeClaims.length === 0 && chain.length === 0) {
|
|
42
|
+
activeClaims = listClaims(cwd).filter(c => c.status === 'active');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
22
45
|
const enriched = sessions.map(s => {
|
|
23
46
|
const age = now - Date.parse(s.last_seen_at);
|
|
24
47
|
const stale = age > ttlMs;
|
|
@@ -30,8 +53,11 @@ export function runWho(options = {}) {
|
|
|
30
53
|
agent: s.agent,
|
|
31
54
|
agent_id: s.agent_id,
|
|
32
55
|
host_id: s.host_id,
|
|
56
|
+
model: s.model ?? null,
|
|
33
57
|
project: s.active_project?.name ?? s.active_project?.path ?? null,
|
|
34
|
-
|
|
58
|
+
branch: s.branch ?? null,
|
|
59
|
+
isolation_mode: s.isolation_mode ?? 'shared-checkout',
|
|
60
|
+
claims: activeClaims.filter(c => c.session_id === s.session_id || (!c.session_id && c.agent_id === s.agent_id)).length,
|
|
35
61
|
started_at: s.started_at,
|
|
36
62
|
last_seen_at: s.last_seen_at,
|
|
37
63
|
status,
|
|
@@ -47,15 +73,17 @@ export function runWho(options = {}) {
|
|
|
47
73
|
return;
|
|
48
74
|
}
|
|
49
75
|
// Table header
|
|
50
|
-
const header = ['USER', 'AGENT', '
|
|
76
|
+
const header = ['USER', 'AGENT', 'MODEL', 'BRANCH', 'CLAIMS', 'STATUS', 'LAST SEEN'];
|
|
51
77
|
const rows = [];
|
|
52
78
|
for (const s of enriched) {
|
|
53
79
|
const age = now - Date.parse(s.last_seen_at);
|
|
54
|
-
const
|
|
80
|
+
const model = s.model ?? '-';
|
|
81
|
+
const branch = s.branch ?? '-';
|
|
55
82
|
rows.push([
|
|
56
83
|
s.user,
|
|
57
84
|
s.agent,
|
|
58
|
-
|
|
85
|
+
model.length > 20 ? model.slice(0, 17) + '...' : model,
|
|
86
|
+
branch.length > 20 ? branch.slice(0, 17) + '...' : branch,
|
|
59
87
|
String(s.claims),
|
|
60
88
|
s.status,
|
|
61
89
|
formatAge(age),
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { createWorktree, listWorktrees, removeWorktree, pruneWorktrees, cleanMergedWorktrees, mergeWorktreeBranch, worktreesBaseDir, } from '../core/worktree.js';
|
|
2
|
+
import { memoryExists } from '../core/io.js';
|
|
3
|
+
import { resolveTargetStore } from '../core/store-resolution.js';
|
|
4
|
+
export function runWorktreeCreate(options) {
|
|
5
|
+
const cwd = resolveTargetStore(options.cwd ?? process.cwd(), options.store ?? 'local');
|
|
6
|
+
if (!memoryExists(cwd)) {
|
|
7
|
+
console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
const worktreePath = createWorktree(cwd, options.branch, {
|
|
12
|
+
sessionId: options.sessionId,
|
|
13
|
+
agent: options.agent,
|
|
14
|
+
});
|
|
15
|
+
console.log(`✔ Worktree created: ${worktreePath}`);
|
|
16
|
+
console.log(` Branch: ${options.branch}`);
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function runWorktreeList(options) {
|
|
24
|
+
const cwd = resolveTargetStore(options.cwd ?? process.cwd(), options.store ?? 'local');
|
|
25
|
+
if (!memoryExists(cwd)) {
|
|
26
|
+
console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
const worktrees = listWorktrees(cwd);
|
|
30
|
+
if (worktrees.length === 0) {
|
|
31
|
+
console.log('No worktrees found.');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const managed = worktreesBaseDir(cwd);
|
|
35
|
+
console.log(`Worktrees (managed under ${managed}):\n`);
|
|
36
|
+
for (const wt of worktrees) {
|
|
37
|
+
const tag = wt.session_id ? ` [session: ${wt.session_id}]` : '';
|
|
38
|
+
const agentTag = wt.agent ? ` [agent: ${wt.agent}]` : '';
|
|
39
|
+
console.log(` ${wt.path}`);
|
|
40
|
+
console.log(` branch: ${wt.branch} commit: ${wt.commit.slice(0, 8)}${tag}${agentTag}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export function runWorktreeRemove(options) {
|
|
44
|
+
const cwd = resolveTargetStore(options.cwd ?? process.cwd(), options.store ?? 'local');
|
|
45
|
+
if (!memoryExists(cwd)) {
|
|
46
|
+
console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
removeWorktree(cwd, options.path, { force: options.force });
|
|
51
|
+
console.log(`✔ Worktree removed: ${options.path}`);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function runWorktreePrune(options) {
|
|
59
|
+
const cwd = resolveTargetStore(options.cwd ?? process.cwd(), options.store ?? 'local');
|
|
60
|
+
pruneWorktrees(cwd);
|
|
61
|
+
console.log('✔ Worktree stale entries pruned.');
|
|
62
|
+
}
|
|
63
|
+
export function runWorktreeClean(options) {
|
|
64
|
+
const cwd = resolveTargetStore(options.cwd ?? process.cwd(), options.store ?? 'local');
|
|
65
|
+
const result = cleanMergedWorktrees(cwd, {
|
|
66
|
+
force: options.force,
|
|
67
|
+
dryRun: options.dryRun,
|
|
68
|
+
});
|
|
69
|
+
if (result.removed.length === 0 && result.skipped.length === 0) {
|
|
70
|
+
console.log('✔ No merged or orphan worktrees to clean.');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const verb = options.dryRun ? 'Would remove' : 'Removed';
|
|
74
|
+
for (const p of result.removed) {
|
|
75
|
+
console.log(`${options.dryRun ? ' (dry-run)' : ' ✔'} ${verb}: ${p}`);
|
|
76
|
+
}
|
|
77
|
+
for (const s of result.skipped) {
|
|
78
|
+
console.log(` ⚠ Skipped: ${s.path} (${s.reason})`);
|
|
79
|
+
}
|
|
80
|
+
console.log(`\n${verb} ${result.removed.length} worktree(s), skipped ${result.skipped.length}.`);
|
|
81
|
+
}
|
|
82
|
+
export function runWorktreeMerge(options) {
|
|
83
|
+
const cwd = resolveTargetStore(options.cwd ?? process.cwd(), options.store ?? 'local');
|
|
84
|
+
const result = mergeWorktreeBranch(cwd, options.branch, {
|
|
85
|
+
message: options.message,
|
|
86
|
+
dryRun: options.dryRun,
|
|
87
|
+
});
|
|
88
|
+
if (!result.merged) {
|
|
89
|
+
if (options.dryRun) {
|
|
90
|
+
console.log(`(dry-run) Would merge ${options.branch}: ${result.filesChanged} files changed, ${result.filesRestored} parasitic deletions restored.`);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
console.error(`Error: ${result.error}`);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
console.log(`✔ Merged ${options.branch} → ${result.commitHash}`);
|
|
99
|
+
console.log(` ${result.filesChanged} files changed, ${result.filesRestored} parasitic deletion(s) auto-restored.`);
|
|
100
|
+
}
|
|
101
|
+
/** Returns WorktreeInfo[] for use by MCP tools. */
|
|
102
|
+
export function getWorktrees(cwd) {
|
|
103
|
+
return listWorktrees(cwd);
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=worktree.js.map
|