macro-agent 0.1.8 → 0.1.10
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/CLAUDE.md +166 -33
- package/README.md +781 -131
- package/dist/acp/claude-code-replay.d.ts +11 -0
- package/dist/acp/claude-code-replay.d.ts.map +1 -0
- package/dist/acp/claude-code-replay.js +190 -0
- package/dist/acp/claude-code-replay.js.map +1 -0
- package/dist/acp/macro-agent.d.ts.map +1 -1
- package/dist/acp/macro-agent.js +155 -6
- package/dist/acp/macro-agent.js.map +1 -1
- package/dist/acp/types.d.ts +9 -0
- package/dist/acp/types.d.ts.map +1 -1
- package/dist/acp/types.js.map +1 -1
- package/dist/agent/agent-manager-v2.d.ts +21 -0
- package/dist/agent/agent-manager-v2.d.ts.map +1 -1
- package/dist/agent/agent-manager-v2.js +234 -43
- package/dist/agent/agent-manager-v2.js.map +1 -1
- package/dist/agent/agent-manager.d.ts +12 -0
- package/dist/agent/agent-manager.d.ts.map +1 -1
- package/dist/agent/agent-manager.js.map +1 -1
- package/dist/agent/types.d.ts +15 -2
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/boot-v2.d.ts +41 -0
- package/dist/boot-v2.d.ts.map +1 -1
- package/dist/boot-v2.js +16 -1
- package/dist/boot-v2.js.map +1 -1
- package/dist/cli/index.js +56 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cognitive/macro-agent-backend.d.ts.map +1 -1
- package/dist/cognitive/macro-agent-backend.js +40 -22
- package/dist/cognitive/macro-agent-backend.js.map +1 -1
- package/dist/integrations/skilltree.d.ts.map +1 -1
- package/dist/integrations/skilltree.js +1 -0
- package/dist/integrations/skilltree.js.map +1 -1
- package/dist/lifecycle/cleanup.d.ts +33 -2
- package/dist/lifecycle/cleanup.d.ts.map +1 -1
- package/dist/lifecycle/cleanup.js +28 -6
- package/dist/lifecycle/cleanup.js.map +1 -1
- package/dist/lifecycle/handlers-v2.d.ts +7 -0
- package/dist/lifecycle/handlers-v2.d.ts.map +1 -1
- package/dist/lifecycle/handlers-v2.js +28 -2
- package/dist/lifecycle/handlers-v2.js.map +1 -1
- package/dist/lifecycle/types.d.ts +11 -0
- package/dist/lifecycle/types.d.ts.map +1 -1
- package/dist/lifecycle/types.js.map +1 -1
- package/dist/map/acp-bridge.d.ts +9 -0
- package/dist/map/acp-bridge.d.ts.map +1 -1
- package/dist/map/acp-bridge.js +15 -2
- package/dist/map/acp-bridge.js.map +1 -1
- package/dist/map/cascade-bridge.d.ts +44 -0
- package/dist/map/cascade-bridge.d.ts.map +1 -0
- package/dist/map/cascade-bridge.js +257 -0
- package/dist/map/cascade-bridge.js.map +1 -0
- package/dist/map/lifecycle-bridge.d.ts +1 -1
- package/dist/map/lifecycle-bridge.d.ts.map +1 -1
- package/dist/map/lifecycle-bridge.js +58 -23
- package/dist/map/lifecycle-bridge.js.map +1 -1
- package/dist/map/server.d.ts.map +1 -1
- package/dist/map/server.js +47 -6
- package/dist/map/server.js.map +1 -1
- package/dist/map/sidecar.d.ts.map +1 -1
- package/dist/map/sidecar.js +33 -2
- package/dist/map/sidecar.js.map +1 -1
- package/dist/map/types.d.ts +20 -0
- package/dist/map/types.d.ts.map +1 -1
- package/dist/mcp/tools/done-v2.d.ts.map +1 -1
- package/dist/mcp/tools/done-v2.js +8 -0
- package/dist/mcp/tools/done-v2.js.map +1 -1
- package/dist/teams/team-manager-v2.d.ts.map +1 -1
- package/dist/teams/team-manager-v2.js +26 -0
- package/dist/teams/team-manager-v2.js.map +1 -1
- package/dist/teams/team-runtime-v2.d.ts.map +1 -1
- package/dist/teams/team-runtime-v2.js +16 -3
- package/dist/teams/team-runtime-v2.js.map +1 -1
- package/dist/workspace/config.d.ts +10 -10
- package/dist/workspace/config.d.ts.map +1 -1
- package/dist/workspace/config.js +4 -4
- package/dist/workspace/config.js.map +1 -1
- package/dist/workspace/git-cascade-adapter.d.ts +510 -0
- package/dist/workspace/git-cascade-adapter.d.ts.map +1 -0
- package/dist/workspace/git-cascade-adapter.js +908 -0
- package/dist/workspace/git-cascade-adapter.js.map +1 -0
- package/dist/workspace/index.d.ts +3 -3
- package/dist/workspace/index.d.ts.map +1 -1
- package/dist/workspace/index.js +4 -4
- package/dist/workspace/index.js.map +1 -1
- package/dist/workspace/landing/direct-push.d.ts +20 -0
- package/dist/workspace/landing/direct-push.d.ts.map +1 -0
- package/dist/workspace/landing/direct-push.js +74 -0
- package/dist/workspace/landing/direct-push.js.map +1 -0
- package/dist/workspace/landing/index.d.ts +29 -0
- package/dist/workspace/landing/index.d.ts.map +1 -0
- package/dist/workspace/landing/index.js +37 -0
- package/dist/workspace/landing/index.js.map +1 -0
- package/dist/workspace/landing/merge-to-parent.d.ts +41 -0
- package/dist/workspace/landing/merge-to-parent.d.ts.map +1 -0
- package/dist/workspace/landing/merge-to-parent.js +185 -0
- package/dist/workspace/landing/merge-to-parent.js.map +1 -0
- package/dist/workspace/landing/optimistic-push.d.ts +16 -0
- package/dist/workspace/landing/optimistic-push.d.ts.map +1 -0
- package/dist/workspace/landing/optimistic-push.js +27 -0
- package/dist/workspace/landing/optimistic-push.js.map +1 -0
- package/dist/workspace/landing/queue-to-branch.d.ts +24 -0
- package/dist/workspace/landing/queue-to-branch.d.ts.map +1 -0
- package/dist/workspace/landing/queue-to-branch.js +79 -0
- package/dist/workspace/landing/queue-to-branch.js.map +1 -0
- package/dist/workspace/merge-queue/merge-queue.d.ts +10 -0
- package/dist/workspace/merge-queue/merge-queue.d.ts.map +1 -1
- package/dist/workspace/merge-queue/merge-queue.js +10 -0
- package/dist/workspace/merge-queue/merge-queue.js.map +1 -1
- package/dist/workspace/merge-queue/types.d.ts +16 -2
- package/dist/workspace/merge-queue/types.d.ts.map +1 -1
- package/dist/workspace/merge-queue/types.js +9 -0
- package/dist/workspace/merge-queue/types.js.map +1 -1
- package/dist/workspace/pool/types.d.ts +1 -0
- package/dist/workspace/pool/types.d.ts.map +1 -1
- package/dist/workspace/pool/worktree-pool.d.ts.map +1 -1
- package/dist/workspace/pool/worktree-pool.js +1 -0
- package/dist/workspace/pool/worktree-pool.js.map +1 -1
- package/dist/workspace/recovery/abandon.d.ts +15 -0
- package/dist/workspace/recovery/abandon.d.ts.map +1 -0
- package/dist/workspace/recovery/abandon.js +45 -0
- package/dist/workspace/recovery/abandon.js.map +1 -0
- package/dist/workspace/recovery/auto-resolve.d.ts +27 -0
- package/dist/workspace/recovery/auto-resolve.d.ts.map +1 -0
- package/dist/workspace/recovery/auto-resolve.js +99 -0
- package/dist/workspace/recovery/auto-resolve.js.map +1 -0
- package/dist/workspace/recovery/defer.d.ts +15 -0
- package/dist/workspace/recovery/defer.d.ts.map +1 -0
- package/dist/workspace/recovery/defer.js +16 -0
- package/dist/workspace/recovery/defer.js.map +1 -0
- package/dist/workspace/recovery/escalate.d.ts +16 -0
- package/dist/workspace/recovery/escalate.d.ts.map +1 -0
- package/dist/workspace/recovery/escalate.js +24 -0
- package/dist/workspace/recovery/escalate.js.map +1 -0
- package/dist/workspace/recovery/index.d.ts +32 -0
- package/dist/workspace/recovery/index.d.ts.map +1 -0
- package/dist/workspace/recovery/index.js +45 -0
- package/dist/workspace/recovery/index.js.map +1 -0
- package/dist/workspace/recovery/spawn-resolver.d.ts +45 -0
- package/dist/workspace/recovery/spawn-resolver.d.ts.map +1 -0
- package/dist/workspace/recovery/spawn-resolver.js +111 -0
- package/dist/workspace/recovery/spawn-resolver.js.map +1 -0
- package/dist/workspace/recovery/types.d.ts +63 -0
- package/dist/workspace/recovery/types.d.ts.map +1 -0
- package/dist/workspace/recovery/types.js +12 -0
- package/dist/workspace/recovery/types.js.map +1 -0
- package/dist/workspace/topology/index.d.ts +9 -0
- package/dist/workspace/topology/index.d.ts.map +1 -0
- package/dist/workspace/topology/index.js +8 -0
- package/dist/workspace/topology/index.js.map +1 -0
- package/dist/workspace/topology/no-workspace.d.ts +18 -0
- package/dist/workspace/topology/no-workspace.d.ts.map +1 -0
- package/dist/workspace/topology/no-workspace.js +25 -0
- package/dist/workspace/topology/no-workspace.js.map +1 -0
- package/dist/workspace/topology/types.d.ts +97 -0
- package/dist/workspace/topology/types.d.ts.map +1 -0
- package/dist/workspace/topology/types.js +20 -0
- package/dist/workspace/topology/types.js.map +1 -0
- package/dist/workspace/topology/yaml-driven.d.ts +69 -0
- package/dist/workspace/topology/yaml-driven.d.ts.map +1 -0
- package/dist/workspace/topology/yaml-driven.js +273 -0
- package/dist/workspace/topology/yaml-driven.js.map +1 -0
- package/dist/workspace/types-v3.d.ts +110 -0
- package/dist/workspace/types-v3.d.ts.map +1 -0
- package/dist/workspace/types-v3.js +20 -0
- package/dist/workspace/types-v3.js.map +1 -0
- package/dist/workspace/types.d.ts +145 -17
- package/dist/workspace/types.d.ts.map +1 -1
- package/dist/workspace/workspace-manager.d.ts +92 -13
- package/dist/workspace/workspace-manager.d.ts.map +1 -1
- package/dist/workspace/workspace-manager.js +373 -13
- package/dist/workspace/workspace-manager.js.map +1 -1
- package/dist/workspace/yaml-schema.d.ts +254 -0
- package/dist/workspace/yaml-schema.d.ts.map +1 -0
- package/dist/workspace/yaml-schema.js +170 -0
- package/dist/workspace/yaml-schema.js.map +1 -0
- package/docs/conflict-recovery.md +472 -0
- package/docs/git-cascade-integration-gaps.md +678 -0
- package/docs/workspace-interfaces.md +731 -0
- package/docs/workspace-redesign-plan.md +302 -0
- package/package.json +4 -4
- package/src/__tests__/e2e/auto-sync.e2e.test.ts +257 -0
- package/src/__tests__/e2e/cascade-rebase.e2e.test.ts +254 -0
- package/src/__tests__/e2e/cli-run.e2e.test.ts +167 -0
- package/src/__tests__/e2e/self-driving-v3.e2e.test.ts +197 -0
- package/src/__tests__/e2e/spawn-resolver.e2e.test.ts +200 -0
- package/src/__tests__/e2e/workspace-lifecycle.e2e.test.ts +30 -22
- package/src/__tests__/e2e/workspace-v3.e2e.test.ts +413 -0
- package/src/acp/__tests__/claude-code-replay.test.ts +225 -0
- package/src/acp/__tests__/macro-agent.test.ts +39 -1
- package/src/acp/claude-code-replay.ts +208 -0
- package/src/acp/macro-agent.ts +167 -9
- package/src/acp/types.ts +10 -0
- package/src/agent/__tests__/agent-manager-topology.test.ts +73 -0
- package/src/agent/__tests__/agent-manager-v2.test.ts +66 -0
- package/src/agent/__tests__/task-ref-resolution.test.ts +231 -0
- package/src/agent/agent-manager-v2.ts +293 -48
- package/src/agent/agent-manager.ts +14 -0
- package/src/agent/types.ts +16 -2
- package/src/boot-v2.ts +68 -1
- package/src/cli/index.ts +61 -0
- package/src/cognitive/macro-agent-backend.ts +45 -29
- package/src/integrations/skilltree.ts +1 -0
- package/src/lifecycle/cleanup.ts +52 -3
- package/src/lifecycle/handlers-v2.ts +40 -3
- package/src/lifecycle/types.ts +12 -0
- package/src/map/__tests__/cascade-bridge.test.ts +229 -0
- package/src/map/__tests__/lifecycle-bridge.test.ts +86 -10
- package/src/map/acp-bridge.ts +26 -3
- package/src/map/cascade-bridge.ts +301 -0
- package/src/map/lifecycle-bridge.ts +52 -17
- package/src/map/server.ts +47 -6
- package/src/map/sidecar.ts +31 -1
- package/src/map/types.ts +20 -0
- package/src/mcp/tools/done-v2.ts +9 -0
- package/src/teams/team-manager-v2.ts +37 -0
- package/src/teams/team-runtime-v2.ts +23 -3
- package/src/workspace/__tests__/{dataplane-adapter.test.ts → git-cascade-adapter.test.ts} +209 -14
- package/src/workspace/__tests__/self-driving-yaml.test.ts +114 -0
- package/src/workspace/__tests__/shared-worktree-refcount.test.ts +154 -0
- package/src/workspace/__tests__/standalone-mode.test.ts +118 -0
- package/src/workspace/__tests__/workspace-manager-v3.test.ts +245 -0
- package/src/workspace/__tests__/yaml-schema.test.ts +210 -0
- package/src/workspace/config.ts +11 -11
- package/src/workspace/git-cascade-adapter.ts +1186 -0
- package/src/workspace/index.ts +11 -11
- package/src/workspace/landing/__tests__/strategies.test.ts +142 -0
- package/src/workspace/landing/direct-push.ts +91 -0
- package/src/workspace/landing/index.ts +40 -0
- package/src/workspace/landing/merge-to-parent.ts +228 -0
- package/src/workspace/landing/optimistic-push.ts +36 -0
- package/src/workspace/landing/queue-to-branch.ts +108 -0
- package/src/workspace/merge-queue/merge-queue.ts +10 -0
- package/src/workspace/merge-queue/types.ts +16 -2
- package/src/workspace/pool/__tests__/worktree-pool.integration.test.ts +5 -5
- package/src/workspace/pool/types.ts +1 -0
- package/src/workspace/pool/worktree-pool.ts +1 -0
- package/src/workspace/recovery/__tests__/auto-resolve-integration.test.ts +127 -0
- package/src/workspace/recovery/__tests__/spawn-resolver.test.ts +139 -0
- package/src/workspace/recovery/__tests__/strategies.test.ts +145 -0
- package/src/workspace/recovery/abandon.ts +51 -0
- package/src/workspace/recovery/auto-resolve.ts +119 -0
- package/src/workspace/recovery/defer.ts +23 -0
- package/src/workspace/recovery/escalate.ts +30 -0
- package/src/workspace/recovery/index.ts +58 -0
- package/src/workspace/recovery/spawn-resolver.ts +145 -0
- package/src/workspace/recovery/types.ts +54 -0
- package/src/workspace/topology/__tests__/yaml-driven.test.ts +345 -0
- package/src/workspace/topology/index.ts +18 -0
- package/src/workspace/topology/no-workspace.ts +39 -0
- package/src/workspace/topology/types.ts +116 -0
- package/src/workspace/topology/yaml-driven.ts +316 -0
- package/src/workspace/types-v3.ts +155 -0
- package/src/workspace/types.ts +191 -20
- package/src/workspace/workspace-manager.ts +474 -19
- package/src/workspace/yaml-schema.ts +216 -0
- package/src/workspace/dataplane-adapter.ts +0 -546
|
@@ -0,0 +1,1186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* git-cascade Adapter
|
|
3
|
+
*
|
|
4
|
+
* Wraps git-cascade's MultiAgentRepoTracker to integrate with macro-agent's
|
|
5
|
+
* event system and provide a simplified interface for workspace management.
|
|
6
|
+
*
|
|
7
|
+
* @module workspace/git-cascade-adapter
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import Database from 'better-sqlite3';
|
|
11
|
+
import {
|
|
12
|
+
MultiAgentRepoTracker,
|
|
13
|
+
type TrackerOptions,
|
|
14
|
+
type Stream,
|
|
15
|
+
type StreamStatus,
|
|
16
|
+
type StreamNode,
|
|
17
|
+
type CreateStreamOptions,
|
|
18
|
+
type ForkStreamOptions,
|
|
19
|
+
type MergeStreamOptions,
|
|
20
|
+
type MergeResult,
|
|
21
|
+
type RebaseOntoStreamOptions,
|
|
22
|
+
type RebaseResult,
|
|
23
|
+
type ConflictStrategy,
|
|
24
|
+
type ConflictRecord,
|
|
25
|
+
type CreateConflictOptions,
|
|
26
|
+
type AgentWorktree,
|
|
27
|
+
type CreateWorktreeOptions,
|
|
28
|
+
type WorkerTask,
|
|
29
|
+
type CreateTaskOptions,
|
|
30
|
+
type StartTaskOptions,
|
|
31
|
+
type CompleteTaskOptions,
|
|
32
|
+
type StartTaskResult,
|
|
33
|
+
type CompleteTaskResult,
|
|
34
|
+
type ListTasksOptions,
|
|
35
|
+
type CleanupWorkerBranchesOptions,
|
|
36
|
+
type CleanupResult,
|
|
37
|
+
type Checkpoint,
|
|
38
|
+
type Change,
|
|
39
|
+
type ChangeStatus,
|
|
40
|
+
workerTasks,
|
|
41
|
+
diffStacks,
|
|
42
|
+
mergeQueue as mergeQueueModule,
|
|
43
|
+
reconcile as reconcileModule,
|
|
44
|
+
cascade as cascadeModule,
|
|
45
|
+
matchCascadeSuffix,
|
|
46
|
+
type StreamOpenedParams,
|
|
47
|
+
type StreamCommittedParams,
|
|
48
|
+
type StreamMergedParams,
|
|
49
|
+
type StreamConflictedParams,
|
|
50
|
+
type StreamAbandonedParams,
|
|
51
|
+
type CascadeRebasedParams,
|
|
52
|
+
type CascadeCompletedParams,
|
|
53
|
+
type QueueAddedParams,
|
|
54
|
+
type QueueReadyParams,
|
|
55
|
+
type QueueCancelledParams,
|
|
56
|
+
type QueueRemovedParams,
|
|
57
|
+
} from 'git-cascade';
|
|
58
|
+
// git-cascade 0.0.3+ exposes both events (via `emit` callback) and the
|
|
59
|
+
// `cascade` namespace for cascadeRebase. All v3 primitives are now reachable.
|
|
60
|
+
import type { GitCascadeConfig } from './config.js';
|
|
61
|
+
import { DEFAULT_GIT_CASCADE_CONFIG } from './config.js';
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Event types emitted by GitCascadeAdapter.
|
|
65
|
+
*
|
|
66
|
+
* Grouped by source:
|
|
67
|
+
* - `stream:*` — from git-cascade `x-cascade/stream.*` events + local emits
|
|
68
|
+
* - `worktree:*`, `task:*` — local emits (macro-agent-only concepts)
|
|
69
|
+
* - `change:*` — change-id lifecycle from git-cascade
|
|
70
|
+
* - `cascade:*` — cascadeRebase completion
|
|
71
|
+
* - `conflict:*` — conflict lifecycle
|
|
72
|
+
* - `mergeQueue:*` — built-in merge queue lifecycle
|
|
73
|
+
*/
|
|
74
|
+
export type GitCascadeEventType =
|
|
75
|
+
| 'stream:created' // mapped from git-cascade stream.opened
|
|
76
|
+
| 'stream:updated' // local (updateStream)
|
|
77
|
+
| 'stream:forked' // local (forkStream)
|
|
78
|
+
| 'stream:committed' // mapped from git-cascade stream.committed
|
|
79
|
+
| 'stream:merged' // mapped from git-cascade stream.merged
|
|
80
|
+
| 'stream:conflicted' // mapped from git-cascade stream.conflicted
|
|
81
|
+
| 'stream:abandoned' // mapped from git-cascade stream.abandoned
|
|
82
|
+
| 'stream:paused' // local (pauseStream)
|
|
83
|
+
| 'stream:resumed' // local (resumeStream)
|
|
84
|
+
| 'worktree:created'
|
|
85
|
+
| 'worktree:deallocated'
|
|
86
|
+
| 'task:created'
|
|
87
|
+
| 'task:started'
|
|
88
|
+
| 'task:completed'
|
|
89
|
+
| 'task:abandoned'
|
|
90
|
+
| 'change:merged'
|
|
91
|
+
| 'change:dropped'
|
|
92
|
+
| 'stream:pushed' // local — emitted by direct-push/optimistic-push landing strategies
|
|
93
|
+
| 'cascade:rebased' // mapped from git-cascade cascade.rebased
|
|
94
|
+
| 'cascade:completed' // mapped from git-cascade cascade.completed
|
|
95
|
+
| 'conflict:created'
|
|
96
|
+
| 'conflict:resolved'
|
|
97
|
+
| 'mergeQueue:added'
|
|
98
|
+
| 'mergeQueue:ready'
|
|
99
|
+
| 'mergeQueue:cancelled'
|
|
100
|
+
| 'mergeQueue:removed';
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Event payload for git-cascade events
|
|
104
|
+
*/
|
|
105
|
+
export interface GitCascadeEvent {
|
|
106
|
+
type: GitCascadeEventType;
|
|
107
|
+
timestamp: number;
|
|
108
|
+
data: Record<string, unknown>;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Callback for git-cascade events
|
|
113
|
+
*/
|
|
114
|
+
export type GitCascadeEventCallback = (event: GitCascadeEvent) => void;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* GitCascadeAdapter wraps MultiAgentRepoTracker for macro-agent integration.
|
|
118
|
+
*
|
|
119
|
+
* Key responsibilities:
|
|
120
|
+
* - Initialize tracker with shared or dedicated database
|
|
121
|
+
* - Emit events on tracker operations
|
|
122
|
+
* - Provide simplified API for workspace management
|
|
123
|
+
*/
|
|
124
|
+
export class GitCascadeAdapter {
|
|
125
|
+
private readonly tracker: MultiAgentRepoTracker;
|
|
126
|
+
private readonly config: Required<
|
|
127
|
+
Pick<GitCascadeConfig, 'enabled' | 'tablePrefix' | 'verbose' | 'skipRecovery'>
|
|
128
|
+
> & { repoPath: string };
|
|
129
|
+
private readonly eventListeners: Set<GitCascadeEventCallback> = new Set();
|
|
130
|
+
private readonly ownsDb: boolean;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Create a new GitCascadeAdapter.
|
|
134
|
+
*
|
|
135
|
+
* @param config - git-cascade configuration
|
|
136
|
+
*/
|
|
137
|
+
constructor(config: GitCascadeConfig) {
|
|
138
|
+
const mergedConfig = {
|
|
139
|
+
...DEFAULT_GIT_CASCADE_CONFIG,
|
|
140
|
+
...config,
|
|
141
|
+
repoPath: config.repoPath ?? process.cwd(),
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
this.config = {
|
|
145
|
+
enabled: mergedConfig.enabled ?? true,
|
|
146
|
+
repoPath: mergedConfig.repoPath,
|
|
147
|
+
tablePrefix: mergedConfig.tablePrefix ?? 'git_cascade_',
|
|
148
|
+
verbose: mergedConfig.verbose ?? false,
|
|
149
|
+
skipRecovery: mergedConfig.skipRecovery ?? false,
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// Determine if we own the database connection
|
|
153
|
+
this.ownsDb = !config.db;
|
|
154
|
+
|
|
155
|
+
const trackerOptions: TrackerOptions = {
|
|
156
|
+
repoPath: this.config.repoPath,
|
|
157
|
+
tablePrefix: this.config.tablePrefix,
|
|
158
|
+
verbose: this.config.verbose,
|
|
159
|
+
skipRecovery: this.config.skipRecovery,
|
|
160
|
+
// Wire git-cascade's native event emitter (0.0.2+) so stream lifecycle
|
|
161
|
+
// events are re-published through our own onEvent channel.
|
|
162
|
+
emit: (method: string, params: unknown) => this.forwardCascadeEvent(method, params),
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
if (config.db) {
|
|
166
|
+
trackerOptions.db = config.db;
|
|
167
|
+
} else if (config.dbPath) {
|
|
168
|
+
trackerOptions.dbPath = config.dbPath;
|
|
169
|
+
}
|
|
170
|
+
// If neither db nor dbPath provided, tracker uses default path
|
|
171
|
+
|
|
172
|
+
this.tracker = new MultiAgentRepoTracker(trackerOptions);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Get whether the adapter is enabled.
|
|
178
|
+
*/
|
|
179
|
+
get enabled(): boolean {
|
|
180
|
+
return this.config.enabled;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Get the repository path.
|
|
185
|
+
*/
|
|
186
|
+
get repoPath(): string {
|
|
187
|
+
return this.config.repoPath;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Get the underlying database connection.
|
|
192
|
+
* Use with caution - prefer adapter methods for operations.
|
|
193
|
+
*/
|
|
194
|
+
get db(): Database.Database {
|
|
195
|
+
return this.tracker.db;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Get the underlying tracker.
|
|
200
|
+
* Use with caution - prefer adapter methods for operations.
|
|
201
|
+
*/
|
|
202
|
+
get rawTracker(): MultiAgentRepoTracker {
|
|
203
|
+
return this.tracker;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
207
|
+
// Event System
|
|
208
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Subscribe to git-cascade events.
|
|
212
|
+
*
|
|
213
|
+
* @param callback - Function called when events occur
|
|
214
|
+
* @returns Unsubscribe function
|
|
215
|
+
*/
|
|
216
|
+
onEvent(callback: GitCascadeEventCallback): () => void {
|
|
217
|
+
this.eventListeners.add(callback);
|
|
218
|
+
return () => this.eventListeners.delete(callback);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Emit an event to all listeners.
|
|
223
|
+
*/
|
|
224
|
+
private emit(type: GitCascadeEventType, data: Record<string, unknown>): void {
|
|
225
|
+
const event: GitCascadeEvent = {
|
|
226
|
+
type,
|
|
227
|
+
timestamp: Date.now(),
|
|
228
|
+
data,
|
|
229
|
+
};
|
|
230
|
+
for (const listener of this.eventListeners) {
|
|
231
|
+
try {
|
|
232
|
+
listener(event);
|
|
233
|
+
} catch (error) {
|
|
234
|
+
console.error('[GitCascadeAdapter] Event listener error:', error);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Forward events emitted by git-cascade (`x-cascade/stream.*`) into our
|
|
241
|
+
* structured `GitCascadeEvent` stream. Called via the `emit` callback wired
|
|
242
|
+
* into the tracker constructor.
|
|
243
|
+
*
|
|
244
|
+
* Note: `stream.opened` is mapped to `stream:created`. The local
|
|
245
|
+
* wrapper methods still emit their own events so consumers don't miss out
|
|
246
|
+
* on operations that don't round-trip through git-cascade's emit (e.g.,
|
|
247
|
+
* updateStream, pauseStream, mergeQueue events).
|
|
248
|
+
*/
|
|
249
|
+
private forwardCascadeEvent(method: string, params: unknown): void {
|
|
250
|
+
const suffix = matchCascadeSuffix(method);
|
|
251
|
+
if (!suffix) return;
|
|
252
|
+
|
|
253
|
+
switch (suffix) {
|
|
254
|
+
case 'stream.opened': {
|
|
255
|
+
const p = params as StreamOpenedParams;
|
|
256
|
+
this.emit('stream:created', {
|
|
257
|
+
streamId: p.stream_id,
|
|
258
|
+
name: p.name,
|
|
259
|
+
agentId: p.agent_id,
|
|
260
|
+
baseCommit: p.base_commit,
|
|
261
|
+
parentStream: p.parent_stream,
|
|
262
|
+
branchName: p.branch_name,
|
|
263
|
+
metadata: p.metadata,
|
|
264
|
+
});
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
case 'stream.committed': {
|
|
268
|
+
const p = params as StreamCommittedParams;
|
|
269
|
+
this.emit('stream:committed', {
|
|
270
|
+
streamId: p.stream_id,
|
|
271
|
+
commit: p.commit_hash,
|
|
272
|
+
changeId: p.change_id,
|
|
273
|
+
agentId: p.agent_id,
|
|
274
|
+
messageSummary: p.message_summary,
|
|
275
|
+
filesTouched: p.files_touched,
|
|
276
|
+
parentCommit: p.parent_commit,
|
|
277
|
+
metadata: p.metadata,
|
|
278
|
+
});
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
case 'stream.merged': {
|
|
282
|
+
const p = params as StreamMergedParams;
|
|
283
|
+
this.emit('stream:merged', {
|
|
284
|
+
sourceStreamId: p.source_stream_id,
|
|
285
|
+
targetStreamId: p.target_stream_id,
|
|
286
|
+
mergeCommit: p.merge_commit,
|
|
287
|
+
agentId: p.agent_id,
|
|
288
|
+
strategy: p.strategy,
|
|
289
|
+
sourceCommit: p.source_commit,
|
|
290
|
+
metadata: p.metadata,
|
|
291
|
+
});
|
|
292
|
+
break;
|
|
293
|
+
}
|
|
294
|
+
case 'stream.conflicted': {
|
|
295
|
+
const p = params as StreamConflictedParams;
|
|
296
|
+
this.emit('stream:conflicted', {
|
|
297
|
+
streamId: p.stream_id,
|
|
298
|
+
conflictId: p.conflict_id,
|
|
299
|
+
conflictedFiles: p.conflicted_files,
|
|
300
|
+
agentId: p.agent_id,
|
|
301
|
+
conflictingCommit: p.conflicting_commit,
|
|
302
|
+
targetCommit: p.target_commit,
|
|
303
|
+
source: p.source,
|
|
304
|
+
metadata: p.metadata,
|
|
305
|
+
});
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
case 'stream.conflict_resolved': {
|
|
309
|
+
const p = params as import('git-cascade').StreamConflictResolvedParams;
|
|
310
|
+
this.emit('conflict:resolved', {
|
|
311
|
+
streamId: p.stream_id,
|
|
312
|
+
conflictId: p.conflict_id,
|
|
313
|
+
resolutionMethod: p.resolution_method,
|
|
314
|
+
resolvedBy: p.resolved_by,
|
|
315
|
+
resolutionSummary: p.resolution_summary,
|
|
316
|
+
metadata: p.metadata,
|
|
317
|
+
});
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
320
|
+
case 'stream.abandoned': {
|
|
321
|
+
const p = params as StreamAbandonedParams;
|
|
322
|
+
this.emit('stream:abandoned', {
|
|
323
|
+
streamId: p.stream_id,
|
|
324
|
+
reason: p.reason,
|
|
325
|
+
cascade: p.cascade,
|
|
326
|
+
metadata: p.metadata,
|
|
327
|
+
});
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
case 'cascade.rebased': {
|
|
331
|
+
const p = params as CascadeRebasedParams;
|
|
332
|
+
this.emit('cascade:rebased', {
|
|
333
|
+
streamId: p.stream_id,
|
|
334
|
+
agentId: p.agent_id,
|
|
335
|
+
triggeredByStreamId: p.triggered_by_stream_id,
|
|
336
|
+
triggeredByAgentId: p.triggered_by_agent_id,
|
|
337
|
+
newBaseCommit: p.new_base_commit,
|
|
338
|
+
newHead: p.new_head,
|
|
339
|
+
newCommits: p.new_commits,
|
|
340
|
+
metadata: p.metadata,
|
|
341
|
+
});
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
344
|
+
case 'cascade.completed': {
|
|
345
|
+
const p = params as CascadeCompletedParams;
|
|
346
|
+
this.emit('cascade:completed', {
|
|
347
|
+
rootStreamId: p.root_stream_id,
|
|
348
|
+
agentId: p.agent_id,
|
|
349
|
+
strategy: p.strategy,
|
|
350
|
+
updatedStreams: p.updated_streams,
|
|
351
|
+
failedStreams: p.failed_streams,
|
|
352
|
+
skippedStreams: p.skipped_streams,
|
|
353
|
+
deferredStreams: p.deferred_streams,
|
|
354
|
+
metadata: p.metadata,
|
|
355
|
+
});
|
|
356
|
+
break;
|
|
357
|
+
}
|
|
358
|
+
case 'queue.added': {
|
|
359
|
+
const p = params as QueueAddedParams;
|
|
360
|
+
this.emit('mergeQueue:added', {
|
|
361
|
+
entryId: p.entry_id,
|
|
362
|
+
streamId: p.stream_id,
|
|
363
|
+
targetBranch: p.target_branch,
|
|
364
|
+
metadata: p.metadata,
|
|
365
|
+
});
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
case 'queue.ready': {
|
|
369
|
+
const p = params as QueueReadyParams;
|
|
370
|
+
this.emit('mergeQueue:ready', {
|
|
371
|
+
entryId: p.entry_id,
|
|
372
|
+
streamId: p.stream_id,
|
|
373
|
+
targetBranch: p.target_branch,
|
|
374
|
+
});
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
377
|
+
case 'queue.cancelled': {
|
|
378
|
+
const p = params as QueueCancelledParams;
|
|
379
|
+
this.emit('mergeQueue:cancelled', {
|
|
380
|
+
entryId: p.entry_id,
|
|
381
|
+
streamId: p.stream_id,
|
|
382
|
+
targetBranch: p.target_branch,
|
|
383
|
+
reason: p.reason,
|
|
384
|
+
});
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
case 'queue.removed': {
|
|
388
|
+
const p = params as QueueRemovedParams;
|
|
389
|
+
this.emit('mergeQueue:removed', {
|
|
390
|
+
entryId: p.entry_id,
|
|
391
|
+
streamId: p.stream_id,
|
|
392
|
+
targetBranch: p.target_branch,
|
|
393
|
+
outcome: p.outcome,
|
|
394
|
+
});
|
|
395
|
+
break;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
401
|
+
// Stream Operations
|
|
402
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Create a new stream (integration branch).
|
|
406
|
+
*
|
|
407
|
+
* Note: `stream:created` is emitted by the cascade event forwarder via
|
|
408
|
+
* git-cascade's `x-cascade/stream.opened`. We don't double-emit here.
|
|
409
|
+
*
|
|
410
|
+
* @param options - Stream creation options
|
|
411
|
+
* @returns Stream ID
|
|
412
|
+
*/
|
|
413
|
+
createStream(options: CreateStreamOptions): string {
|
|
414
|
+
return this.tracker.createStream(options);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Get a stream by ID.
|
|
419
|
+
*/
|
|
420
|
+
getStream(streamId: string): Stream | null {
|
|
421
|
+
return this.tracker.getStream(streamId);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* List streams with optional filters.
|
|
426
|
+
*/
|
|
427
|
+
listStreams(options?: { agentId?: string; status?: StreamStatus }): Stream[] {
|
|
428
|
+
return this.tracker.listStreams(options);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Update a stream.
|
|
433
|
+
*/
|
|
434
|
+
updateStream(
|
|
435
|
+
streamId: string,
|
|
436
|
+
updates: Partial<Pick<Stream, 'name' | 'status' | 'metadata'>>
|
|
437
|
+
): void {
|
|
438
|
+
this.tracker.updateStream(streamId, updates);
|
|
439
|
+
this.emit('stream:updated', { streamId, updates });
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Abandon a stream.
|
|
444
|
+
*
|
|
445
|
+
* Note: `stream:abandoned` is emitted by the cascade event forwarder.
|
|
446
|
+
*/
|
|
447
|
+
abandonStream(
|
|
448
|
+
streamId: string,
|
|
449
|
+
options?: { reason?: string; cascade?: boolean }
|
|
450
|
+
): void {
|
|
451
|
+
this.tracker.abandonStream(streamId, options);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Get the git branch name for a stream.
|
|
456
|
+
*/
|
|
457
|
+
getStreamBranchName(streamId: string): string {
|
|
458
|
+
return this.tracker.getStreamBranchName(streamId);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Get the HEAD commit of a stream.
|
|
463
|
+
*/
|
|
464
|
+
getStreamHead(streamId: string): string {
|
|
465
|
+
return this.tracker.getStreamHead(streamId);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Fork a child stream off a parent.
|
|
470
|
+
*/
|
|
471
|
+
forkStream(options: ForkStreamOptions): string {
|
|
472
|
+
const streamId = this.tracker.forkStream(options);
|
|
473
|
+
this.emit('stream:forked', {
|
|
474
|
+
streamId,
|
|
475
|
+
parentStreamId: options.parentStreamId,
|
|
476
|
+
name: options.name,
|
|
477
|
+
agentId: options.agentId,
|
|
478
|
+
});
|
|
479
|
+
return streamId;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Merge source stream into target stream.
|
|
484
|
+
*
|
|
485
|
+
* Note: `stream:merged` is emitted by the cascade event forwarder on success.
|
|
486
|
+
* On conflict, `stream:conflicted` is also forwarded.
|
|
487
|
+
*/
|
|
488
|
+
mergeStream(options: MergeStreamOptions): MergeResult {
|
|
489
|
+
return this.tracker.mergeStream(options);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Rebase a stream onto its parent to pick up new commits.
|
|
494
|
+
*/
|
|
495
|
+
syncWithParent(
|
|
496
|
+
streamId: string,
|
|
497
|
+
agentId: string,
|
|
498
|
+
worktree: string,
|
|
499
|
+
onConflict?: ConflictStrategy
|
|
500
|
+
): RebaseResult {
|
|
501
|
+
return this.tracker.syncWithParent(streamId, agentId, worktree, onConflict);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Rebase a stream onto a specific target stream.
|
|
506
|
+
*/
|
|
507
|
+
rebaseOntoStream(options: RebaseOntoStreamOptions): RebaseResult {
|
|
508
|
+
return this.tracker.rebaseOntoStream(options);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Async version of rebaseOntoStream — supports async conflict handlers.
|
|
513
|
+
*/
|
|
514
|
+
rebaseOntoStreamAsync(options: RebaseOntoStreamOptions): Promise<RebaseResult> {
|
|
515
|
+
return this.tracker.rebaseOntoStreamAsync(options);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Pause a stream (halt work without abandoning).
|
|
520
|
+
*/
|
|
521
|
+
pauseStream(streamId: string, reason?: string): void {
|
|
522
|
+
this.tracker.pauseStream(streamId, reason);
|
|
523
|
+
this.emit('stream:paused', { streamId, reason });
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Resume a paused stream.
|
|
528
|
+
*/
|
|
529
|
+
resumeStream(streamId: string): void {
|
|
530
|
+
this.tracker.resumeStream(streamId);
|
|
531
|
+
this.emit('stream:resumed', { streamId });
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Track an existing branch as a stream (local mode — no new `stream/<id>` branch).
|
|
536
|
+
*/
|
|
537
|
+
trackExistingBranch(options: Parameters<MultiAgentRepoTracker['trackExistingBranch']>[0]): string {
|
|
538
|
+
return this.tracker.trackExistingBranch(options);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Get stream hierarchy as a tree.
|
|
543
|
+
*/
|
|
544
|
+
getStreamHierarchy(rootStreamId?: string): StreamNode | StreamNode[] {
|
|
545
|
+
return this.tracker.getStreamHierarchy(rootStreamId);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Get child streams (direct children only).
|
|
550
|
+
*/
|
|
551
|
+
getChildStreams(streamId: string): Stream[] {
|
|
552
|
+
return this.tracker.getChildStreams(streamId);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Find the common ancestor of two streams.
|
|
557
|
+
*/
|
|
558
|
+
findCommonAncestor(streamIdA: string, streamIdB: string): string {
|
|
559
|
+
return this.tracker.findCommonAncestor(streamIdA, streamIdB);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
563
|
+
// Stream Dependencies
|
|
564
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Declare that one stream depends on another.
|
|
568
|
+
*/
|
|
569
|
+
addDependency(streamId: string, dependsOnId: string): void {
|
|
570
|
+
this.tracker.addDependency(streamId, dependsOnId);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Remove a dependency declaration.
|
|
575
|
+
*/
|
|
576
|
+
removeDependency(streamId: string, dependsOnId: string): void {
|
|
577
|
+
this.tracker.removeDependency(streamId, dependsOnId);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Get direct dependencies of a stream.
|
|
582
|
+
*/
|
|
583
|
+
getDependencies(streamId: string): string[] {
|
|
584
|
+
return this.tracker.getDependencies(streamId);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Get direct dependents of a stream.
|
|
589
|
+
*/
|
|
590
|
+
getDependents(streamId: string): string[] {
|
|
591
|
+
return this.tracker.getDependents(streamId);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
595
|
+
// Cascade Rebase (git-cascade 0.0.3+)
|
|
596
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Cascade-rebase all dependents of a root stream.
|
|
600
|
+
*
|
|
601
|
+
* Propagates rebases through the dependency graph. Uses a callback-based
|
|
602
|
+
* worktree provider so not every dependent needs a pre-allocated worktree.
|
|
603
|
+
*
|
|
604
|
+
* @param options - Cascade options including root stream, agent id, and worktree provider
|
|
605
|
+
* @returns CascadeResult with updated/failed/skipped stream lists
|
|
606
|
+
*/
|
|
607
|
+
cascadeRebase(
|
|
608
|
+
options: cascadeModule.CascadeRebaseOptions
|
|
609
|
+
): ReturnType<typeof cascadeModule.cascadeRebase> {
|
|
610
|
+
// Use tracker.cascadeRebase which threads the tracker's emit + eventPrefix
|
|
611
|
+
// into the cascade walk so `cascade.rebased` (per dependent) and
|
|
612
|
+
// `cascade.completed` (at end) both round-trip through our
|
|
613
|
+
// forwardCascadeEvent. No manual emit needed — events are driven by
|
|
614
|
+
// git-cascade 0.0.4+ from inside the walk.
|
|
615
|
+
return this.tracker.cascadeRebase(options);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
619
|
+
// Worktree Operations
|
|
620
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Create a worktree for an agent.
|
|
624
|
+
*
|
|
625
|
+
* @param options - Worktree creation options
|
|
626
|
+
* @returns Created worktree info
|
|
627
|
+
*/
|
|
628
|
+
createWorktree(options: CreateWorktreeOptions): AgentWorktree {
|
|
629
|
+
const worktree = this.tracker.createWorktree(options);
|
|
630
|
+
this.emit('worktree:created', { ...worktree });
|
|
631
|
+
return worktree;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Get a worktree by agent ID.
|
|
636
|
+
*/
|
|
637
|
+
getWorktree(agentId: string): AgentWorktree | null {
|
|
638
|
+
return this.tracker.getWorktree(agentId);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* List all worktrees.
|
|
643
|
+
*/
|
|
644
|
+
listWorktrees(): AgentWorktree[] {
|
|
645
|
+
return this.tracker.listWorktrees();
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Update the stream associated with a worktree.
|
|
650
|
+
*/
|
|
651
|
+
updateWorktreeStream(agentId: string, streamId: string | null): void {
|
|
652
|
+
this.tracker.updateWorktreeStream(agentId, streamId);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* Deallocate a worktree.
|
|
657
|
+
*/
|
|
658
|
+
deallocateWorktree(agentId: string): void {
|
|
659
|
+
this.tracker.deallocateWorktree(agentId);
|
|
660
|
+
this.emit('worktree:deallocated', { agentId });
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
664
|
+
// Worker Task Operations
|
|
665
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Create a worker task under a stream.
|
|
669
|
+
*
|
|
670
|
+
* @param options - Task creation options
|
|
671
|
+
* @returns Task ID
|
|
672
|
+
*/
|
|
673
|
+
createTask(options: CreateTaskOptions): string {
|
|
674
|
+
const taskId = this.tracker.createTask(options);
|
|
675
|
+
this.emit('task:created', { taskId, ...options });
|
|
676
|
+
return taskId;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* Get a task by ID.
|
|
681
|
+
*/
|
|
682
|
+
getTask(taskId: string): WorkerTask | null {
|
|
683
|
+
return this.tracker.getTask(taskId);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* List tasks for a stream.
|
|
688
|
+
*/
|
|
689
|
+
listTasks(streamId: string, options?: ListTasksOptions): WorkerTask[] {
|
|
690
|
+
return this.tracker.listTasks(streamId, options);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Start a task - assigns agent and creates worker branch.
|
|
695
|
+
*
|
|
696
|
+
* @param options - Start task options
|
|
697
|
+
* @returns Branch name and start commit
|
|
698
|
+
*/
|
|
699
|
+
startTask(options: StartTaskOptions): StartTaskResult {
|
|
700
|
+
const result = this.tracker.startTask(options);
|
|
701
|
+
this.emit('task:started', {
|
|
702
|
+
taskId: options.taskId,
|
|
703
|
+
agentId: options.agentId,
|
|
704
|
+
branchName: result.branchName,
|
|
705
|
+
startCommit: result.startCommit,
|
|
706
|
+
});
|
|
707
|
+
return result;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* Complete a task - merges worker branch to stream.
|
|
712
|
+
*
|
|
713
|
+
* @param options - Complete task options
|
|
714
|
+
* @returns Merge result
|
|
715
|
+
*/
|
|
716
|
+
completeTask(options: CompleteTaskOptions): CompleteTaskResult {
|
|
717
|
+
const result = this.tracker.completeTask(options);
|
|
718
|
+
this.emit('task:completed', { taskId: options.taskId, ...result });
|
|
719
|
+
return result;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* Abandon a task.
|
|
724
|
+
*
|
|
725
|
+
* @param taskId - Task ID
|
|
726
|
+
* @param options - Options
|
|
727
|
+
*/
|
|
728
|
+
abandonTask(taskId: string, options?: { deleteBranch?: boolean }): void {
|
|
729
|
+
this.tracker.abandonTask(taskId, options);
|
|
730
|
+
this.emit('task:abandoned', { taskId, ...options });
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Release a task back to 'open' status.
|
|
735
|
+
*/
|
|
736
|
+
releaseTask(taskId: string): void {
|
|
737
|
+
this.tracker.releaseTask(taskId);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* Detect conflicts for a task before completing.
|
|
742
|
+
*
|
|
743
|
+
* @param taskId - Task ID
|
|
744
|
+
* @param worktree - Worktree path
|
|
745
|
+
* @returns Array of conflicting file paths, empty if no conflicts
|
|
746
|
+
*/
|
|
747
|
+
detectTaskConflicts(taskId: string, worktree: string): string[] {
|
|
748
|
+
return workerTasks.detectTaskConflicts(
|
|
749
|
+
this.tracker.db,
|
|
750
|
+
this.config.repoPath,
|
|
751
|
+
taskId,
|
|
752
|
+
worktree
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* Recover stale tasks that have been in_progress too long.
|
|
758
|
+
*
|
|
759
|
+
* @param thresholdMs - Tasks older than this are considered stale
|
|
760
|
+
* @returns Result with released task IDs
|
|
761
|
+
*/
|
|
762
|
+
recoverStaleTasks(thresholdMs: number = 60 * 60 * 1000): { released: string[] } {
|
|
763
|
+
return workerTasks.recoverStaleTasks(this.tracker.db, thresholdMs);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
767
|
+
// Checkpoint Operations
|
|
768
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* Create checkpoints for commits made during a task.
|
|
772
|
+
*
|
|
773
|
+
* Creates a checkpoint for each commit between the task's startCommit and
|
|
774
|
+
* the current HEAD of the task's stream. This captures the work done during
|
|
775
|
+
* the task for future review and merge workflows.
|
|
776
|
+
*
|
|
777
|
+
* @param taskId - Task ID to create checkpoints for
|
|
778
|
+
* @param agentId - Agent ID (used as createdBy)
|
|
779
|
+
* @returns Array of created checkpoints
|
|
780
|
+
*/
|
|
781
|
+
createCheckpointsForTask(taskId: string, agentId: string): Checkpoint[] {
|
|
782
|
+
const task = this.getTask(taskId);
|
|
783
|
+
if (!task) {
|
|
784
|
+
console.warn(`[GitCascadeAdapter] Task not found: ${taskId}`);
|
|
785
|
+
return [];
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
if (!task.streamId) {
|
|
789
|
+
console.warn(`[GitCascadeAdapter] Task ${taskId} has no streamId`);
|
|
790
|
+
return [];
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
if (!task.startCommit) {
|
|
794
|
+
console.warn(`[GitCascadeAdapter] Task ${taskId} has no startCommit`);
|
|
795
|
+
return [];
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
try {
|
|
799
|
+
// Create checkpoints from task's startCommit to stream's current HEAD
|
|
800
|
+
const checkpoints = diffStacks.createCheckpointsFromStream(
|
|
801
|
+
this.tracker.db,
|
|
802
|
+
this.config.repoPath,
|
|
803
|
+
task.streamId,
|
|
804
|
+
{
|
|
805
|
+
from: task.startCommit,
|
|
806
|
+
createdBy: agentId,
|
|
807
|
+
}
|
|
808
|
+
);
|
|
809
|
+
|
|
810
|
+
return checkpoints;
|
|
811
|
+
} catch (error) {
|
|
812
|
+
console.error(
|
|
813
|
+
`[GitCascadeAdapter] Failed to create checkpoints for task ${taskId}:`,
|
|
814
|
+
error
|
|
815
|
+
);
|
|
816
|
+
return [];
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
821
|
+
// Commit Operations
|
|
822
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* Commit changes in a worktree with Change tracking.
|
|
826
|
+
*
|
|
827
|
+
* @param options - Commit options
|
|
828
|
+
* @returns Commit hash and change ID
|
|
829
|
+
*/
|
|
830
|
+
commitChanges(options: {
|
|
831
|
+
streamId: string;
|
|
832
|
+
agentId: string;
|
|
833
|
+
worktree: string;
|
|
834
|
+
message: string;
|
|
835
|
+
/**
|
|
836
|
+
* Optional metadata threaded verbatim to git-cascade's
|
|
837
|
+
* `x-cascade/stream.committed` event. Use `{ task_ref: { resource_id,
|
|
838
|
+
* node_id } }` to bind this commit to an external task (see
|
|
839
|
+
* `SpawnAgentOptions.taskRef`). Each commit can carry a distinct ref —
|
|
840
|
+
* useful for workers handling multiple sub-tasks within one session.
|
|
841
|
+
*/
|
|
842
|
+
metadata?: Record<string, unknown>;
|
|
843
|
+
}): { commit: string; changeId: string } {
|
|
844
|
+
return this.tracker.commitChanges(options);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
848
|
+
// Maintenance Operations
|
|
849
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
* Clean up old worker branches.
|
|
853
|
+
*
|
|
854
|
+
* Deletes branches for:
|
|
855
|
+
* - Completed tasks older than threshold (default 24h)
|
|
856
|
+
* - Abandoned tasks
|
|
857
|
+
* - Orphaned branches (no task record)
|
|
858
|
+
*
|
|
859
|
+
* @param options - Cleanup options
|
|
860
|
+
* @returns Deleted branches and any errors
|
|
861
|
+
*/
|
|
862
|
+
cleanupWorkerBranches(options?: CleanupWorkerBranchesOptions): CleanupResult {
|
|
863
|
+
return workerTasks.cleanupWorkerBranches(
|
|
864
|
+
this.tracker.db,
|
|
865
|
+
this.config.repoPath,
|
|
866
|
+
options
|
|
867
|
+
);
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
/**
|
|
871
|
+
* Delete a specific worker branch.
|
|
872
|
+
*
|
|
873
|
+
* @param branchName - Branch name to delete
|
|
874
|
+
* @returns true if deleted, false if branch didn't exist
|
|
875
|
+
*/
|
|
876
|
+
deleteWorkerBranch(branchName: string): boolean {
|
|
877
|
+
try {
|
|
878
|
+
const { execSync } = require('child_process');
|
|
879
|
+
execSync(`git branch -D "${branchName}"`, {
|
|
880
|
+
cwd: this.config.repoPath,
|
|
881
|
+
stdio: 'pipe',
|
|
882
|
+
});
|
|
883
|
+
return true;
|
|
884
|
+
} catch {
|
|
885
|
+
return false;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
890
|
+
// Change Operations (Change-Id tracking)
|
|
891
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
892
|
+
|
|
893
|
+
/**
|
|
894
|
+
* Get a change by ID.
|
|
895
|
+
*/
|
|
896
|
+
getChange(changeId: string): Change | null {
|
|
897
|
+
return this.tracker.getChange(changeId);
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* Get a change by its current commit hash.
|
|
902
|
+
*/
|
|
903
|
+
getChangeByCommit(commit: string): Change | null {
|
|
904
|
+
return this.tracker.getChangeByCommit(commit);
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
/**
|
|
908
|
+
* Get a change by any of its historical commit hashes (survives rebases).
|
|
909
|
+
*/
|
|
910
|
+
getChangeByHistoricalCommit(commit: string): Change | null {
|
|
911
|
+
return this.tracker.getChangeByHistoricalCommit(commit);
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
/**
|
|
915
|
+
* List changes for a stream, optionally filtered by status.
|
|
916
|
+
*/
|
|
917
|
+
getChangesForStream(
|
|
918
|
+
streamId: string,
|
|
919
|
+
options?: { status?: ChangeStatus }
|
|
920
|
+
): Change[] {
|
|
921
|
+
return this.tracker.getChangesForStream(streamId, options);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
/**
|
|
925
|
+
* Mark changes as merged.
|
|
926
|
+
*/
|
|
927
|
+
markChangesMerged(changeIds: string[]): void {
|
|
928
|
+
this.tracker.markChangesMerged(changeIds);
|
|
929
|
+
for (const id of changeIds) {
|
|
930
|
+
this.emit('change:merged', { changeId: id });
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
/**
|
|
935
|
+
* Mark a single change as dropped.
|
|
936
|
+
*/
|
|
937
|
+
markChangeDropped(changeId: string): void {
|
|
938
|
+
this.tracker.markChangeDropped(changeId);
|
|
939
|
+
this.emit('change:dropped', { changeId });
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
943
|
+
// Merge Queue (git-cascade built-in)
|
|
944
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
945
|
+
|
|
946
|
+
/**
|
|
947
|
+
* Add a stream to the merge queue. Local `mergeQueue:added` event fires
|
|
948
|
+
* via forwardCascadeEvent (git-cascade 0.0.7+ emits queue.added natively).
|
|
949
|
+
*/
|
|
950
|
+
addToMergeQueue(options: mergeQueueModule.AddToQueueOptions): string {
|
|
951
|
+
return this.tracker.addToMergeQueue(options);
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
/**
|
|
955
|
+
* Get a merge queue entry by id.
|
|
956
|
+
*/
|
|
957
|
+
getMergeQueueEntry(entryId: string): mergeQueueModule.MergeQueueEntry | null {
|
|
958
|
+
return this.tracker.getMergeQueueEntry(entryId);
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* List merge queue entries with optional filters.
|
|
963
|
+
*/
|
|
964
|
+
listMergeQueue(
|
|
965
|
+
options?: {
|
|
966
|
+
targetBranch?: string;
|
|
967
|
+
status?: mergeQueueModule.MergeQueueStatus | mergeQueueModule.MergeQueueStatus[];
|
|
968
|
+
}
|
|
969
|
+
): mergeQueueModule.MergeQueueEntry[] {
|
|
970
|
+
return this.tracker.getMergeQueue(options);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
/**
|
|
974
|
+
* Mark a queue entry as ready to merge. Local `mergeQueue:ready` event
|
|
975
|
+
* fires via forwardCascadeEvent (git-cascade 0.0.7+).
|
|
976
|
+
*/
|
|
977
|
+
markMergeQueueReady(entryId: string): void {
|
|
978
|
+
this.tracker.markMergeQueueReady(entryId);
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
/**
|
|
982
|
+
* Cancel a queue entry. Local `mergeQueue:cancelled` event fires via
|
|
983
|
+
* forwardCascadeEvent (git-cascade 0.0.7+).
|
|
984
|
+
*/
|
|
985
|
+
cancelMergeQueueEntry(entryId: string): void {
|
|
986
|
+
this.tracker.cancelMergeQueueEntry(entryId);
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
/**
|
|
990
|
+
* Remove a queue entry. Local `mergeQueue:removed` event fires via
|
|
991
|
+
* forwardCascadeEvent (git-cascade 0.0.7+).
|
|
992
|
+
*/
|
|
993
|
+
removeFromMergeQueue(entryId: string): void {
|
|
994
|
+
this.tracker.removeFromMergeQueue(entryId);
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
/**
|
|
998
|
+
* Get the next entry to process for a target branch.
|
|
999
|
+
*/
|
|
1000
|
+
getNextToMerge(targetBranch?: string): mergeQueueModule.MergeQueueEntry | null {
|
|
1001
|
+
return this.tracker.getNextToMerge(targetBranch);
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
/**
|
|
1005
|
+
* Process the merge queue — drains ready entries per provided handler.
|
|
1006
|
+
*/
|
|
1007
|
+
processMergeQueue(
|
|
1008
|
+
options: mergeQueueModule.ProcessQueueOptions
|
|
1009
|
+
): mergeQueueModule.ProcessQueueResult {
|
|
1010
|
+
return this.tracker.processMergeQueue(options);
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
/**
|
|
1014
|
+
* Get a stream's position in the queue (lower = sooner).
|
|
1015
|
+
*/
|
|
1016
|
+
getMergeQueuePosition(streamId: string, targetBranch?: string): number | null {
|
|
1017
|
+
return this.tracker.getMergeQueuePosition(streamId, targetBranch);
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1021
|
+
// Conflict Operations
|
|
1022
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1023
|
+
|
|
1024
|
+
/**
|
|
1025
|
+
* Create a conflict record.
|
|
1026
|
+
*
|
|
1027
|
+
* Usually conflicts are created implicitly by merge/rebase operations.
|
|
1028
|
+
* This is for explicit creation (e.g., an external process detected a
|
|
1029
|
+
* conflict that git-cascade didn't).
|
|
1030
|
+
*/
|
|
1031
|
+
createConflict(options: CreateConflictOptions): string {
|
|
1032
|
+
const id = this.tracker.createConflict(options);
|
|
1033
|
+
this.emit('conflict:created', {
|
|
1034
|
+
conflictId: id,
|
|
1035
|
+
streamId: options.streamId,
|
|
1036
|
+
});
|
|
1037
|
+
return id;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
/**
|
|
1041
|
+
* Get a conflict record by id.
|
|
1042
|
+
*/
|
|
1043
|
+
getConflict(conflictId: string): ConflictRecord | null {
|
|
1044
|
+
return this.tracker.getConflict(conflictId);
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
/**
|
|
1048
|
+
* Get the active conflict record for a stream, if any.
|
|
1049
|
+
*/
|
|
1050
|
+
getConflictForStream(streamId: string): ConflictRecord | null {
|
|
1051
|
+
return this.tracker.getConflictForStream(streamId);
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
/**
|
|
1055
|
+
* Emit a `stream:pushed` event for trunk-style landing strategies that push
|
|
1056
|
+
* to a remote rather than merging into another stream. Strategies call this
|
|
1057
|
+
* after a successful push so observers (the OpenHive cascade-bridge) can
|
|
1058
|
+
* surface the push as `x-cascade/stream.pushed`.
|
|
1059
|
+
*/
|
|
1060
|
+
notifyStreamPushed(args: {
|
|
1061
|
+
streamId: string;
|
|
1062
|
+
agentId: string;
|
|
1063
|
+
pushedCommit: string;
|
|
1064
|
+
remote: string;
|
|
1065
|
+
remoteRef: string;
|
|
1066
|
+
strategy?: string;
|
|
1067
|
+
metadata?: Record<string, unknown>;
|
|
1068
|
+
}): void {
|
|
1069
|
+
this.emit('stream:pushed', {
|
|
1070
|
+
streamId: args.streamId,
|
|
1071
|
+
agentId: args.agentId,
|
|
1072
|
+
pushedCommit: args.pushedCommit,
|
|
1073
|
+
remote: args.remote,
|
|
1074
|
+
remoteRef: args.remoteRef,
|
|
1075
|
+
strategy: args.strategy,
|
|
1076
|
+
metadata: args.metadata,
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
/**
|
|
1081
|
+
* Mark a conflict as resolved. Routes through git-cascade's tracker so
|
|
1082
|
+
* `x-cascade/stream.conflict_resolved` fires for hub observers (closes
|
|
1083
|
+
* cascade_conflicts.status from pending → resolved on the OpenHive side).
|
|
1084
|
+
*/
|
|
1085
|
+
resolveConflict(args: {
|
|
1086
|
+
conflictId: string;
|
|
1087
|
+
resolution: import('git-cascade').ConflictResolution & { summary?: string };
|
|
1088
|
+
metadata?: Record<string, unknown>;
|
|
1089
|
+
}): void {
|
|
1090
|
+
this.tracker.resolveConflict(args.conflictId, args.resolution, {
|
|
1091
|
+
metadata: args.metadata,
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
/**
|
|
1096
|
+
* Abandon a conflict (stream-level give-up). Emits a
|
|
1097
|
+
* `stream.conflict_resolved` event with method='abandoned' so observers
|
|
1098
|
+
* see the conflict is no longer pending.
|
|
1099
|
+
*/
|
|
1100
|
+
abandonConflict(args: {
|
|
1101
|
+
conflictId: string;
|
|
1102
|
+
agentId?: string;
|
|
1103
|
+
reason?: string;
|
|
1104
|
+
metadata?: Record<string, unknown>;
|
|
1105
|
+
}): void {
|
|
1106
|
+
this.tracker.abandonConflict(args.conflictId, {
|
|
1107
|
+
agentId: args.agentId,
|
|
1108
|
+
reason: args.reason,
|
|
1109
|
+
metadata: args.metadata,
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1114
|
+
// Reconciliation
|
|
1115
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1116
|
+
|
|
1117
|
+
/**
|
|
1118
|
+
* Check if a stream's database state is in sync with its git branch.
|
|
1119
|
+
*/
|
|
1120
|
+
checkStreamSync(streamId: string): reconcileModule.StreamSyncStatus {
|
|
1121
|
+
return this.tracker.checkStreamSync(streamId);
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
/**
|
|
1125
|
+
* Check all active streams for sync status.
|
|
1126
|
+
*/
|
|
1127
|
+
checkAllStreamsSync(
|
|
1128
|
+
options?: { streamIds?: string[] }
|
|
1129
|
+
): reconcileModule.ReconcileCheckResult {
|
|
1130
|
+
return this.tracker.checkAllStreamsSync(options);
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
/**
|
|
1134
|
+
* Reconcile database state with git state. Fixes missing branches, resets
|
|
1135
|
+
* diverged HEAD (per options), etc. Does NOT handle orphan worktrees — the
|
|
1136
|
+
* macro-agent-level reconcile wrapper covers that.
|
|
1137
|
+
*/
|
|
1138
|
+
reconcile(
|
|
1139
|
+
options?: reconcileModule.ReconcileOptions
|
|
1140
|
+
): reconcileModule.ReconcileResult {
|
|
1141
|
+
return this.tracker.reconcile(options);
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
/**
|
|
1145
|
+
* Ensure a stream is in sync before performing an operation.
|
|
1146
|
+
* @throws DesyncError if out of sync unless `force: true`.
|
|
1147
|
+
*/
|
|
1148
|
+
ensureStreamInSync(streamId: string, options?: { force?: boolean }): void {
|
|
1149
|
+
this.tracker.ensureStreamInSync(streamId, options);
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1153
|
+
// Health & Recovery
|
|
1154
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1155
|
+
|
|
1156
|
+
/**
|
|
1157
|
+
* Check system health.
|
|
1158
|
+
*/
|
|
1159
|
+
healthCheck(): ReturnType<MultiAgentRepoTracker['healthCheck']> {
|
|
1160
|
+
return this.tracker.healthCheck();
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1164
|
+
// Lifecycle
|
|
1165
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1166
|
+
|
|
1167
|
+
/**
|
|
1168
|
+
* Close the adapter and release resources.
|
|
1169
|
+
*
|
|
1170
|
+
* Only closes the database if we created it (not if using shared DB).
|
|
1171
|
+
*/
|
|
1172
|
+
close(): void {
|
|
1173
|
+
this.eventListeners.clear();
|
|
1174
|
+
this.tracker.close();
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
/**
|
|
1179
|
+
* Create a GitCascadeAdapter instance.
|
|
1180
|
+
*
|
|
1181
|
+
* @param config - Configuration options
|
|
1182
|
+
* @returns GitCascadeAdapter instance
|
|
1183
|
+
*/
|
|
1184
|
+
export function createGitCascadeAdapter(config: GitCascadeConfig): GitCascadeAdapter {
|
|
1185
|
+
return new GitCascadeAdapter(config);
|
|
1186
|
+
}
|