@ai-devkit/agent-manager 0.9.0 → 0.11.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/dist/AgentManager.d.ts +21 -1
- package/dist/AgentManager.d.ts.map +1 -1
- package/dist/AgentManager.js +47 -0
- package/dist/AgentManager.js.map +1 -1
- package/dist/adapters/AgentAdapter.d.ts +66 -0
- package/dist/adapters/AgentAdapter.d.ts.map +1 -1
- package/dist/adapters/ClaudeCodeAdapter.d.ts +14 -43
- package/dist/adapters/ClaudeCodeAdapter.d.ts.map +1 -1
- package/dist/adapters/ClaudeCodeAdapter.js +60 -275
- package/dist/adapters/ClaudeCodeAdapter.js.map +1 -1
- package/dist/adapters/CodexAdapter.d.ts +14 -1
- package/dist/adapters/CodexAdapter.d.ts.map +1 -1
- package/dist/adapters/CodexAdapter.js +105 -6
- package/dist/adapters/CodexAdapter.js.map +1 -1
- package/dist/adapters/GeminiCliAdapter.d.ts +9 -1
- package/dist/adapters/GeminiCliAdapter.d.ts.map +1 -1
- package/dist/adapters/GeminiCliAdapter.js +77 -6
- package/dist/adapters/GeminiCliAdapter.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/terminal/TerminalFocusManager.d.ts.map +1 -1
- package/dist/terminal/TerminalFocusManager.js +36 -27
- package/dist/terminal/TerminalFocusManager.js.map +1 -1
- package/dist/terminal/TtyWriter.d.ts.map +1 -1
- package/dist/terminal/TtyWriter.js +3 -12
- package/dist/terminal/TtyWriter.js.map +1 -1
- package/dist/utils/ClaudeSessionParser.d.ts +114 -0
- package/dist/utils/ClaudeSessionParser.d.ts.map +1 -0
- package/dist/utils/ClaudeSessionParser.js +377 -0
- package/dist/utils/ClaudeSessionParser.js.map +1 -0
- package/dist/utils/applescript.d.ts +6 -0
- package/dist/utils/applescript.d.ts.map +1 -0
- package/dist/utils/applescript.js +14 -0
- package/dist/utils/applescript.js.map +1 -0
- package/dist/utils/process.d.ts +3 -4
- package/dist/utils/process.d.ts.map +1 -1
- package/dist/utils/process.js +11 -15
- package/dist/utils/process.js.map +1 -1
- package/dist/utils/session.d.ts +34 -5
- package/dist/utils/session.d.ts.map +1 -1
- package/dist/utils/session.js +90 -44
- package/dist/utils/session.js.map +1 -1
- package/package.json +1 -1
- package/src/AgentManager.ts +66 -4
- package/src/__tests__/AgentManager.test.ts +134 -2
- package/src/__tests__/adapters/ClaudeCodeAdapter.test.ts +188 -32
- package/src/__tests__/adapters/CodexAdapter.test.ts +123 -3
- package/src/__tests__/adapters/GeminiCliAdapter.test.ts +133 -0
- package/src/__tests__/utils/ClaudeSessionParser.test.ts +195 -0
- package/src/__tests__/utils/process.test.ts +27 -27
- package/src/__tests__/utils/session.test.ts +79 -43
- package/src/adapters/AgentAdapter.ts +76 -0
- package/src/adapters/ClaudeCodeAdapter.ts +82 -356
- package/src/adapters/CodexAdapter.ts +126 -8
- package/src/adapters/GeminiCliAdapter.ts +102 -7
- package/src/index.ts +9 -1
- package/src/terminal/TerminalFocusManager.ts +35 -26
- package/src/terminal/TtyWriter.ts +1 -11
- package/src/utils/ClaudeSessionParser.ts +437 -0
- package/src/utils/applescript.ts +10 -0
- package/src/utils/process.ts +21 -24
- package/src/utils/session.ts +86 -45
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { ConversationMessage } from '../adapters/AgentAdapter';
|
|
2
|
+
import { AgentStatus } from '../adapters/AgentAdapter';
|
|
3
|
+
/**
|
|
4
|
+
* Content block within a Claude Code JSONL message entry.
|
|
5
|
+
* Handles text, tool_use, and tool_result block types.
|
|
6
|
+
*/
|
|
7
|
+
export interface ContentBlock {
|
|
8
|
+
type?: string;
|
|
9
|
+
text?: string;
|
|
10
|
+
content?: string;
|
|
11
|
+
name?: string;
|
|
12
|
+
input?: Record<string, unknown>;
|
|
13
|
+
tool_use_id?: string;
|
|
14
|
+
is_error?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A single line entry in a Claude Code session JSONL file.
|
|
18
|
+
*
|
|
19
|
+
* Each line is an independent JSON object with a type discriminator:
|
|
20
|
+
* - "user" / "assistant" / "system" — conversation turns
|
|
21
|
+
* - "progress" / "thinking" — intermediate agent state
|
|
22
|
+
* - "last-prompt" / "file-history-snapshot" — metadata (not conversation state)
|
|
23
|
+
*/
|
|
24
|
+
export interface SessionEntry {
|
|
25
|
+
type?: string;
|
|
26
|
+
timestamp?: string;
|
|
27
|
+
cwd?: string;
|
|
28
|
+
message?: {
|
|
29
|
+
content?: string | ContentBlock[];
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Parsed session state extracted from a JSONL file.
|
|
34
|
+
* Aggregates data from all entries into a single summary.
|
|
35
|
+
*/
|
|
36
|
+
export interface ClaudeSession {
|
|
37
|
+
sessionId: string;
|
|
38
|
+
projectPath: string;
|
|
39
|
+
lastCwd?: string;
|
|
40
|
+
sessionStart: Date;
|
|
41
|
+
lastActive: Date;
|
|
42
|
+
lastEntryType?: string;
|
|
43
|
+
isInterrupted: boolean;
|
|
44
|
+
lastUserMessage?: string;
|
|
45
|
+
/** First meaningful user prompt in the session (post noise filter) */
|
|
46
|
+
firstUserMessage?: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Parses Claude Code session JSONL files into structured data.
|
|
50
|
+
*
|
|
51
|
+
* Session files live at ~/.claude/projects/<encoded-cwd>/<session-id>.jsonl
|
|
52
|
+
* and contain one JSON object per line, each representing a conversation
|
|
53
|
+
* event (user turn, assistant response, tool call, etc.).
|
|
54
|
+
*/
|
|
55
|
+
export declare class ClaudeSessionParser {
|
|
56
|
+
/**
|
|
57
|
+
* Parse a session JSONL file into a ClaudeSession summary.
|
|
58
|
+
*
|
|
59
|
+
* Iterates all lines to extract: session start time (from first entry),
|
|
60
|
+
* last activity timestamp, last entry type (for status), whether the
|
|
61
|
+
* session was interrupted, and the last meaningful user message.
|
|
62
|
+
*
|
|
63
|
+
* Returns null if the file is unreadable or empty.
|
|
64
|
+
*/
|
|
65
|
+
readSession(filePath: string, projectPath: string): ClaudeSession | null;
|
|
66
|
+
/**
|
|
67
|
+
* Determine agent status from parsed session state.
|
|
68
|
+
*
|
|
69
|
+
* Status mapping:
|
|
70
|
+
* - "user" + interrupted → WAITING (agent finished, awaiting new input)
|
|
71
|
+
* - "user" + not interrupted → RUNNING (agent is processing)
|
|
72
|
+
* - "progress" / "thinking" → RUNNING
|
|
73
|
+
* - "assistant" → WAITING (agent responded, awaiting user)
|
|
74
|
+
* - "system" → IDLE
|
|
75
|
+
*/
|
|
76
|
+
determineStatus(session: ClaudeSession): AgentStatus;
|
|
77
|
+
/**
|
|
78
|
+
* Read the full conversation from a session JSONL file.
|
|
79
|
+
*
|
|
80
|
+
* Default mode returns only text content from user/assistant/system messages.
|
|
81
|
+
* Verbose mode also includes tool_use and tool_result blocks.
|
|
82
|
+
*/
|
|
83
|
+
getConversation(sessionFilePath: string, options?: {
|
|
84
|
+
verbose?: boolean;
|
|
85
|
+
}): ConversationMessage[];
|
|
86
|
+
/**
|
|
87
|
+
* Parse session start time from the first JSONL line.
|
|
88
|
+
*
|
|
89
|
+
* Claude Code may emit a "file-history-snapshot" as the first entry,
|
|
90
|
+
* which stores its timestamp inside "snapshot.timestamp" rather than
|
|
91
|
+
* at the root level.
|
|
92
|
+
*/
|
|
93
|
+
private parseSessionStart;
|
|
94
|
+
/**
|
|
95
|
+
* Extract meaningful text from a user message content field.
|
|
96
|
+
*
|
|
97
|
+
* Handles multiple formats:
|
|
98
|
+
* - Plain string content
|
|
99
|
+
* - Array of content blocks (extracts first text block)
|
|
100
|
+
* - Skill slash-commands (<command-message> tags)
|
|
101
|
+
* - Expanded skill content (extracts ARGUMENTS line)
|
|
102
|
+
* - Filters noise messages (interruptions, tool loaded, session continued)
|
|
103
|
+
*/
|
|
104
|
+
private extractUserMessageText;
|
|
105
|
+
/**
|
|
106
|
+
* Parse a <command-message> string into "/command args" format.
|
|
107
|
+
*/
|
|
108
|
+
private parseCommandMessage;
|
|
109
|
+
/**
|
|
110
|
+
* Extract displayable content from a message content field for conversation output.
|
|
111
|
+
*/
|
|
112
|
+
private extractConversationContent;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=ClaudeSessionParser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClaudeSessionParser.d.ts","sourceRoot":"","sources":["../../src/utils/ClaudeSessionParser.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD;;;GAGG;AACH,MAAM,WAAW,YAAY;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE;QACN,OAAO,CAAC,EAAE,MAAM,GAAG,YAAY,EAAE,CAAC;KACrC,CAAC;CACL;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,IAAI,CAAC;IACnB,UAAU,EAAE,IAAI,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAKD;;;;;;GAMG;AACH,qBAAa,mBAAmB;IAC5B;;;;;;;;OAQG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IAmFxE;;;;;;;;;OASG;IACH,eAAe,CAAC,OAAO,EAAE,aAAa,GAAG,WAAW;IA6BpD;;;;;OAKG;IACH,eAAe,CAAC,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,mBAAmB,EAAE;IAiDhG;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IAiBzB;;;;;;;;;OASG;IACH,OAAO,CAAC,sBAAsB;IAwC9B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAW3B;;OAEG;IACH,OAAO,CAAC,0BAA0B;CAmCrC"}
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ClaudeSessionParser = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const AgentAdapter_1 = require("../adapters/AgentAdapter");
|
|
40
|
+
/** Entry types that are metadata, not conversation state. */
|
|
41
|
+
const METADATA_ENTRY_TYPES = new Set(['last-prompt', 'file-history-snapshot']);
|
|
42
|
+
/**
|
|
43
|
+
* Parses Claude Code session JSONL files into structured data.
|
|
44
|
+
*
|
|
45
|
+
* Session files live at ~/.claude/projects/<encoded-cwd>/<session-id>.jsonl
|
|
46
|
+
* and contain one JSON object per line, each representing a conversation
|
|
47
|
+
* event (user turn, assistant response, tool call, etc.).
|
|
48
|
+
*/
|
|
49
|
+
class ClaudeSessionParser {
|
|
50
|
+
/**
|
|
51
|
+
* Parse a session JSONL file into a ClaudeSession summary.
|
|
52
|
+
*
|
|
53
|
+
* Iterates all lines to extract: session start time (from first entry),
|
|
54
|
+
* last activity timestamp, last entry type (for status), whether the
|
|
55
|
+
* session was interrupted, and the last meaningful user message.
|
|
56
|
+
*
|
|
57
|
+
* Returns null if the file is unreadable or empty.
|
|
58
|
+
*/
|
|
59
|
+
readSession(filePath, projectPath) {
|
|
60
|
+
const sessionId = path.basename(filePath, '.jsonl');
|
|
61
|
+
let content;
|
|
62
|
+
try {
|
|
63
|
+
content = fs.readFileSync(filePath, 'utf-8');
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
const allLines = content.trim().split('\n');
|
|
69
|
+
if (allLines.length === 0) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
const sessionStart = this.parseSessionStart(allLines[0]);
|
|
73
|
+
let lastEntryType;
|
|
74
|
+
let lastActive;
|
|
75
|
+
let lastCwd;
|
|
76
|
+
let isInterrupted = false;
|
|
77
|
+
let lastUserMessage;
|
|
78
|
+
let firstUserMessage;
|
|
79
|
+
for (const line of allLines) {
|
|
80
|
+
try {
|
|
81
|
+
const entry = JSON.parse(line);
|
|
82
|
+
if (entry.timestamp) {
|
|
83
|
+
const ts = new Date(entry.timestamp);
|
|
84
|
+
if (!Number.isNaN(ts.getTime())) {
|
|
85
|
+
lastActive = ts;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (typeof entry.cwd === 'string' && entry.cwd.trim().length > 0) {
|
|
89
|
+
lastCwd = entry.cwd;
|
|
90
|
+
}
|
|
91
|
+
if (entry.type && !METADATA_ENTRY_TYPES.has(entry.type)) {
|
|
92
|
+
lastEntryType = entry.type;
|
|
93
|
+
if (entry.type === 'user') {
|
|
94
|
+
const msgContent = entry.message?.content;
|
|
95
|
+
isInterrupted =
|
|
96
|
+
Array.isArray(msgContent) &&
|
|
97
|
+
msgContent.some((c) => (c.type === 'text' &&
|
|
98
|
+
c.text?.includes('[Request interrupted')) ||
|
|
99
|
+
(c.type === 'tool_result' &&
|
|
100
|
+
c.content?.includes('[Request interrupted')));
|
|
101
|
+
const text = this.extractUserMessageText(msgContent);
|
|
102
|
+
if (text) {
|
|
103
|
+
lastUserMessage = text;
|
|
104
|
+
if (!firstUserMessage) {
|
|
105
|
+
firstUserMessage = text;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
isInterrupted = false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
sessionId,
|
|
120
|
+
projectPath: projectPath || lastCwd || '',
|
|
121
|
+
lastCwd,
|
|
122
|
+
sessionStart: sessionStart || lastActive || new Date(),
|
|
123
|
+
lastActive: lastActive || new Date(),
|
|
124
|
+
lastEntryType,
|
|
125
|
+
isInterrupted,
|
|
126
|
+
lastUserMessage,
|
|
127
|
+
firstUserMessage,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Determine agent status from parsed session state.
|
|
132
|
+
*
|
|
133
|
+
* Status mapping:
|
|
134
|
+
* - "user" + interrupted → WAITING (agent finished, awaiting new input)
|
|
135
|
+
* - "user" + not interrupted → RUNNING (agent is processing)
|
|
136
|
+
* - "progress" / "thinking" → RUNNING
|
|
137
|
+
* - "assistant" → WAITING (agent responded, awaiting user)
|
|
138
|
+
* - "system" → IDLE
|
|
139
|
+
*/
|
|
140
|
+
determineStatus(session) {
|
|
141
|
+
if (!session.lastEntryType) {
|
|
142
|
+
return AgentAdapter_1.AgentStatus.UNKNOWN;
|
|
143
|
+
}
|
|
144
|
+
if (session.lastEntryType === 'user') {
|
|
145
|
+
return session.isInterrupted
|
|
146
|
+
? AgentAdapter_1.AgentStatus.WAITING
|
|
147
|
+
: AgentAdapter_1.AgentStatus.RUNNING;
|
|
148
|
+
}
|
|
149
|
+
if (session.lastEntryType === 'progress' ||
|
|
150
|
+
session.lastEntryType === 'thinking') {
|
|
151
|
+
return AgentAdapter_1.AgentStatus.RUNNING;
|
|
152
|
+
}
|
|
153
|
+
if (session.lastEntryType === 'assistant') {
|
|
154
|
+
return AgentAdapter_1.AgentStatus.WAITING;
|
|
155
|
+
}
|
|
156
|
+
if (session.lastEntryType === 'system') {
|
|
157
|
+
return AgentAdapter_1.AgentStatus.IDLE;
|
|
158
|
+
}
|
|
159
|
+
return AgentAdapter_1.AgentStatus.UNKNOWN;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Read the full conversation from a session JSONL file.
|
|
163
|
+
*
|
|
164
|
+
* Default mode returns only text content from user/assistant/system messages.
|
|
165
|
+
* Verbose mode also includes tool_use and tool_result blocks.
|
|
166
|
+
*/
|
|
167
|
+
getConversation(sessionFilePath, options) {
|
|
168
|
+
const verbose = options?.verbose ?? false;
|
|
169
|
+
let content;
|
|
170
|
+
try {
|
|
171
|
+
content = fs.readFileSync(sessionFilePath, 'utf-8');
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
const lines = content.trim().split('\n');
|
|
177
|
+
const messages = [];
|
|
178
|
+
for (const line of lines) {
|
|
179
|
+
let entry;
|
|
180
|
+
try {
|
|
181
|
+
entry = JSON.parse(line);
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const entryType = entry.type;
|
|
187
|
+
if (!entryType || METADATA_ENTRY_TYPES.has(entryType))
|
|
188
|
+
continue;
|
|
189
|
+
if (entryType === 'progress' || entryType === 'thinking')
|
|
190
|
+
continue;
|
|
191
|
+
let role;
|
|
192
|
+
if (entryType === 'user') {
|
|
193
|
+
role = 'user';
|
|
194
|
+
}
|
|
195
|
+
else if (entryType === 'assistant') {
|
|
196
|
+
role = 'assistant';
|
|
197
|
+
}
|
|
198
|
+
else if (entryType === 'system') {
|
|
199
|
+
role = 'system';
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const text = this.extractConversationContent(entry.message?.content, role, verbose);
|
|
205
|
+
if (!text)
|
|
206
|
+
continue;
|
|
207
|
+
messages.push({
|
|
208
|
+
role,
|
|
209
|
+
content: text,
|
|
210
|
+
timestamp: entry.timestamp,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
return messages;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Parse session start time from the first JSONL line.
|
|
217
|
+
*
|
|
218
|
+
* Claude Code may emit a "file-history-snapshot" as the first entry,
|
|
219
|
+
* which stores its timestamp inside "snapshot.timestamp" rather than
|
|
220
|
+
* at the root level.
|
|
221
|
+
*/
|
|
222
|
+
parseSessionStart(firstLine) {
|
|
223
|
+
try {
|
|
224
|
+
const firstEntry = JSON.parse(firstLine);
|
|
225
|
+
const rawTs = firstEntry.timestamp || firstEntry.snapshot?.timestamp;
|
|
226
|
+
if (rawTs) {
|
|
227
|
+
const ts = new Date(rawTs);
|
|
228
|
+
if (!Number.isNaN(ts.getTime())) {
|
|
229
|
+
return ts;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
/* malformed first line */
|
|
235
|
+
}
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Extract meaningful text from a user message content field.
|
|
240
|
+
*
|
|
241
|
+
* Handles multiple formats:
|
|
242
|
+
* - Plain string content
|
|
243
|
+
* - Array of content blocks (extracts first text block)
|
|
244
|
+
* - Skill slash-commands (<command-message> tags)
|
|
245
|
+
* - Expanded skill content (extracts ARGUMENTS line)
|
|
246
|
+
* - Filters noise messages (interruptions, tool loaded, session continued)
|
|
247
|
+
*/
|
|
248
|
+
extractUserMessageText(content) {
|
|
249
|
+
if (!content) {
|
|
250
|
+
return undefined;
|
|
251
|
+
}
|
|
252
|
+
let raw;
|
|
253
|
+
if (typeof content === 'string') {
|
|
254
|
+
raw = content.trim();
|
|
255
|
+
}
|
|
256
|
+
else if (Array.isArray(content)) {
|
|
257
|
+
for (const block of content) {
|
|
258
|
+
if (block.type === 'text' && block.text?.trim()) {
|
|
259
|
+
raw = block.text.trim();
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (!raw) {
|
|
265
|
+
return undefined;
|
|
266
|
+
}
|
|
267
|
+
if (raw.startsWith('<command-message>')) {
|
|
268
|
+
return this.parseCommandMessage(raw);
|
|
269
|
+
}
|
|
270
|
+
if (raw.startsWith('Base directory for this skill:')) {
|
|
271
|
+
const argsMatch = raw.match(/\nARGUMENTS:\s*(.+)/);
|
|
272
|
+
return argsMatch?.[1]?.trim() || undefined;
|
|
273
|
+
}
|
|
274
|
+
if (isNoiseMessage(raw)) {
|
|
275
|
+
return undefined;
|
|
276
|
+
}
|
|
277
|
+
return raw;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Parse a <command-message> string into "/command args" format.
|
|
281
|
+
*/
|
|
282
|
+
parseCommandMessage(raw) {
|
|
283
|
+
const nameMatch = raw.match(/<command-name>([^<]+)<\/command-name>/);
|
|
284
|
+
const argsMatch = raw.match(/<command-args>([^<]+)<\/command-args>/);
|
|
285
|
+
const name = nameMatch?.[1]?.trim();
|
|
286
|
+
if (!name) {
|
|
287
|
+
return undefined;
|
|
288
|
+
}
|
|
289
|
+
const args = argsMatch?.[1]?.trim();
|
|
290
|
+
return args ? `${name} ${args}` : name;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Extract displayable content from a message content field for conversation output.
|
|
294
|
+
*/
|
|
295
|
+
extractConversationContent(content, role, verbose) {
|
|
296
|
+
if (!content)
|
|
297
|
+
return undefined;
|
|
298
|
+
if (typeof content === 'string') {
|
|
299
|
+
const cleaned = stripHarnessTags(content);
|
|
300
|
+
if (role === 'user' && isNoiseMessage(cleaned))
|
|
301
|
+
return undefined;
|
|
302
|
+
return cleaned || undefined;
|
|
303
|
+
}
|
|
304
|
+
if (!Array.isArray(content))
|
|
305
|
+
return undefined;
|
|
306
|
+
const parts = [];
|
|
307
|
+
for (const block of content) {
|
|
308
|
+
if (block.type === 'text' && block.text?.trim()) {
|
|
309
|
+
const cleaned = stripHarnessTags(block.text);
|
|
310
|
+
if (!cleaned)
|
|
311
|
+
continue;
|
|
312
|
+
if (role === 'user' && isNoiseMessage(cleaned))
|
|
313
|
+
continue;
|
|
314
|
+
parts.push(cleaned);
|
|
315
|
+
}
|
|
316
|
+
else if (block.type === 'tool_use' && verbose) {
|
|
317
|
+
const inputSummary = block.input?.file_path || block.input?.pattern || block.input?.command || '';
|
|
318
|
+
parts.push(`[Tool: ${block.name}]${inputSummary ? ' ' + inputSummary : ''}`);
|
|
319
|
+
}
|
|
320
|
+
else if (block.type === 'tool_result' && verbose) {
|
|
321
|
+
const truncated = truncateToolResult(block.content || '');
|
|
322
|
+
const prefix = block.is_error ? '[Tool Error]' : '[Tool Result]';
|
|
323
|
+
parts.push(`${prefix} ${truncated}`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return parts.length > 0 ? parts.join('\n') : undefined;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
exports.ClaudeSessionParser = ClaudeSessionParser;
|
|
330
|
+
/**
|
|
331
|
+
* Tags whose entire block (including content) should be dropped — they are
|
|
332
|
+
* harness-injected prompt context (system reminders, hook output, command
|
|
333
|
+
* stdout), not meaningful conversation content.
|
|
334
|
+
*/
|
|
335
|
+
const HARNESS_DROP_TAGS = [
|
|
336
|
+
'system-reminder',
|
|
337
|
+
'local-command-stdout',
|
|
338
|
+
'local-command-stderr',
|
|
339
|
+
'user-prompt-submit-hook',
|
|
340
|
+
'command-stdout',
|
|
341
|
+
'command-stderr',
|
|
342
|
+
'bash-input',
|
|
343
|
+
'bash-stdout',
|
|
344
|
+
'bash-stderr',
|
|
345
|
+
'command-message',
|
|
346
|
+
];
|
|
347
|
+
const HARNESS_DROP_RE = new RegExp(`<(${HARNESS_DROP_TAGS.join('|')})>[\\s\\S]*?</\\1>`, 'g');
|
|
348
|
+
const COMMAND_INVOCATION_RE = /<command-name>([^<]+)<\/command-name>(?:\s*<command-args>([\s\S]*?)<\/command-args>)?/g;
|
|
349
|
+
/**
|
|
350
|
+
* Remove harness-injected XML blocks from message text and collapse
|
|
351
|
+
* <command-name>/<command-args> pairs into a "/name args" shorthand.
|
|
352
|
+
*
|
|
353
|
+
* Returns the cleaned, trimmed text. Returns an empty string if nothing
|
|
354
|
+
* survives stripping.
|
|
355
|
+
*/
|
|
356
|
+
function stripHarnessTags(text) {
|
|
357
|
+
let out = text.replace(HARNESS_DROP_RE, '');
|
|
358
|
+
out = out.replace(COMMAND_INVOCATION_RE, (_match, rawName, rawArgs) => {
|
|
359
|
+
const name = rawName.trim();
|
|
360
|
+
const args = rawArgs?.trim();
|
|
361
|
+
return args ? `${name} ${args}` : name;
|
|
362
|
+
});
|
|
363
|
+
return out.replace(/\n{3,}/g, '\n\n').trim();
|
|
364
|
+
}
|
|
365
|
+
/** Check if a message is noise (not a meaningful user intent). */
|
|
366
|
+
function isNoiseMessage(text) {
|
|
367
|
+
return (text.startsWith('[Request interrupted') ||
|
|
368
|
+
text === 'Tool loaded.' ||
|
|
369
|
+
text.startsWith('This session is being continued'));
|
|
370
|
+
}
|
|
371
|
+
function truncateToolResult(content, maxLength = 200) {
|
|
372
|
+
const firstLine = content.split('\n')[0] || '';
|
|
373
|
+
if (firstLine.length <= maxLength)
|
|
374
|
+
return firstLine;
|
|
375
|
+
return firstLine.slice(0, maxLength - 3) + '...';
|
|
376
|
+
}
|
|
377
|
+
//# sourceMappingURL=ClaudeSessionParser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClaudeSessionParser.js","sourceRoot":"","sources":["../../src/utils/ClaudeSessionParser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"applescript.d.ts","sourceRoot":"","sources":["../../src/utils/applescript.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKtD"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.escapeAppleScript = escapeAppleScript;
|
|
4
|
+
/**
|
|
5
|
+
* Escape a string for safe use inside an AppleScript double-quoted string.
|
|
6
|
+
* Backslashes, double quotes, and newlines must be escaped.
|
|
7
|
+
*/
|
|
8
|
+
function escapeAppleScript(text) {
|
|
9
|
+
return text
|
|
10
|
+
.replace(/\\/g, '\\\\')
|
|
11
|
+
.replace(/"/g, '\\"')
|
|
12
|
+
.replace(/\r\n|\r|\n/g, '\\n');
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=applescript.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"applescript.js","sourceRoot":"","sources":["../../src/utils/applescript.ts"],"names":[],"mappings":";;AAIA,8CAKC;AATD;;;GAGG;AACH,SAAgB,iBAAiB,CAAC,IAAY;IAC1C,OAAO,IAAI;SACN,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;AACvC,CAAC"}
|
package/dist/utils/process.d.ts
CHANGED
|
@@ -2,15 +2,14 @@
|
|
|
2
2
|
* Process Detection Utilities
|
|
3
3
|
*
|
|
4
4
|
* Shared shell command wrappers for detecting and inspecting running processes.
|
|
5
|
-
* All
|
|
5
|
+
* All execFileSync calls for process data live here — adapters must not call execFileSync directly.
|
|
6
6
|
*/
|
|
7
7
|
import type { ProcessInfo } from '../adapters/AgentAdapter';
|
|
8
8
|
/**
|
|
9
9
|
* List running processes matching an agent executable name.
|
|
10
10
|
*
|
|
11
|
-
* Uses `ps aux
|
|
12
|
-
*
|
|
13
|
-
* `claude-helper`, `vscode-claude-extension`, or the grep process itself).
|
|
11
|
+
* Uses `ps aux` then filters in JS for exact executable basename match.
|
|
12
|
+
* This avoids shell pipelines and string interpolation.
|
|
14
13
|
*
|
|
15
14
|
* Returned ProcessInfo has pid, command, tty populated.
|
|
16
15
|
* cwd and startTime are NOT populated — call enrichProcesses() to fill them.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../src/utils/process.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D
|
|
1
|
+
{"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../src/utils/process.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,EAAE,CA6CrE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAuCvE;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAkC3E;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAavE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAYjD"}
|
package/dist/utils/process.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Process Detection Utilities
|
|
4
4
|
*
|
|
5
5
|
* Shared shell command wrappers for detecting and inspecting running processes.
|
|
6
|
-
* All
|
|
6
|
+
* All execFileSync calls for process data live here — adapters must not call execFileSync directly.
|
|
7
7
|
*/
|
|
8
8
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
9
|
if (k2 === undefined) k2 = k;
|
|
@@ -49,9 +49,8 @@ const child_process_1 = require("child_process");
|
|
|
49
49
|
/**
|
|
50
50
|
* List running processes matching an agent executable name.
|
|
51
51
|
*
|
|
52
|
-
* Uses `ps aux
|
|
53
|
-
*
|
|
54
|
-
* `claude-helper`, `vscode-claude-extension`, or the grep process itself).
|
|
52
|
+
* Uses `ps aux` then filters in JS for exact executable basename match.
|
|
53
|
+
* This avoids shell pipelines and string interpolation.
|
|
55
54
|
*
|
|
56
55
|
* Returned ProcessInfo has pid, command, tty populated.
|
|
57
56
|
* cwd and startTime are NOT populated — call enrichProcesses() to fill them.
|
|
@@ -62,9 +61,8 @@ function listAgentProcesses(namePattern) {
|
|
|
62
61
|
return [];
|
|
63
62
|
}
|
|
64
63
|
try {
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
const output = (0, child_process_1.execSync)(`ps aux | grep -i '${escapedPattern}'`, { encoding: 'utf-8' });
|
|
64
|
+
const output = (0, child_process_1.execFileSync)('ps', ['aux'], { encoding: 'utf-8' });
|
|
65
|
+
const lowerPattern = namePattern.toLowerCase();
|
|
68
66
|
const processes = [];
|
|
69
67
|
for (const line of output.trim().split('\n')) {
|
|
70
68
|
if (!line.trim())
|
|
@@ -77,10 +75,10 @@ function listAgentProcesses(namePattern) {
|
|
|
77
75
|
continue;
|
|
78
76
|
const tty = parts[6];
|
|
79
77
|
const command = parts.slice(10).join(' ');
|
|
80
|
-
//
|
|
78
|
+
// Check that the executable basename matches exactly
|
|
81
79
|
const executable = command.trim().split(/\s+/)[0] || '';
|
|
82
80
|
const base = path.basename(executable).toLowerCase();
|
|
83
|
-
if (base !==
|
|
81
|
+
if (base !== lowerPattern && base !== `${lowerPattern}.exe`) {
|
|
84
82
|
continue;
|
|
85
83
|
}
|
|
86
84
|
const ttyShort = tty.startsWith('/dev/') ? tty.slice(5) : tty;
|
|
@@ -108,7 +106,7 @@ function batchGetProcessCwds(pids) {
|
|
|
108
106
|
if (pids.length === 0)
|
|
109
107
|
return result;
|
|
110
108
|
try {
|
|
111
|
-
const output = (0, child_process_1.
|
|
109
|
+
const output = (0, child_process_1.execFileSync)('lsof', ['-a', '-d', 'cwd', '-Fn', '-p', pids.join(',')], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] });
|
|
112
110
|
// lsof output format: p{PID}\nn{path}\np{PID}\nn{path}...
|
|
113
111
|
let currentPid = null;
|
|
114
112
|
for (const line of output.trim().split('\n')) {
|
|
@@ -125,7 +123,7 @@ function batchGetProcessCwds(pids) {
|
|
|
125
123
|
// Try per-PID fallback with pwdx (Linux)
|
|
126
124
|
for (const pid of pids) {
|
|
127
125
|
try {
|
|
128
|
-
const output = (0, child_process_1.
|
|
126
|
+
const output = (0, child_process_1.execFileSync)('pwdx', [String(pid)], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] });
|
|
129
127
|
const match = output.match(/^\d+:\s*(.+)$/);
|
|
130
128
|
if (match) {
|
|
131
129
|
result.set(pid, match[1].trim());
|
|
@@ -150,7 +148,7 @@ function batchGetProcessStartTimes(pids) {
|
|
|
150
148
|
if (pids.length === 0)
|
|
151
149
|
return result;
|
|
152
150
|
try {
|
|
153
|
-
const output = (0, child_process_1.
|
|
151
|
+
const output = (0, child_process_1.execFileSync)('ps', ['-o', 'pid=,lstart=', '-p', pids.join(',')], { encoding: 'utf-8' });
|
|
154
152
|
for (const rawLine of output.split('\n')) {
|
|
155
153
|
const line = rawLine.trim();
|
|
156
154
|
if (!line)
|
|
@@ -199,9 +197,7 @@ function enrichProcesses(processes) {
|
|
|
199
197
|
*/
|
|
200
198
|
function getProcessTty(pid) {
|
|
201
199
|
try {
|
|
202
|
-
const output = (0, child_process_1.
|
|
203
|
-
encoding: 'utf-8',
|
|
204
|
-
});
|
|
200
|
+
const output = (0, child_process_1.execFileSync)('ps', ['-p', String(pid), '-o', 'tty='], { encoding: 'utf-8' });
|
|
205
201
|
const tty = output.trim();
|
|
206
202
|
return tty.startsWith('/dev/') ? tty.slice(5) : tty;
|
|
207
203
|
}
|