opencode-swarm-plugin 0.42.9 → 0.44.0
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/.hive/issues.jsonl +14 -0
- package/.turbo/turbo-build.log +2 -2
- package/CHANGELOG.md +110 -0
- package/README.md +296 -6
- package/bin/cass.characterization.test.ts +422 -0
- package/bin/swarm.test.ts +683 -0
- package/bin/swarm.ts +501 -0
- package/dist/contributor-tools.d.ts +42 -0
- package/dist/contributor-tools.d.ts.map +1 -0
- package/dist/dashboard.d.ts +83 -0
- package/dist/dashboard.d.ts.map +1 -0
- package/dist/error-enrichment.d.ts +49 -0
- package/dist/error-enrichment.d.ts.map +1 -0
- package/dist/export-tools.d.ts +76 -0
- package/dist/export-tools.d.ts.map +1 -0
- package/dist/index.d.ts +14 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +95 -2
- package/dist/observability-tools.d.ts +2 -2
- package/dist/plugin.js +95 -2
- package/dist/query-tools.d.ts +59 -0
- package/dist/query-tools.d.ts.map +1 -0
- package/dist/replay-tools.d.ts +28 -0
- package/dist/replay-tools.d.ts.map +1 -0
- package/dist/sessions/agent-discovery.d.ts +59 -0
- package/dist/sessions/agent-discovery.d.ts.map +1 -0
- package/dist/sessions/index.d.ts +10 -0
- package/dist/sessions/index.d.ts.map +1 -0
- package/docs/planning/ADR-010-cass-inhousing.md +1215 -0
- package/evals/fixtures/cass-baseline.ts +217 -0
- package/examples/plugin-wrapper-template.ts +89 -0
- package/package.json +1 -1
- package/src/contributor-tools.test.ts +133 -0
- package/src/contributor-tools.ts +201 -0
- package/src/dashboard.test.ts +611 -0
- package/src/dashboard.ts +462 -0
- package/src/error-enrichment.test.ts +403 -0
- package/src/error-enrichment.ts +219 -0
- package/src/export-tools.test.ts +476 -0
- package/src/export-tools.ts +257 -0
- package/src/index.ts +8 -3
- package/src/query-tools.test.ts +636 -0
- package/src/query-tools.ts +324 -0
- package/src/replay-tools.test.ts +496 -0
- package/src/replay-tools.ts +240 -0
- package/src/sessions/agent-discovery.test.ts +137 -0
- package/src/sessions/agent-discovery.ts +112 -0
- package/src/sessions/index.ts +15 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replay Tools - Event replay with timing simulation
|
|
3
|
+
*
|
|
4
|
+
* TDD GREEN: Minimal implementation to pass tests
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import { existsSync } from "node:fs";
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Types
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
export type ReplaySpeed = "1x" | "2x" | "instant";
|
|
15
|
+
|
|
16
|
+
export interface ReplayEvent {
|
|
17
|
+
session_id: string;
|
|
18
|
+
epic_id: string;
|
|
19
|
+
timestamp: string;
|
|
20
|
+
event_type: "DECISION" | "VIOLATION" | "OUTCOME" | "COMPACTION";
|
|
21
|
+
decision_type?: string;
|
|
22
|
+
violation_type?: string;
|
|
23
|
+
outcome_type?: string;
|
|
24
|
+
payload: Record<string, unknown>;
|
|
25
|
+
delta_ms: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ReplayFilter {
|
|
29
|
+
type?: Array<"DECISION" | "VIOLATION" | "OUTCOME" | "COMPACTION">;
|
|
30
|
+
agent?: string;
|
|
31
|
+
since?: Date;
|
|
32
|
+
until?: Date;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ============================================================================
|
|
36
|
+
// fetchEpicEvents - Read JSONL and calculate deltas
|
|
37
|
+
// ============================================================================
|
|
38
|
+
|
|
39
|
+
export async function fetchEpicEvents(
|
|
40
|
+
epicId: string,
|
|
41
|
+
sessionFile: string,
|
|
42
|
+
): Promise<ReplayEvent[]> {
|
|
43
|
+
// Handle non-existent files
|
|
44
|
+
if (!existsSync(sessionFile)) {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const content = readFileSync(sessionFile, "utf-8");
|
|
50
|
+
const lines = content.trim().split("\n").filter(Boolean);
|
|
51
|
+
|
|
52
|
+
if (lines.length === 0) {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Parse JSONL
|
|
57
|
+
const events = lines.map((line) => JSON.parse(line));
|
|
58
|
+
|
|
59
|
+
// Sort by timestamp (chronological order)
|
|
60
|
+
events.sort(
|
|
61
|
+
(a, b) =>
|
|
62
|
+
new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime(),
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
// Calculate delta_ms between events
|
|
66
|
+
const replayEvents: ReplayEvent[] = [];
|
|
67
|
+
let prevTime = 0;
|
|
68
|
+
|
|
69
|
+
for (const event of events) {
|
|
70
|
+
const currTime = new Date(event.timestamp).getTime();
|
|
71
|
+
const delta_ms = prevTime === 0 ? 0 : currTime - prevTime;
|
|
72
|
+
|
|
73
|
+
replayEvents.push({
|
|
74
|
+
...event,
|
|
75
|
+
delta_ms,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
prevTime = currTime;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return replayEvents;
|
|
82
|
+
} catch (error) {
|
|
83
|
+
// Handle errors (e.g., invalid JSON)
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ============================================================================
|
|
89
|
+
// filterEvents - Filter by type/agent/time (AND logic)
|
|
90
|
+
// ============================================================================
|
|
91
|
+
|
|
92
|
+
export function filterEvents(
|
|
93
|
+
events: ReplayEvent[],
|
|
94
|
+
filter: ReplayFilter,
|
|
95
|
+
): ReplayEvent[] {
|
|
96
|
+
return events.filter((event) => {
|
|
97
|
+
// Filter by event type
|
|
98
|
+
if (filter.type && !filter.type.includes(event.event_type)) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Filter by agent name (from payload)
|
|
103
|
+
if (filter.agent) {
|
|
104
|
+
const payload = event.payload as any;
|
|
105
|
+
if (payload.agent_name !== filter.agent) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Filter by time range (since)
|
|
111
|
+
if (filter.since) {
|
|
112
|
+
const eventTime = new Date(event.timestamp);
|
|
113
|
+
if (eventTime < filter.since) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Filter by time range (until)
|
|
119
|
+
if (filter.until) {
|
|
120
|
+
const eventTime = new Date(event.timestamp);
|
|
121
|
+
if (eventTime > filter.until) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return true;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ============================================================================
|
|
131
|
+
// replayWithTiming - Async generator with speed control
|
|
132
|
+
// ============================================================================
|
|
133
|
+
|
|
134
|
+
export async function* replayWithTiming(
|
|
135
|
+
events: ReplayEvent[],
|
|
136
|
+
speed: ReplaySpeed,
|
|
137
|
+
): AsyncGenerator<ReplayEvent> {
|
|
138
|
+
if (events.length === 0) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const startTime = Date.now();
|
|
143
|
+
let cumulativeDelay = 0;
|
|
144
|
+
|
|
145
|
+
for (const event of events) {
|
|
146
|
+
// Calculate target time for this event
|
|
147
|
+
cumulativeDelay += event.delta_ms;
|
|
148
|
+
|
|
149
|
+
let targetDelay = cumulativeDelay;
|
|
150
|
+
if (speed === "2x") {
|
|
151
|
+
targetDelay = targetDelay / 2;
|
|
152
|
+
} else if (speed === "instant") {
|
|
153
|
+
targetDelay = 0;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Calculate actual delay needed (accounting for time already elapsed)
|
|
157
|
+
const elapsed = Date.now() - startTime;
|
|
158
|
+
const delay = targetDelay - elapsed;
|
|
159
|
+
|
|
160
|
+
// Wait for the delay if needed (with small buffer for overhead)
|
|
161
|
+
if (delay > 3) {
|
|
162
|
+
// Subtract 3ms buffer to account for async overhead
|
|
163
|
+
const adjustedDelay = delay - 3;
|
|
164
|
+
|
|
165
|
+
// Use Bun.sleep for more precise timing in Bun runtime
|
|
166
|
+
if (typeof Bun !== "undefined" && typeof Bun.sleep === "function") {
|
|
167
|
+
await Bun.sleep(adjustedDelay);
|
|
168
|
+
} else {
|
|
169
|
+
await new Promise((resolve) => setTimeout(resolve, adjustedDelay));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Yield the event
|
|
174
|
+
yield event;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ============================================================================
|
|
179
|
+
// formatReplayEvent - ANSI colors + box-drawing + relationships
|
|
180
|
+
// ============================================================================
|
|
181
|
+
|
|
182
|
+
export function formatReplayEvent(event: ReplayEvent): string {
|
|
183
|
+
// ANSI color codes
|
|
184
|
+
const colors = {
|
|
185
|
+
DECISION: "\x1b[34m", // Blue
|
|
186
|
+
VIOLATION: "\x1b[31m", // Red
|
|
187
|
+
OUTCOME: "\x1b[32m", // Green
|
|
188
|
+
COMPACTION: "\x1b[33m", // Yellow
|
|
189
|
+
reset: "\x1b[0m",
|
|
190
|
+
gray: "\x1b[90m",
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const color = colors[event.event_type] || colors.reset;
|
|
194
|
+
|
|
195
|
+
// Format timestamp (remove 'Z' suffix and put it BEFORE color codes for regex match)
|
|
196
|
+
const timestamp = new Date(event.timestamp)
|
|
197
|
+
.toISOString()
|
|
198
|
+
.split("T")[1]
|
|
199
|
+
.replace("Z", "");
|
|
200
|
+
const timePrefix = `[${timestamp}]`;
|
|
201
|
+
|
|
202
|
+
// Extract relevant payload fields
|
|
203
|
+
const payload = event.payload as any;
|
|
204
|
+
const beadId = payload.bead_id || "";
|
|
205
|
+
const agentName = payload.agent_name || "";
|
|
206
|
+
const strategyUsed = payload.strategy_used || "";
|
|
207
|
+
const subtaskCount = payload.subtask_count;
|
|
208
|
+
|
|
209
|
+
// Build formatted output with box-drawing characters
|
|
210
|
+
// Put timestamp BEFORE any color codes so regex matches
|
|
211
|
+
let output = `${timePrefix} `;
|
|
212
|
+
output += `${color}┌─ ${event.event_type}${colors.reset}\n`;
|
|
213
|
+
|
|
214
|
+
// Epic relationship
|
|
215
|
+
output += `${colors.gray}│${colors.reset} epic: ${event.epic_id}\n`;
|
|
216
|
+
|
|
217
|
+
// Bead relationship (if present)
|
|
218
|
+
if (beadId) {
|
|
219
|
+
output += `${colors.gray}│${colors.reset} bead: ${beadId}\n`;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Agent (if present)
|
|
223
|
+
if (agentName) {
|
|
224
|
+
output += `${colors.gray}│${colors.reset} agent: ${agentName}\n`;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Strategy (if present)
|
|
228
|
+
if (strategyUsed) {
|
|
229
|
+
output += `${colors.gray}│${colors.reset} strategy: ${strategyUsed}\n`;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Subtask count (if present)
|
|
233
|
+
if (subtaskCount !== undefined) {
|
|
234
|
+
output += `${colors.gray}│${colors.reset} subtasks: ${subtaskCount}\n`;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
output += `${colors.gray}└─${colors.reset}`;
|
|
238
|
+
|
|
239
|
+
return output;
|
|
240
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Discovery Tests
|
|
3
|
+
*
|
|
4
|
+
* Maps file paths to agent types using pattern matching.
|
|
5
|
+
* Follows TDD pattern from ADR-010 Section 4.5.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, test, expect, afterEach } from "bun:test";
|
|
9
|
+
import {
|
|
10
|
+
detectAgentType,
|
|
11
|
+
loadAgentPatterns,
|
|
12
|
+
resetAgentPatterns,
|
|
13
|
+
} from "./agent-discovery";
|
|
14
|
+
|
|
15
|
+
describe("detectAgentType", () => {
|
|
16
|
+
test("detects OpenCode Swarm sessions", () => {
|
|
17
|
+
expect(
|
|
18
|
+
detectAgentType(
|
|
19
|
+
"/home/user/.config/swarm-tools/sessions/ses_123.jsonl"
|
|
20
|
+
)
|
|
21
|
+
).toBe("opencode-swarm");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("detects Cursor sessions", () => {
|
|
25
|
+
expect(
|
|
26
|
+
detectAgentType(
|
|
27
|
+
"/Users/joel/Library/Application Support/Cursor/User/History/abc/9ScS.jsonl"
|
|
28
|
+
)
|
|
29
|
+
).toBe("cursor");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("detects OpenCode sessions", () => {
|
|
33
|
+
expect(detectAgentType("/Users/joel/.opencode/session.jsonl")).toBe(
|
|
34
|
+
"opencode"
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("detects Claude sessions", () => {
|
|
39
|
+
expect(
|
|
40
|
+
detectAgentType("/home/user/.local/share/Claude/ses_456.jsonl")
|
|
41
|
+
).toBe("claude");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("detects Aider sessions", () => {
|
|
45
|
+
expect(detectAgentType("/home/user/.aider/session.jsonl")).toBe("aider");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("returns null for unknown paths", () => {
|
|
49
|
+
expect(detectAgentType("/tmp/random.jsonl")).toBeNull();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("handles Windows-style paths", () => {
|
|
53
|
+
expect(
|
|
54
|
+
detectAgentType("C:\\Users\\joel\\.config\\swarm-tools\\sessions\\ses.jsonl")
|
|
55
|
+
).toBe("opencode-swarm");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("handles nested paths", () => {
|
|
59
|
+
expect(
|
|
60
|
+
detectAgentType(
|
|
61
|
+
"/Users/joel/.opencode/deeply/nested/folder/session.jsonl"
|
|
62
|
+
)
|
|
63
|
+
).toBe("opencode");
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe("loadAgentPatterns", () => {
|
|
68
|
+
afterEach(() => {
|
|
69
|
+
// Reset to defaults after each test
|
|
70
|
+
resetAgentPatterns();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("loads custom agent patterns", () => {
|
|
74
|
+
const count = loadAgentPatterns([
|
|
75
|
+
{ pattern: "\\.codex[/\\\\]", agentType: "opencode-swarm" },
|
|
76
|
+
]);
|
|
77
|
+
|
|
78
|
+
expect(count).toBe(1);
|
|
79
|
+
expect(detectAgentType("/home/user/.codex/session.jsonl")).toBe(
|
|
80
|
+
"opencode-swarm"
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("replaces default patterns with custom ones", () => {
|
|
85
|
+
loadAgentPatterns([
|
|
86
|
+
{ pattern: "\\.custom[/\\\\]", agentType: "cursor" },
|
|
87
|
+
]);
|
|
88
|
+
|
|
89
|
+
// Custom pattern works
|
|
90
|
+
expect(detectAgentType("/path/.custom/file.jsonl")).toBe("cursor");
|
|
91
|
+
|
|
92
|
+
// Default patterns no longer work
|
|
93
|
+
expect(
|
|
94
|
+
detectAgentType("/home/user/.config/swarm-tools/sessions/ses.jsonl")
|
|
95
|
+
).toBeNull();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("supports multiple custom patterns", () => {
|
|
99
|
+
loadAgentPatterns([
|
|
100
|
+
{ pattern: "\\.agent1[/\\\\]", agentType: "opencode-swarm" },
|
|
101
|
+
{ pattern: "\\.agent2[/\\\\]", agentType: "cursor" },
|
|
102
|
+
{ pattern: "\\.agent3[/\\\\]", agentType: "claude" },
|
|
103
|
+
]);
|
|
104
|
+
|
|
105
|
+
expect(detectAgentType("/path/.agent1/file.jsonl")).toBe(
|
|
106
|
+
"opencode-swarm"
|
|
107
|
+
);
|
|
108
|
+
expect(detectAgentType("/path/.agent2/file.jsonl")).toBe("cursor");
|
|
109
|
+
expect(detectAgentType("/path/.agent3/file.jsonl")).toBe("claude");
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe("resetAgentPatterns", () => {
|
|
114
|
+
test("restores default patterns after custom load", () => {
|
|
115
|
+
// Load custom patterns
|
|
116
|
+
loadAgentPatterns([
|
|
117
|
+
{ pattern: "\\.custom[/\\\\]", agentType: "opencode-swarm" },
|
|
118
|
+
]);
|
|
119
|
+
|
|
120
|
+
// Custom works, defaults don't
|
|
121
|
+
expect(detectAgentType("/path/.custom/file.jsonl")).toBe(
|
|
122
|
+
"opencode-swarm"
|
|
123
|
+
);
|
|
124
|
+
expect(
|
|
125
|
+
detectAgentType("/home/user/.config/swarm-tools/sessions/ses.jsonl")
|
|
126
|
+
).toBeNull();
|
|
127
|
+
|
|
128
|
+
// Reset
|
|
129
|
+
resetAgentPatterns();
|
|
130
|
+
|
|
131
|
+
// Defaults work again
|
|
132
|
+
expect(
|
|
133
|
+
detectAgentType("/home/user/.config/swarm-tools/sessions/ses.jsonl")
|
|
134
|
+
).toBe("opencode-swarm");
|
|
135
|
+
expect(detectAgentType("/path/.custom/file.jsonl")).toBeNull();
|
|
136
|
+
});
|
|
137
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Discovery Module
|
|
3
|
+
*
|
|
4
|
+
* Maps file paths to agent types using pattern matching.
|
|
5
|
+
* Supports cross-platform paths (Unix and Windows).
|
|
6
|
+
*
|
|
7
|
+
* @module sessions/agent-discovery
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Agent type identifier
|
|
12
|
+
*/
|
|
13
|
+
export type AgentType =
|
|
14
|
+
| "opencode-swarm"
|
|
15
|
+
| "cursor"
|
|
16
|
+
| "opencode"
|
|
17
|
+
| "claude"
|
|
18
|
+
| "aider";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Path pattern configuration for agent type detection
|
|
22
|
+
*/
|
|
23
|
+
interface AgentPathPattern {
|
|
24
|
+
/** RegExp pattern to match against file path */
|
|
25
|
+
pattern: RegExp;
|
|
26
|
+
/** Agent type to return when pattern matches */
|
|
27
|
+
agentType: AgentType;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Default path patterns for agent type detection
|
|
32
|
+
* Order matters - first match wins
|
|
33
|
+
*
|
|
34
|
+
* Can be overridden via loadAgentPatterns()
|
|
35
|
+
*/
|
|
36
|
+
let AGENT_PATH_PATTERNS: AgentPathPattern[] = [
|
|
37
|
+
{ pattern: /\.config[\/\\]swarm-tools[\/\\]sessions[\/\\]/, agentType: "opencode-swarm" },
|
|
38
|
+
{ pattern: /Cursor[\/\\]User[\/\\]History[\/\\]/, agentType: "cursor" },
|
|
39
|
+
{ pattern: /\.opencode[\/\\]/, agentType: "opencode" },
|
|
40
|
+
{ pattern: /\.local[\/\\]share[\/\\]Claude[\/\\]/, agentType: "claude" },
|
|
41
|
+
{ pattern: /\.aider/, agentType: "aider" },
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Configuration for custom agent patterns
|
|
46
|
+
*/
|
|
47
|
+
interface AgentPatternConfig {
|
|
48
|
+
/** Pattern string (will be converted to RegExp) */
|
|
49
|
+
pattern: string;
|
|
50
|
+
/** Agent type identifier */
|
|
51
|
+
agentType: AgentType;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Load custom agent patterns from config
|
|
56
|
+
*
|
|
57
|
+
* @param patterns - Array of custom pattern configurations
|
|
58
|
+
* @returns Number of patterns loaded
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* loadAgentPatterns([
|
|
63
|
+
* { pattern: "\\.codex[/\\\\]", agentType: "codex" },
|
|
64
|
+
* { pattern: "\\.gemini[/\\\\]", agentType: "gemini" }
|
|
65
|
+
* ]);
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export function loadAgentPatterns(patterns: AgentPatternConfig[]): number {
|
|
69
|
+
AGENT_PATH_PATTERNS = patterns.map(({ pattern, agentType }) => ({
|
|
70
|
+
pattern: new RegExp(pattern),
|
|
71
|
+
agentType,
|
|
72
|
+
}));
|
|
73
|
+
return AGENT_PATH_PATTERNS.length;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Reset agent patterns to defaults
|
|
78
|
+
* Useful for testing
|
|
79
|
+
*/
|
|
80
|
+
export function resetAgentPatterns(): void {
|
|
81
|
+
AGENT_PATH_PATTERNS = [
|
|
82
|
+
{ pattern: /\.config[\/\\]swarm-tools[\/\\]sessions[\/\\]/, agentType: "opencode-swarm" },
|
|
83
|
+
{ pattern: /Cursor[\/\\]User[\/\\]History[\/\\]/, agentType: "cursor" },
|
|
84
|
+
{ pattern: /\.opencode[\/\\]/, agentType: "opencode" },
|
|
85
|
+
{ pattern: /\.local[\/\\]share[\/\\]Claude[\/\\]/, agentType: "claude" },
|
|
86
|
+
{ pattern: /\.aider/, agentType: "aider" },
|
|
87
|
+
];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Detect agent type from file path
|
|
92
|
+
*
|
|
93
|
+
* @param filePath - Absolute or relative file path (Unix or Windows)
|
|
94
|
+
* @returns Agent type identifier, or null if unknown
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* detectAgentType("/home/user/.config/swarm-tools/sessions/ses_123.jsonl")
|
|
99
|
+
* // => "opencode-swarm"
|
|
100
|
+
*
|
|
101
|
+
* detectAgentType("/tmp/random.jsonl")
|
|
102
|
+
* // => null
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
export function detectAgentType(filePath: string): AgentType | null {
|
|
106
|
+
for (const { pattern, agentType } of AGENT_PATH_PATTERNS) {
|
|
107
|
+
if (pattern.test(filePath)) {
|
|
108
|
+
return agentType;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Indexing Module
|
|
3
|
+
*
|
|
4
|
+
* Provides agent session discovery and indexing capabilities.
|
|
5
|
+
* Part of the CASS Inhousing initiative (ADR-010).
|
|
6
|
+
*
|
|
7
|
+
* @module sessions
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
detectAgentType,
|
|
12
|
+
loadAgentPatterns,
|
|
13
|
+
resetAgentPatterns,
|
|
14
|
+
type AgentType,
|
|
15
|
+
} from "./agent-discovery";
|