agent-insights 0.0.13 → 0.0.18
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/cli.js +384 -62
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +359 -56
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
type AgentName = "claude-code" | "cursor" | "opencode" | "codex" | "unknown";
|
|
1
|
+
type AgentName = "claude-code" | "cursor" | "opencode" | "codex" | "pi" | "unknown";
|
|
2
2
|
type AgentEventType = "session.start" | "session.end" | "user.prompt.submit" | "tool.start" | "tool.end" | "tool.failure" | "permission.request" | "permission.denied" | "subagent.start" | "subagent.end" | "agent.stop" | "notification" | "context.compact.start" | "context.compact.end" | "error";
|
|
3
3
|
type AgentOutcome = "success" | "error" | "cancelled" | "unknown";
|
|
4
4
|
type AgentEvent = {
|
|
@@ -82,7 +82,7 @@ type AgentInsightsConfig = {
|
|
|
82
82
|
exporter: ExporterConfig;
|
|
83
83
|
transcriptStore: TranscriptStoreConfig;
|
|
84
84
|
sessionAnalysis: SessionAnalysisConfig;
|
|
85
|
-
agents: Record<"claudeCode" | "cursor", AgentConfig>;
|
|
85
|
+
agents: Record<"claudeCode" | "cursor" | "codex" | "pi", AgentConfig>;
|
|
86
86
|
privacy: {
|
|
87
87
|
capturePrompts: false;
|
|
88
88
|
captureMessages: false;
|
|
@@ -94,7 +94,7 @@ declare function defaultConfig(): AgentInsightsConfig;
|
|
|
94
94
|
declare function loadConfig(): Promise<AgentInsightsConfig | undefined>;
|
|
95
95
|
declare function saveConfig(cfg: AgentInsightsConfig): Promise<void>;
|
|
96
96
|
|
|
97
|
-
type AgentId = "claude-code" | "cursor";
|
|
97
|
+
type AgentId = "claude-code" | "cursor" | "codex" | "pi";
|
|
98
98
|
type HookPayload = {
|
|
99
99
|
/** Native event name as emitted by the agent (e.g. `SessionStart`). */
|
|
100
100
|
event: string;
|
package/dist/index.js
CHANGED
|
@@ -95,6 +95,33 @@ function sanitizeInner(value) {
|
|
|
95
95
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
96
96
|
import { homedir } from "os";
|
|
97
97
|
import { dirname, join } from "path";
|
|
98
|
+
|
|
99
|
+
// src/util/hook-command.ts
|
|
100
|
+
import { realpathSync } from "fs";
|
|
101
|
+
function resolve() {
|
|
102
|
+
const node = process.execPath;
|
|
103
|
+
let script = process.argv[1] ?? "";
|
|
104
|
+
try {
|
|
105
|
+
script = realpathSync(script);
|
|
106
|
+
} catch {
|
|
107
|
+
}
|
|
108
|
+
return { node, script };
|
|
109
|
+
}
|
|
110
|
+
function quote(s) {
|
|
111
|
+
if (s.length === 0) return "''";
|
|
112
|
+
if (!/[\s'"\\$`<>|&;()*?#~]/.test(s)) return s;
|
|
113
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
114
|
+
}
|
|
115
|
+
function hookCommandString(event) {
|
|
116
|
+
const { node, script } = resolve();
|
|
117
|
+
return `${quote(node)} ${quote(script)} hook ${event}`;
|
|
118
|
+
}
|
|
119
|
+
function hookCommandArgv(event) {
|
|
120
|
+
const { node, script } = resolve();
|
|
121
|
+
return { node, script, args: ["hook", event] };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// src/adapters/claude-code.ts
|
|
98
125
|
var HOOK_COMMAND_NAME = "agent-insights";
|
|
99
126
|
var HOOK_MAP = {
|
|
100
127
|
SessionStart: "session.start",
|
|
@@ -189,7 +216,8 @@ function resolveClaudePath(repoRoot, scope) {
|
|
|
189
216
|
}
|
|
190
217
|
function isAgentInsightsCommand(cmd) {
|
|
191
218
|
if (!cmd) return false;
|
|
192
|
-
|
|
219
|
+
if (cmd.startsWith(`${HOOK_COMMAND_NAME} hook`)) return true;
|
|
220
|
+
return /\bagent-insights\b.*\bcli\.js\b.*\bhook\b/.test(cmd);
|
|
193
221
|
}
|
|
194
222
|
function isHookCommand(value) {
|
|
195
223
|
if (!value || typeof value !== "object") return false;
|
|
@@ -232,7 +260,7 @@ function removeAgentInsightsMatchers(matchers) {
|
|
|
232
260
|
return result;
|
|
233
261
|
}
|
|
234
262
|
function addAgentInsightsHook(matchers, event) {
|
|
235
|
-
const command =
|
|
263
|
+
const command = hookCommandString(event);
|
|
236
264
|
const entry = { type: "command", command };
|
|
237
265
|
for (const group of matchers) {
|
|
238
266
|
if (group.matcher === "") {
|
|
@@ -257,12 +285,163 @@ function asString(v) {
|
|
|
257
285
|
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
258
286
|
}
|
|
259
287
|
|
|
260
|
-
// src/adapters/
|
|
288
|
+
// src/adapters/codex.ts
|
|
261
289
|
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
262
290
|
import { homedir as homedir2 } from "os";
|
|
263
291
|
import { dirname as dirname2, join as join2 } from "path";
|
|
264
292
|
var HOOK_COMMAND_NAME2 = "agent-insights";
|
|
265
293
|
var HOOK_MAP2 = {
|
|
294
|
+
SessionStart: "session.start",
|
|
295
|
+
UserPromptSubmit: "user.prompt.submit",
|
|
296
|
+
PreToolUse: "tool.start",
|
|
297
|
+
PostToolUse: "tool.end",
|
|
298
|
+
PermissionRequest: "permission.request",
|
|
299
|
+
Stop: "session.end"
|
|
300
|
+
};
|
|
301
|
+
var CODEX_HOOK_EVENTS = Object.keys(HOOK_MAP2);
|
|
302
|
+
var codexAdapter = {
|
|
303
|
+
id: "codex",
|
|
304
|
+
agent: "codex",
|
|
305
|
+
label: "Codex",
|
|
306
|
+
settingsFile: ".codex/hooks.json",
|
|
307
|
+
map(payload) {
|
|
308
|
+
const type = HOOK_MAP2[payload.event];
|
|
309
|
+
if (!type) return void 0;
|
|
310
|
+
const data = payload.data ?? {};
|
|
311
|
+
const sessionId = asString2(data["session_id"]) ?? asString2(data["sessionId"]);
|
|
312
|
+
const toolName = asString2(data["tool_name"]) ?? asString2(data["toolName"]) ?? asString2(data["tool"]?.["name"]);
|
|
313
|
+
const transcriptPath = asString2(data["transcript_path"]) ?? asString2(data["transcriptPath"]);
|
|
314
|
+
return {
|
|
315
|
+
type,
|
|
316
|
+
...sessionId !== void 0 ? { sessionId } : {},
|
|
317
|
+
...toolName !== void 0 ? { toolName } : {},
|
|
318
|
+
...transcriptPath !== void 0 ? { transcriptPath } : {}
|
|
319
|
+
};
|
|
320
|
+
},
|
|
321
|
+
async install(repoRoot, scope = "global") {
|
|
322
|
+
const path = resolveCodexPath(repoRoot, scope);
|
|
323
|
+
const settings = await readJson2(path);
|
|
324
|
+
const next = { ...settings ?? {} };
|
|
325
|
+
const hooks = {};
|
|
326
|
+
for (const [event, raw] of Object.entries(next.hooks ?? {})) {
|
|
327
|
+
hooks[event] = normalizeEventHooks2(raw);
|
|
328
|
+
}
|
|
329
|
+
for (const event of CODEX_HOOK_EVENTS) {
|
|
330
|
+
const withoutInsights = removeAgentInsightsMatchers2(hooks[event] ?? []);
|
|
331
|
+
hooks[event] = addAgentInsightsHook2(withoutInsights, event);
|
|
332
|
+
}
|
|
333
|
+
next.hooks = hooks;
|
|
334
|
+
await mkdir2(dirname2(path), { recursive: true });
|
|
335
|
+
await writeFile2(path, `${JSON.stringify(next, null, 2)}
|
|
336
|
+
`, "utf8");
|
|
337
|
+
return { written: true, path };
|
|
338
|
+
},
|
|
339
|
+
async uninstall(repoRoot, scope = "global") {
|
|
340
|
+
const path = resolveCodexPath(repoRoot, scope);
|
|
341
|
+
const settings = await readJson2(path);
|
|
342
|
+
if (!settings?.hooks) return { removed: false, path };
|
|
343
|
+
const hooks = {};
|
|
344
|
+
let touched = false;
|
|
345
|
+
for (const [event, raw] of Object.entries(settings.hooks)) {
|
|
346
|
+
const before = normalizeEventHooks2(raw);
|
|
347
|
+
const after = removeAgentInsightsMatchers2(before);
|
|
348
|
+
if (after.length !== before.length || JSON.stringify(after) !== JSON.stringify(before)) {
|
|
349
|
+
touched = true;
|
|
350
|
+
}
|
|
351
|
+
if (after.length > 0) hooks[event] = after;
|
|
352
|
+
}
|
|
353
|
+
settings.hooks = hooks;
|
|
354
|
+
await writeFile2(path, `${JSON.stringify(settings, null, 2)}
|
|
355
|
+
`, "utf8");
|
|
356
|
+
return { removed: touched, path };
|
|
357
|
+
},
|
|
358
|
+
async isInstalled(repoRoot, scope = "global") {
|
|
359
|
+
const settings = await readJson2(
|
|
360
|
+
resolveCodexPath(repoRoot, scope)
|
|
361
|
+
);
|
|
362
|
+
if (!settings?.hooks) return false;
|
|
363
|
+
return Object.values(settings.hooks).some(
|
|
364
|
+
(raw) => normalizeEventHooks2(raw).some(
|
|
365
|
+
(group) => group.hooks.some((h) => isAgentInsightsCommand2(h.command))
|
|
366
|
+
)
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
function resolveCodexPath(repoRoot, scope) {
|
|
371
|
+
return scope === "global" ? join2(homedir2(), ".codex", "hooks.json") : join2(repoRoot, ".codex", "hooks.json");
|
|
372
|
+
}
|
|
373
|
+
function isAgentInsightsCommand2(cmd) {
|
|
374
|
+
if (!cmd) return false;
|
|
375
|
+
if (cmd.startsWith(`${HOOK_COMMAND_NAME2} hook`)) return true;
|
|
376
|
+
return /\bagent-insights\b.*\bcli\.js\b.*\bhook\b/.test(cmd);
|
|
377
|
+
}
|
|
378
|
+
function isHookCommand2(value) {
|
|
379
|
+
if (!value || typeof value !== "object") return false;
|
|
380
|
+
const entry = value;
|
|
381
|
+
return entry.type === "command" && typeof entry.command === "string";
|
|
382
|
+
}
|
|
383
|
+
function normalizeEventHooks2(raw) {
|
|
384
|
+
const groups = [];
|
|
385
|
+
for (const entry of raw) {
|
|
386
|
+
if (!entry || typeof entry !== "object") continue;
|
|
387
|
+
const record = entry;
|
|
388
|
+
if (Array.isArray(record.hooks)) {
|
|
389
|
+
const commands = record.hooks.filter(isHookCommand2);
|
|
390
|
+
if (commands.length === 0) continue;
|
|
391
|
+
groups.push({
|
|
392
|
+
matcher: typeof record.matcher === "string" ? record.matcher : "",
|
|
393
|
+
hooks: commands
|
|
394
|
+
});
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
if (isHookCommand2(record)) {
|
|
398
|
+
groups.push({ matcher: "", hooks: [record] });
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return groups;
|
|
402
|
+
}
|
|
403
|
+
function removeAgentInsightsMatchers2(matchers) {
|
|
404
|
+
const result = [];
|
|
405
|
+
for (const group of matchers) {
|
|
406
|
+
const hooks = group.hooks.filter(
|
|
407
|
+
(h) => !isAgentInsightsCommand2(h.command)
|
|
408
|
+
);
|
|
409
|
+
if (hooks.length > 0) result.push({ ...group, hooks });
|
|
410
|
+
}
|
|
411
|
+
return result;
|
|
412
|
+
}
|
|
413
|
+
function addAgentInsightsHook2(matchers, event) {
|
|
414
|
+
const command = hookCommandString(event);
|
|
415
|
+
const entry = { type: "command", command };
|
|
416
|
+
for (const group of matchers) {
|
|
417
|
+
if (group.matcher === "") {
|
|
418
|
+
if (!group.hooks.some((h) => h.command === command)) {
|
|
419
|
+
group.hooks.push(entry);
|
|
420
|
+
}
|
|
421
|
+
return matchers;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return [...matchers, { matcher: "", hooks: [entry] }];
|
|
425
|
+
}
|
|
426
|
+
async function readJson2(path) {
|
|
427
|
+
try {
|
|
428
|
+
const raw = await readFile2(path, "utf8");
|
|
429
|
+
return JSON.parse(raw);
|
|
430
|
+
} catch (err) {
|
|
431
|
+
if (err.code === "ENOENT") return void 0;
|
|
432
|
+
throw err;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
function asString2(v) {
|
|
436
|
+
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// src/adapters/cursor.ts
|
|
440
|
+
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
441
|
+
import { homedir as homedir3 } from "os";
|
|
442
|
+
import { dirname as dirname3, join as join3 } from "path";
|
|
443
|
+
var HOOK_COMMAND_NAME3 = "agent-insights";
|
|
444
|
+
var HOOK_MAP3 = {
|
|
266
445
|
sessionStart: "session.start",
|
|
267
446
|
sessionEnd: "session.end",
|
|
268
447
|
beforeSubmitPrompt: "user.prompt.submit",
|
|
@@ -279,19 +458,19 @@ var HOOK_MAP2 = {
|
|
|
279
458
|
afterFileEdit: "tool.end",
|
|
280
459
|
workspaceOpen: "session.start"
|
|
281
460
|
};
|
|
282
|
-
var CURSOR_HOOK_EVENTS = Object.keys(
|
|
461
|
+
var CURSOR_HOOK_EVENTS = Object.keys(HOOK_MAP3);
|
|
283
462
|
var cursorAdapter = {
|
|
284
463
|
id: "cursor",
|
|
285
464
|
agent: "cursor",
|
|
286
465
|
label: "Cursor",
|
|
287
466
|
settingsFile: ".cursor/hooks.json",
|
|
288
467
|
map(payload) {
|
|
289
|
-
const type =
|
|
468
|
+
const type = HOOK_MAP3[payload.event];
|
|
290
469
|
if (!type) return void 0;
|
|
291
470
|
const data = payload.data ?? {};
|
|
292
|
-
const sessionId =
|
|
293
|
-
const toolName =
|
|
294
|
-
const transcriptPath =
|
|
471
|
+
const sessionId = asString3(data["session_id"]) ?? asString3(data["sessionId"]) ?? asString3(data["conversation_id"]);
|
|
472
|
+
const toolName = asString3(data["tool_name"]) ?? asString3(data["toolName"]);
|
|
473
|
+
const transcriptPath = asString3(data["transcript_path"]) ?? asString3(data["transcriptPath"]) ?? asString3(process.env.CURSOR_TRANSCRIPT_PATH);
|
|
295
474
|
return {
|
|
296
475
|
type,
|
|
297
476
|
...sessionId !== void 0 ? { sessionId } : {},
|
|
@@ -301,7 +480,7 @@ var cursorAdapter = {
|
|
|
301
480
|
},
|
|
302
481
|
async install(repoRoot, scope = "global") {
|
|
303
482
|
const path = resolveCursorPath(repoRoot, scope);
|
|
304
|
-
const settings = await
|
|
483
|
+
const settings = await readJson3(path);
|
|
305
484
|
const next = {
|
|
306
485
|
version: 1,
|
|
307
486
|
...settings ?? {}
|
|
@@ -309,26 +488,26 @@ var cursorAdapter = {
|
|
|
309
488
|
const hooks = { ...next.hooks ?? {} };
|
|
310
489
|
for (const event of CURSOR_HOOK_EVENTS) {
|
|
311
490
|
const existing = (hooks[event] ?? []).filter(
|
|
312
|
-
(h) => !
|
|
491
|
+
(h) => !isAgentInsightsCommand3(h.command)
|
|
313
492
|
);
|
|
314
|
-
existing.push({ command:
|
|
493
|
+
existing.push({ command: hookCommandString(event) });
|
|
315
494
|
hooks[event] = existing;
|
|
316
495
|
}
|
|
317
496
|
next.hooks = hooks;
|
|
318
|
-
await
|
|
319
|
-
await
|
|
497
|
+
await mkdir3(dirname3(path), { recursive: true });
|
|
498
|
+
await writeFile3(path, `${JSON.stringify(next, null, 2)}
|
|
320
499
|
`, "utf8");
|
|
321
500
|
return { written: true, path };
|
|
322
501
|
},
|
|
323
502
|
async uninstall(repoRoot, scope = "global") {
|
|
324
503
|
const path = resolveCursorPath(repoRoot, scope);
|
|
325
|
-
const settings = await
|
|
504
|
+
const settings = await readJson3(path);
|
|
326
505
|
if (!settings?.hooks) return { removed: false, path };
|
|
327
506
|
const hooks = { ...settings.hooks };
|
|
328
507
|
let touched = false;
|
|
329
508
|
for (const [event, entries] of Object.entries(hooks)) {
|
|
330
509
|
const filtered = entries.filter(
|
|
331
|
-
(h) => !
|
|
510
|
+
(h) => !isAgentInsightsCommand3(h.command)
|
|
332
511
|
);
|
|
333
512
|
if (filtered.length !== entries.length) touched = true;
|
|
334
513
|
if (filtered.length === 0) {
|
|
@@ -338,44 +517,164 @@ var cursorAdapter = {
|
|
|
338
517
|
}
|
|
339
518
|
}
|
|
340
519
|
settings.hooks = hooks;
|
|
341
|
-
await
|
|
520
|
+
await writeFile3(path, `${JSON.stringify(settings, null, 2)}
|
|
342
521
|
`, "utf8");
|
|
343
522
|
return { removed: touched, path };
|
|
344
523
|
},
|
|
345
524
|
async isInstalled(repoRoot, scope = "global") {
|
|
346
|
-
const settings = await
|
|
525
|
+
const settings = await readJson3(
|
|
347
526
|
resolveCursorPath(repoRoot, scope)
|
|
348
527
|
);
|
|
349
528
|
if (!settings?.hooks) return false;
|
|
350
529
|
return Object.values(settings.hooks).some(
|
|
351
|
-
(entries) => entries.some((h) =>
|
|
530
|
+
(entries) => entries.some((h) => isAgentInsightsCommand3(h.command))
|
|
352
531
|
);
|
|
353
532
|
}
|
|
354
533
|
};
|
|
355
534
|
function resolveCursorPath(repoRoot, scope) {
|
|
356
|
-
return scope === "global" ?
|
|
535
|
+
return scope === "global" ? join3(homedir3(), ".cursor", "hooks.json") : join3(repoRoot, ".cursor", "hooks.json");
|
|
357
536
|
}
|
|
358
|
-
function
|
|
537
|
+
function isAgentInsightsCommand3(cmd) {
|
|
359
538
|
if (!cmd) return false;
|
|
360
|
-
|
|
539
|
+
if (cmd.startsWith(`${HOOK_COMMAND_NAME3} hook`)) return true;
|
|
540
|
+
return /\bagent-insights\b.*\bcli\.js\b.*\bhook\b/.test(cmd);
|
|
361
541
|
}
|
|
362
|
-
async function
|
|
542
|
+
async function readJson3(path) {
|
|
363
543
|
try {
|
|
364
|
-
const raw = await
|
|
544
|
+
const raw = await readFile3(path, "utf8");
|
|
365
545
|
return JSON.parse(raw);
|
|
366
546
|
} catch (err) {
|
|
367
547
|
if (err.code === "ENOENT") return void 0;
|
|
368
548
|
throw err;
|
|
369
549
|
}
|
|
370
550
|
}
|
|
371
|
-
function
|
|
551
|
+
function asString3(v) {
|
|
552
|
+
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// src/adapters/pi.ts
|
|
556
|
+
import { mkdir as mkdir4, readFile as readFile4, unlink, writeFile as writeFile4 } from "fs/promises";
|
|
557
|
+
import { homedir as homedir4 } from "os";
|
|
558
|
+
import { dirname as dirname4, join as join4 } from "path";
|
|
559
|
+
var EXTENSION_FILENAME = "agent-insights.ts";
|
|
560
|
+
var HOOK_MAP4 = {
|
|
561
|
+
session_start: "session.start",
|
|
562
|
+
session_shutdown: "session.end",
|
|
563
|
+
tool_call: "tool.start",
|
|
564
|
+
tool_result: "tool.end"
|
|
565
|
+
};
|
|
566
|
+
var PI_HOOK_EVENTS = Object.keys(HOOK_MAP4);
|
|
567
|
+
var EXTENSION_MARKER = "// installed-by: agent-insights";
|
|
568
|
+
function extensionSource() {
|
|
569
|
+
const { node, script } = hookCommandArgv("Placeholder");
|
|
570
|
+
return `${EXTENSION_MARKER}
|
|
571
|
+
// Generated by \`agent-insights init\`. Edit-with-caution: it's overwritten on every install.
|
|
572
|
+
|
|
573
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
574
|
+
import { spawn } from "node:child_process";
|
|
575
|
+
|
|
576
|
+
const EVENTS = ${JSON.stringify(PI_HOOK_EVENTS)} as const;
|
|
577
|
+
const NODE_BIN = ${JSON.stringify(node)};
|
|
578
|
+
const CLI_SCRIPT = ${JSON.stringify(script)};
|
|
579
|
+
|
|
580
|
+
function emit(event: string, payload: Record<string, unknown>): void {
|
|
581
|
+
try {
|
|
582
|
+
const child = spawn(NODE_BIN, [CLI_SCRIPT, "hook", event], {
|
|
583
|
+
stdio: ["pipe", "ignore", "ignore"],
|
|
584
|
+
detached: true,
|
|
585
|
+
});
|
|
586
|
+
child.on("error", () => {});
|
|
587
|
+
child.stdin.end(JSON.stringify(payload));
|
|
588
|
+
child.unref();
|
|
589
|
+
} catch {
|
|
590
|
+
// best-effort
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
export default function (pi: ExtensionAPI) {
|
|
595
|
+
for (const event of EVENTS) {
|
|
596
|
+
pi.on(event as never, async (raw: unknown, ctx: { sessionManager?: { getSessionFile?: () => string | undefined; getSessionId?: () => string | undefined } }) => {
|
|
597
|
+
const transcript_path = ctx.sessionManager?.getSessionFile?.();
|
|
598
|
+
const session_id = ctx.sessionManager?.getSessionId?.();
|
|
599
|
+
const e = (raw ?? {}) as Record<string, unknown>;
|
|
600
|
+
emit(event, {
|
|
601
|
+
...(session_id ? { session_id } : {}),
|
|
602
|
+
...(transcript_path ? { transcript_path } : {}),
|
|
603
|
+
...("toolName" in e && typeof e.toolName === "string"
|
|
604
|
+
? { tool_name: e.toolName }
|
|
605
|
+
: {}),
|
|
606
|
+
...("reason" in e ? { reason: e.reason } : {}),
|
|
607
|
+
});
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
`;
|
|
612
|
+
}
|
|
613
|
+
var piAdapter = {
|
|
614
|
+
id: "pi",
|
|
615
|
+
agent: "pi",
|
|
616
|
+
label: "Pi",
|
|
617
|
+
settingsFile: ".pi/extensions/agent-insights.ts",
|
|
618
|
+
map(payload) {
|
|
619
|
+
const type = HOOK_MAP4[payload.event];
|
|
620
|
+
if (!type) return void 0;
|
|
621
|
+
const data = payload.data ?? {};
|
|
622
|
+
const sessionId = asString4(data["session_id"]) ?? asString4(data["sessionId"]);
|
|
623
|
+
const toolName = asString4(data["tool_name"]) ?? asString4(data["toolName"]);
|
|
624
|
+
const transcriptPath = asString4(data["transcript_path"]) ?? asString4(data["transcriptPath"]);
|
|
625
|
+
return {
|
|
626
|
+
type,
|
|
627
|
+
...sessionId !== void 0 ? { sessionId } : {},
|
|
628
|
+
...toolName !== void 0 ? { toolName } : {},
|
|
629
|
+
...transcriptPath !== void 0 ? { transcriptPath } : {}
|
|
630
|
+
};
|
|
631
|
+
},
|
|
632
|
+
async install(repoRoot, scope = "global") {
|
|
633
|
+
const path = resolvePath(repoRoot, scope);
|
|
634
|
+
await mkdir4(dirname4(path), { recursive: true });
|
|
635
|
+
await writeFile4(path, extensionSource(), "utf8");
|
|
636
|
+
return { written: true, path };
|
|
637
|
+
},
|
|
638
|
+
async uninstall(repoRoot, scope = "global") {
|
|
639
|
+
const path = resolvePath(repoRoot, scope);
|
|
640
|
+
try {
|
|
641
|
+
const raw = await readFile4(path, "utf8");
|
|
642
|
+
if (!raw.includes(EXTENSION_MARKER)) {
|
|
643
|
+
return { removed: false, path };
|
|
644
|
+
}
|
|
645
|
+
await unlink(path);
|
|
646
|
+
return { removed: true, path };
|
|
647
|
+
} catch (err) {
|
|
648
|
+
if (err.code === "ENOENT") {
|
|
649
|
+
return { removed: false, path };
|
|
650
|
+
}
|
|
651
|
+
throw err;
|
|
652
|
+
}
|
|
653
|
+
},
|
|
654
|
+
async isInstalled(repoRoot, scope = "global") {
|
|
655
|
+
const path = resolvePath(repoRoot, scope);
|
|
656
|
+
try {
|
|
657
|
+
const raw = await readFile4(path, "utf8");
|
|
658
|
+
return raw.includes(EXTENSION_MARKER);
|
|
659
|
+
} catch (err) {
|
|
660
|
+
if (err.code === "ENOENT") return false;
|
|
661
|
+
throw err;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
};
|
|
665
|
+
function resolvePath(repoRoot, scope) {
|
|
666
|
+
return scope === "global" ? join4(homedir4(), ".pi", "agent", "extensions", EXTENSION_FILENAME) : join4(repoRoot, ".pi", "extensions", EXTENSION_FILENAME);
|
|
667
|
+
}
|
|
668
|
+
function asString4(v) {
|
|
372
669
|
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
373
670
|
}
|
|
374
671
|
|
|
375
672
|
// src/adapters/registry.ts
|
|
376
673
|
var adapters = {
|
|
377
674
|
"claude-code": claudeCodeAdapter,
|
|
378
|
-
cursor: cursorAdapter
|
|
675
|
+
cursor: cursorAdapter,
|
|
676
|
+
codex: codexAdapter,
|
|
677
|
+
pi: piAdapter
|
|
379
678
|
};
|
|
380
679
|
var adapterList = Object.values(adapters);
|
|
381
680
|
function getAdapter(id) {
|
|
@@ -383,8 +682,8 @@ function getAdapter(id) {
|
|
|
383
682
|
}
|
|
384
683
|
|
|
385
684
|
// src/config/config.ts
|
|
386
|
-
import { mkdir as
|
|
387
|
-
import { dirname as
|
|
685
|
+
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
|
|
686
|
+
import { dirname as dirname5 } from "path";
|
|
388
687
|
|
|
389
688
|
// src/config/analyzer.ts
|
|
390
689
|
var DEFAULT_ANALYZER_URL = "https://agent-insights.labs.vercel.dev";
|
|
@@ -393,25 +692,25 @@ function getAnalyzerUrl() {
|
|
|
393
692
|
}
|
|
394
693
|
|
|
395
694
|
// src/config/paths.ts
|
|
396
|
-
import { homedir as
|
|
397
|
-
import { join as
|
|
695
|
+
import { homedir as homedir5 } from "os";
|
|
696
|
+
import { join as join5 } from "path";
|
|
398
697
|
function configRoot() {
|
|
399
|
-
return process.env.AGENT_INSIGHTS_HOME ??
|
|
698
|
+
return process.env.AGENT_INSIGHTS_HOME ?? join5(homedir5(), ".agent-insights");
|
|
400
699
|
}
|
|
401
700
|
function configFile() {
|
|
402
|
-
return
|
|
701
|
+
return join5(configRoot(), "config.json");
|
|
403
702
|
}
|
|
404
703
|
function authFile() {
|
|
405
|
-
return
|
|
704
|
+
return join5(configRoot(), "auth.json");
|
|
406
705
|
}
|
|
407
706
|
function bypassSecretFile() {
|
|
408
|
-
return
|
|
707
|
+
return join5(configRoot(), "bypass-secret");
|
|
409
708
|
}
|
|
410
709
|
function sessionCacheDir() {
|
|
411
|
-
return
|
|
710
|
+
return join5(configRoot(), "session-cache");
|
|
412
711
|
}
|
|
413
712
|
function logsDir() {
|
|
414
|
-
return
|
|
713
|
+
return join5(configRoot(), "logs");
|
|
415
714
|
}
|
|
416
715
|
|
|
417
716
|
// src/config/config.ts
|
|
@@ -448,7 +747,9 @@ function defaultConfig() {
|
|
|
448
747
|
},
|
|
449
748
|
agents: {
|
|
450
749
|
claudeCode: { enabled: false },
|
|
451
|
-
cursor: { enabled: false }
|
|
750
|
+
cursor: { enabled: false },
|
|
751
|
+
codex: { enabled: false },
|
|
752
|
+
pi: { enabled: false }
|
|
452
753
|
},
|
|
453
754
|
privacy: {
|
|
454
755
|
capturePrompts: false,
|
|
@@ -460,7 +761,7 @@ function defaultConfig() {
|
|
|
460
761
|
}
|
|
461
762
|
async function loadConfig() {
|
|
462
763
|
try {
|
|
463
|
-
const raw = await
|
|
764
|
+
const raw = await readFile5(configFile(), "utf8");
|
|
464
765
|
const parsed = JSON.parse(raw);
|
|
465
766
|
return mergeConfig(defaultConfig(), parsed);
|
|
466
767
|
} catch (err) {
|
|
@@ -470,13 +771,13 @@ async function loadConfig() {
|
|
|
470
771
|
}
|
|
471
772
|
async function saveConfig(cfg) {
|
|
472
773
|
await ensureDirs();
|
|
473
|
-
await
|
|
774
|
+
await writeFile5(configFile(), `${JSON.stringify(cfg, null, 2)}
|
|
474
775
|
`, "utf8");
|
|
475
776
|
}
|
|
476
777
|
async function ensureDirs() {
|
|
477
|
-
await
|
|
478
|
-
await
|
|
479
|
-
await
|
|
778
|
+
await mkdir5(dirname5(configFile()), { recursive: true });
|
|
779
|
+
await mkdir5(sessionCacheDir(), { recursive: true });
|
|
780
|
+
await mkdir5(logsDir(), { recursive: true });
|
|
480
781
|
}
|
|
481
782
|
function mergeConfig(base, patch) {
|
|
482
783
|
return {
|
|
@@ -495,20 +796,22 @@ function mergeConfig(base, patch) {
|
|
|
495
796
|
...base.agents.claudeCode,
|
|
496
797
|
...patch.agents?.claudeCode ?? {}
|
|
497
798
|
},
|
|
498
|
-
cursor: { ...base.agents.cursor, ...patch.agents?.cursor ?? {} }
|
|
799
|
+
cursor: { ...base.agents.cursor, ...patch.agents?.cursor ?? {} },
|
|
800
|
+
codex: { ...base.agents.codex, ...patch.agents?.codex ?? {} },
|
|
801
|
+
pi: { ...base.agents.pi, ...patch.agents?.pi ?? {} }
|
|
499
802
|
},
|
|
500
803
|
privacy: base.privacy
|
|
501
804
|
};
|
|
502
805
|
}
|
|
503
806
|
|
|
504
807
|
// src/transcript/store.ts
|
|
505
|
-
import { readFile as
|
|
808
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
506
809
|
import { put } from "@vercel/blob";
|
|
507
810
|
|
|
508
811
|
// src/auth/store.ts
|
|
509
|
-
import { chmod, readFile as
|
|
510
|
-
import { dirname as
|
|
511
|
-
import { mkdir as
|
|
812
|
+
import { chmod, readFile as readFile6, writeFile as writeFile6 } from "fs/promises";
|
|
813
|
+
import { dirname as dirname6 } from "path";
|
|
814
|
+
import { mkdir as mkdir6 } from "fs/promises";
|
|
512
815
|
|
|
513
816
|
// src/auth/oauth.ts
|
|
514
817
|
import { createHash as createHash2, randomBytes } from "crypto";
|
|
@@ -540,7 +843,7 @@ async function refreshAccessToken(refreshToken) {
|
|
|
540
843
|
var REFRESH_BUFFER_MS = 6e4;
|
|
541
844
|
async function loadAuth() {
|
|
542
845
|
try {
|
|
543
|
-
const raw = await
|
|
846
|
+
const raw = await readFile6(authFile(), "utf8");
|
|
544
847
|
const parsed = JSON.parse(raw);
|
|
545
848
|
if (typeof parsed.accessToken === "string" && parsed.accessToken.length > 0) {
|
|
546
849
|
return parsed;
|
|
@@ -553,8 +856,8 @@ async function loadAuth() {
|
|
|
553
856
|
}
|
|
554
857
|
async function saveAuth(state) {
|
|
555
858
|
const path = authFile();
|
|
556
|
-
await
|
|
557
|
-
await
|
|
859
|
+
await mkdir6(dirname6(path), { recursive: true });
|
|
860
|
+
await writeFile6(path, `${JSON.stringify(state, null, 2)}
|
|
558
861
|
`, {
|
|
559
862
|
encoding: "utf8",
|
|
560
863
|
mode: 384
|
|
@@ -565,9 +868,9 @@ async function saveAuth(state) {
|
|
|
565
868
|
}
|
|
566
869
|
}
|
|
567
870
|
async function clearAuth() {
|
|
568
|
-
const { unlink:
|
|
871
|
+
const { unlink: unlink3 } = await import("fs/promises");
|
|
569
872
|
try {
|
|
570
|
-
await
|
|
873
|
+
await unlink3(authFile());
|
|
571
874
|
} catch (err) {
|
|
572
875
|
if (err.code !== "ENOENT") throw err;
|
|
573
876
|
}
|
|
@@ -598,13 +901,13 @@ async function getValidToken() {
|
|
|
598
901
|
}
|
|
599
902
|
|
|
600
903
|
// src/config/bypass.ts
|
|
601
|
-
import { chmod as chmod2, mkdir as
|
|
602
|
-
import { dirname as
|
|
904
|
+
import { chmod as chmod2, mkdir as mkdir7, readFile as readFile7, unlink as unlink2, writeFile as writeFile7 } from "fs/promises";
|
|
905
|
+
import { dirname as dirname7 } from "path";
|
|
603
906
|
async function loadBypassSecret() {
|
|
604
907
|
const fromEnv = process.env.AGENT_INSIGHTS_BYPASS_SECRET || process.env.VERCEL_AUTOMATION_BYPASS_SECRET;
|
|
605
908
|
if (fromEnv) return fromEnv;
|
|
606
909
|
try {
|
|
607
|
-
const raw = (await
|
|
910
|
+
const raw = (await readFile7(bypassSecretFile(), "utf8")).trim();
|
|
608
911
|
return raw || void 0;
|
|
609
912
|
} catch (err) {
|
|
610
913
|
if (err.code === "ENOENT") return void 0;
|
|
@@ -636,7 +939,7 @@ var BlobTranscriptStore = class {
|
|
|
636
939
|
token;
|
|
637
940
|
prefix;
|
|
638
941
|
async upload(input) {
|
|
639
|
-
const body = await
|
|
942
|
+
const body = await readFile8(input.transcriptPath);
|
|
640
943
|
const requestedPath = buildPathname(
|
|
641
944
|
this.prefix,
|
|
642
945
|
input.userHash,
|
|
@@ -667,7 +970,7 @@ var AnalyzerTranscriptStore = class {
|
|
|
667
970
|
"transcript store: not logged in \u2014 run `agent-insights login`"
|
|
668
971
|
);
|
|
669
972
|
}
|
|
670
|
-
const body = await
|
|
973
|
+
const body = await readFile8(input.transcriptPath);
|
|
671
974
|
const url = new URL("/api/sessions/upload", this.analyzerUrl).toString();
|
|
672
975
|
const headers = await withBypassHeader({
|
|
673
976
|
authorization: `Bearer ${token}`,
|