agent-insights 0.0.13 → 0.0.16
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 +339 -53
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +324 -54
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -225,15 +225,15 @@ __export(store_exports, {
|
|
|
225
225
|
loadEmail: () => loadEmail,
|
|
226
226
|
saveAuth: () => saveAuth
|
|
227
227
|
});
|
|
228
|
-
import { chmod, readFile as
|
|
229
|
-
import { dirname as
|
|
230
|
-
import { mkdir as
|
|
228
|
+
import { chmod, readFile as readFile6, writeFile as writeFile6 } from "fs/promises";
|
|
229
|
+
import { dirname as dirname6 } from "path";
|
|
230
|
+
import { mkdir as mkdir6 } from "fs/promises";
|
|
231
231
|
async function loadEmail() {
|
|
232
232
|
return (await loadAuth())?.email;
|
|
233
233
|
}
|
|
234
234
|
async function loadAuth() {
|
|
235
235
|
try {
|
|
236
|
-
const raw = await
|
|
236
|
+
const raw = await readFile6(authFile(), "utf8");
|
|
237
237
|
const parsed = JSON.parse(raw);
|
|
238
238
|
if (typeof parsed.accessToken === "string" && parsed.accessToken.length > 0) {
|
|
239
239
|
return parsed;
|
|
@@ -246,8 +246,8 @@ async function loadAuth() {
|
|
|
246
246
|
}
|
|
247
247
|
async function saveAuth(state) {
|
|
248
248
|
const path = authFile();
|
|
249
|
-
await
|
|
250
|
-
await
|
|
249
|
+
await mkdir6(dirname6(path), { recursive: true });
|
|
250
|
+
await writeFile6(path, `${JSON.stringify(state, null, 2)}
|
|
251
251
|
`, {
|
|
252
252
|
encoding: "utf8",
|
|
253
253
|
mode: 384
|
|
@@ -258,9 +258,9 @@ async function saveAuth(state) {
|
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
260
|
async function clearAuth() {
|
|
261
|
-
const { unlink:
|
|
261
|
+
const { unlink: unlink3 } = await import("fs/promises");
|
|
262
262
|
try {
|
|
263
|
-
await
|
|
263
|
+
await unlink3(authFile());
|
|
264
264
|
} catch (err) {
|
|
265
265
|
if (err.code !== "ENOENT") throw err;
|
|
266
266
|
}
|
|
@@ -351,7 +351,9 @@ function defaultConfig() {
|
|
|
351
351
|
},
|
|
352
352
|
agents: {
|
|
353
353
|
claudeCode: { enabled: false },
|
|
354
|
-
cursor: { enabled: false }
|
|
354
|
+
cursor: { enabled: false },
|
|
355
|
+
codex: { enabled: false },
|
|
356
|
+
pi: { enabled: false }
|
|
355
357
|
},
|
|
356
358
|
privacy: {
|
|
357
359
|
capturePrompts: false,
|
|
@@ -398,7 +400,9 @@ function mergeConfig(base, patch) {
|
|
|
398
400
|
...base.agents.claudeCode,
|
|
399
401
|
...patch.agents?.claudeCode ?? {}
|
|
400
402
|
},
|
|
401
|
-
cursor: { ...base.agents.cursor, ...patch.agents?.cursor ?? {} }
|
|
403
|
+
cursor: { ...base.agents.cursor, ...patch.agents?.cursor ?? {} },
|
|
404
|
+
codex: { ...base.agents.codex, ...patch.agents?.codex ?? {} },
|
|
405
|
+
pi: { ...base.agents.pi, ...patch.agents?.pi ?? {} }
|
|
402
406
|
},
|
|
403
407
|
privacy: base.privacy
|
|
404
408
|
};
|
|
@@ -784,12 +788,162 @@ function asString(v) {
|
|
|
784
788
|
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
785
789
|
}
|
|
786
790
|
|
|
787
|
-
// src/adapters/
|
|
791
|
+
// src/adapters/codex.ts
|
|
788
792
|
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
789
793
|
import { homedir as homedir3 } from "os";
|
|
790
794
|
import { dirname as dirname3, join as join3 } from "path";
|
|
791
795
|
var HOOK_COMMAND_NAME2 = "agent-insights";
|
|
792
796
|
var HOOK_MAP2 = {
|
|
797
|
+
SessionStart: "session.start",
|
|
798
|
+
UserPromptSubmit: "user.prompt.submit",
|
|
799
|
+
PreToolUse: "tool.start",
|
|
800
|
+
PostToolUse: "tool.end",
|
|
801
|
+
PermissionRequest: "permission.request",
|
|
802
|
+
Stop: "session.end"
|
|
803
|
+
};
|
|
804
|
+
var CODEX_HOOK_EVENTS = Object.keys(HOOK_MAP2);
|
|
805
|
+
var codexAdapter = {
|
|
806
|
+
id: "codex",
|
|
807
|
+
agent: "codex",
|
|
808
|
+
label: "Codex",
|
|
809
|
+
settingsFile: ".codex/hooks.json",
|
|
810
|
+
map(payload) {
|
|
811
|
+
const type = HOOK_MAP2[payload.event];
|
|
812
|
+
if (!type) return void 0;
|
|
813
|
+
const data = payload.data ?? {};
|
|
814
|
+
const sessionId = asString2(data["session_id"]) ?? asString2(data["sessionId"]);
|
|
815
|
+
const toolName = asString2(data["tool_name"]) ?? asString2(data["toolName"]) ?? asString2(data["tool"]?.["name"]);
|
|
816
|
+
const transcriptPath = asString2(data["transcript_path"]) ?? asString2(data["transcriptPath"]);
|
|
817
|
+
return {
|
|
818
|
+
type,
|
|
819
|
+
...sessionId !== void 0 ? { sessionId } : {},
|
|
820
|
+
...toolName !== void 0 ? { toolName } : {},
|
|
821
|
+
...transcriptPath !== void 0 ? { transcriptPath } : {}
|
|
822
|
+
};
|
|
823
|
+
},
|
|
824
|
+
async install(repoRoot, scope = "global") {
|
|
825
|
+
const path = resolveCodexPath(repoRoot, scope);
|
|
826
|
+
const settings = await readJson2(path);
|
|
827
|
+
const next = { ...settings ?? {} };
|
|
828
|
+
const hooks = {};
|
|
829
|
+
for (const [event, raw] of Object.entries(next.hooks ?? {})) {
|
|
830
|
+
hooks[event] = normalizeEventHooks2(raw);
|
|
831
|
+
}
|
|
832
|
+
for (const event of CODEX_HOOK_EVENTS) {
|
|
833
|
+
const withoutInsights = removeAgentInsightsMatchers2(hooks[event] ?? []);
|
|
834
|
+
hooks[event] = addAgentInsightsHook2(withoutInsights, event);
|
|
835
|
+
}
|
|
836
|
+
next.hooks = hooks;
|
|
837
|
+
await mkdir3(dirname3(path), { recursive: true });
|
|
838
|
+
await writeFile3(path, `${JSON.stringify(next, null, 2)}
|
|
839
|
+
`, "utf8");
|
|
840
|
+
return { written: true, path };
|
|
841
|
+
},
|
|
842
|
+
async uninstall(repoRoot, scope = "global") {
|
|
843
|
+
const path = resolveCodexPath(repoRoot, scope);
|
|
844
|
+
const settings = await readJson2(path);
|
|
845
|
+
if (!settings?.hooks) return { removed: false, path };
|
|
846
|
+
const hooks = {};
|
|
847
|
+
let touched = false;
|
|
848
|
+
for (const [event, raw] of Object.entries(settings.hooks)) {
|
|
849
|
+
const before = normalizeEventHooks2(raw);
|
|
850
|
+
const after = removeAgentInsightsMatchers2(before);
|
|
851
|
+
if (after.length !== before.length || JSON.stringify(after) !== JSON.stringify(before)) {
|
|
852
|
+
touched = true;
|
|
853
|
+
}
|
|
854
|
+
if (after.length > 0) hooks[event] = after;
|
|
855
|
+
}
|
|
856
|
+
settings.hooks = hooks;
|
|
857
|
+
await writeFile3(path, `${JSON.stringify(settings, null, 2)}
|
|
858
|
+
`, "utf8");
|
|
859
|
+
return { removed: touched, path };
|
|
860
|
+
},
|
|
861
|
+
async isInstalled(repoRoot, scope = "global") {
|
|
862
|
+
const settings = await readJson2(
|
|
863
|
+
resolveCodexPath(repoRoot, scope)
|
|
864
|
+
);
|
|
865
|
+
if (!settings?.hooks) return false;
|
|
866
|
+
return Object.values(settings.hooks).some(
|
|
867
|
+
(raw) => normalizeEventHooks2(raw).some(
|
|
868
|
+
(group) => group.hooks.some((h) => isAgentInsightsCommand2(h.command))
|
|
869
|
+
)
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
};
|
|
873
|
+
function resolveCodexPath(repoRoot, scope) {
|
|
874
|
+
return scope === "global" ? join3(homedir3(), ".codex", "hooks.json") : join3(repoRoot, ".codex", "hooks.json");
|
|
875
|
+
}
|
|
876
|
+
function isAgentInsightsCommand2(cmd) {
|
|
877
|
+
if (!cmd) return false;
|
|
878
|
+
return cmd.startsWith(`${HOOK_COMMAND_NAME2} hook`);
|
|
879
|
+
}
|
|
880
|
+
function isHookCommand2(value) {
|
|
881
|
+
if (!value || typeof value !== "object") return false;
|
|
882
|
+
const entry = value;
|
|
883
|
+
return entry.type === "command" && typeof entry.command === "string";
|
|
884
|
+
}
|
|
885
|
+
function normalizeEventHooks2(raw) {
|
|
886
|
+
const groups = [];
|
|
887
|
+
for (const entry of raw) {
|
|
888
|
+
if (!entry || typeof entry !== "object") continue;
|
|
889
|
+
const record = entry;
|
|
890
|
+
if (Array.isArray(record.hooks)) {
|
|
891
|
+
const commands = record.hooks.filter(isHookCommand2);
|
|
892
|
+
if (commands.length === 0) continue;
|
|
893
|
+
groups.push({
|
|
894
|
+
matcher: typeof record.matcher === "string" ? record.matcher : "",
|
|
895
|
+
hooks: commands
|
|
896
|
+
});
|
|
897
|
+
continue;
|
|
898
|
+
}
|
|
899
|
+
if (isHookCommand2(record)) {
|
|
900
|
+
groups.push({ matcher: "", hooks: [record] });
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
return groups;
|
|
904
|
+
}
|
|
905
|
+
function removeAgentInsightsMatchers2(matchers) {
|
|
906
|
+
const result = [];
|
|
907
|
+
for (const group of matchers) {
|
|
908
|
+
const hooks = group.hooks.filter(
|
|
909
|
+
(h) => !isAgentInsightsCommand2(h.command)
|
|
910
|
+
);
|
|
911
|
+
if (hooks.length > 0) result.push({ ...group, hooks });
|
|
912
|
+
}
|
|
913
|
+
return result;
|
|
914
|
+
}
|
|
915
|
+
function addAgentInsightsHook2(matchers, event) {
|
|
916
|
+
const command = `${HOOK_COMMAND_NAME2} hook ${event}`;
|
|
917
|
+
const entry = { type: "command", command };
|
|
918
|
+
for (const group of matchers) {
|
|
919
|
+
if (group.matcher === "") {
|
|
920
|
+
if (!group.hooks.some((h) => h.command === command)) {
|
|
921
|
+
group.hooks.push(entry);
|
|
922
|
+
}
|
|
923
|
+
return matchers;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
return [...matchers, { matcher: "", hooks: [entry] }];
|
|
927
|
+
}
|
|
928
|
+
async function readJson2(path) {
|
|
929
|
+
try {
|
|
930
|
+
const raw = await readFile3(path, "utf8");
|
|
931
|
+
return JSON.parse(raw);
|
|
932
|
+
} catch (err) {
|
|
933
|
+
if (err.code === "ENOENT") return void 0;
|
|
934
|
+
throw err;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
function asString2(v) {
|
|
938
|
+
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// src/adapters/cursor.ts
|
|
942
|
+
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
943
|
+
import { homedir as homedir4 } from "os";
|
|
944
|
+
import { dirname as dirname4, join as join4 } from "path";
|
|
945
|
+
var HOOK_COMMAND_NAME3 = "agent-insights";
|
|
946
|
+
var HOOK_MAP3 = {
|
|
793
947
|
sessionStart: "session.start",
|
|
794
948
|
sessionEnd: "session.end",
|
|
795
949
|
beforeSubmitPrompt: "user.prompt.submit",
|
|
@@ -806,19 +960,19 @@ var HOOK_MAP2 = {
|
|
|
806
960
|
afterFileEdit: "tool.end",
|
|
807
961
|
workspaceOpen: "session.start"
|
|
808
962
|
};
|
|
809
|
-
var CURSOR_HOOK_EVENTS = Object.keys(
|
|
963
|
+
var CURSOR_HOOK_EVENTS = Object.keys(HOOK_MAP3);
|
|
810
964
|
var cursorAdapter = {
|
|
811
965
|
id: "cursor",
|
|
812
966
|
agent: "cursor",
|
|
813
967
|
label: "Cursor",
|
|
814
968
|
settingsFile: ".cursor/hooks.json",
|
|
815
969
|
map(payload) {
|
|
816
|
-
const type =
|
|
970
|
+
const type = HOOK_MAP3[payload.event];
|
|
817
971
|
if (!type) return void 0;
|
|
818
972
|
const data = payload.data ?? {};
|
|
819
|
-
const sessionId =
|
|
820
|
-
const toolName =
|
|
821
|
-
const transcriptPath =
|
|
973
|
+
const sessionId = asString3(data["session_id"]) ?? asString3(data["sessionId"]) ?? asString3(data["conversation_id"]);
|
|
974
|
+
const toolName = asString3(data["tool_name"]) ?? asString3(data["toolName"]);
|
|
975
|
+
const transcriptPath = asString3(data["transcript_path"]) ?? asString3(data["transcriptPath"]) ?? asString3(process.env.CURSOR_TRANSCRIPT_PATH);
|
|
822
976
|
return {
|
|
823
977
|
type,
|
|
824
978
|
...sessionId !== void 0 ? { sessionId } : {},
|
|
@@ -828,7 +982,7 @@ var cursorAdapter = {
|
|
|
828
982
|
},
|
|
829
983
|
async install(repoRoot, scope = "global") {
|
|
830
984
|
const path = resolveCursorPath(repoRoot, scope);
|
|
831
|
-
const settings = await
|
|
985
|
+
const settings = await readJson3(path);
|
|
832
986
|
const next = {
|
|
833
987
|
version: 1,
|
|
834
988
|
...settings ?? {}
|
|
@@ -836,26 +990,26 @@ var cursorAdapter = {
|
|
|
836
990
|
const hooks = { ...next.hooks ?? {} };
|
|
837
991
|
for (const event of CURSOR_HOOK_EVENTS) {
|
|
838
992
|
const existing = (hooks[event] ?? []).filter(
|
|
839
|
-
(h) => !
|
|
993
|
+
(h) => !isAgentInsightsCommand3(h.command)
|
|
840
994
|
);
|
|
841
|
-
existing.push({ command: `${
|
|
995
|
+
existing.push({ command: `${HOOK_COMMAND_NAME3} hook ${event}` });
|
|
842
996
|
hooks[event] = existing;
|
|
843
997
|
}
|
|
844
998
|
next.hooks = hooks;
|
|
845
|
-
await
|
|
846
|
-
await
|
|
999
|
+
await mkdir4(dirname4(path), { recursive: true });
|
|
1000
|
+
await writeFile4(path, `${JSON.stringify(next, null, 2)}
|
|
847
1001
|
`, "utf8");
|
|
848
1002
|
return { written: true, path };
|
|
849
1003
|
},
|
|
850
1004
|
async uninstall(repoRoot, scope = "global") {
|
|
851
1005
|
const path = resolveCursorPath(repoRoot, scope);
|
|
852
|
-
const settings = await
|
|
1006
|
+
const settings = await readJson3(path);
|
|
853
1007
|
if (!settings?.hooks) return { removed: false, path };
|
|
854
1008
|
const hooks = { ...settings.hooks };
|
|
855
1009
|
let touched = false;
|
|
856
1010
|
for (const [event, entries] of Object.entries(hooks)) {
|
|
857
1011
|
const filtered = entries.filter(
|
|
858
|
-
(h) => !
|
|
1012
|
+
(h) => !isAgentInsightsCommand3(h.command)
|
|
859
1013
|
);
|
|
860
1014
|
if (filtered.length !== entries.length) touched = true;
|
|
861
1015
|
if (filtered.length === 0) {
|
|
@@ -865,44 +1019,160 @@ var cursorAdapter = {
|
|
|
865
1019
|
}
|
|
866
1020
|
}
|
|
867
1021
|
settings.hooks = hooks;
|
|
868
|
-
await
|
|
1022
|
+
await writeFile4(path, `${JSON.stringify(settings, null, 2)}
|
|
869
1023
|
`, "utf8");
|
|
870
1024
|
return { removed: touched, path };
|
|
871
1025
|
},
|
|
872
1026
|
async isInstalled(repoRoot, scope = "global") {
|
|
873
|
-
const settings = await
|
|
1027
|
+
const settings = await readJson3(
|
|
874
1028
|
resolveCursorPath(repoRoot, scope)
|
|
875
1029
|
);
|
|
876
1030
|
if (!settings?.hooks) return false;
|
|
877
1031
|
return Object.values(settings.hooks).some(
|
|
878
|
-
(entries) => entries.some((h) =>
|
|
1032
|
+
(entries) => entries.some((h) => isAgentInsightsCommand3(h.command))
|
|
879
1033
|
);
|
|
880
1034
|
}
|
|
881
1035
|
};
|
|
882
1036
|
function resolveCursorPath(repoRoot, scope) {
|
|
883
|
-
return scope === "global" ?
|
|
1037
|
+
return scope === "global" ? join4(homedir4(), ".cursor", "hooks.json") : join4(repoRoot, ".cursor", "hooks.json");
|
|
884
1038
|
}
|
|
885
|
-
function
|
|
1039
|
+
function isAgentInsightsCommand3(cmd) {
|
|
886
1040
|
if (!cmd) return false;
|
|
887
|
-
return cmd.startsWith(`${
|
|
1041
|
+
return cmd.startsWith(`${HOOK_COMMAND_NAME3} hook`);
|
|
888
1042
|
}
|
|
889
|
-
async function
|
|
1043
|
+
async function readJson3(path) {
|
|
890
1044
|
try {
|
|
891
|
-
const raw = await
|
|
1045
|
+
const raw = await readFile4(path, "utf8");
|
|
892
1046
|
return JSON.parse(raw);
|
|
893
1047
|
} catch (err) {
|
|
894
1048
|
if (err.code === "ENOENT") return void 0;
|
|
895
1049
|
throw err;
|
|
896
1050
|
}
|
|
897
1051
|
}
|
|
898
|
-
function
|
|
1052
|
+
function asString3(v) {
|
|
1053
|
+
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
// src/adapters/pi.ts
|
|
1057
|
+
import { mkdir as mkdir5, readFile as readFile5, unlink, writeFile as writeFile5 } from "fs/promises";
|
|
1058
|
+
import { homedir as homedir5 } from "os";
|
|
1059
|
+
import { dirname as dirname5, join as join5 } from "path";
|
|
1060
|
+
var EXTENSION_FILENAME = "agent-insights.ts";
|
|
1061
|
+
var HOOK_MAP4 = {
|
|
1062
|
+
session_start: "session.start",
|
|
1063
|
+
session_shutdown: "session.end",
|
|
1064
|
+
tool_call: "tool.start",
|
|
1065
|
+
tool_result: "tool.end"
|
|
1066
|
+
};
|
|
1067
|
+
var PI_HOOK_EVENTS = Object.keys(HOOK_MAP4);
|
|
1068
|
+
var EXTENSION_MARKER = "// installed-by: agent-insights";
|
|
1069
|
+
function extensionSource() {
|
|
1070
|
+
return `${EXTENSION_MARKER}
|
|
1071
|
+
// Generated by \`agent-insights init\`. Edit-with-caution: it's overwritten on every install.
|
|
1072
|
+
|
|
1073
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
1074
|
+
import { spawn } from "node:child_process";
|
|
1075
|
+
|
|
1076
|
+
const EVENTS = ${JSON.stringify(PI_HOOK_EVENTS)} as const;
|
|
1077
|
+
|
|
1078
|
+
function emit(event: string, payload: Record<string, unknown>): void {
|
|
1079
|
+
try {
|
|
1080
|
+
const child = spawn("agent-insights", ["hook", event], {
|
|
1081
|
+
stdio: ["pipe", "ignore", "ignore"],
|
|
1082
|
+
detached: true,
|
|
1083
|
+
});
|
|
1084
|
+
child.on("error", () => {});
|
|
1085
|
+
child.stdin.end(JSON.stringify(payload));
|
|
1086
|
+
child.unref();
|
|
1087
|
+
} catch {
|
|
1088
|
+
// best-effort
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
export default function (pi: ExtensionAPI) {
|
|
1093
|
+
for (const event of EVENTS) {
|
|
1094
|
+
pi.on(event as never, async (raw: unknown, ctx: { sessionManager?: { getSessionFile?: () => string | undefined; getSessionId?: () => string | undefined } }) => {
|
|
1095
|
+
const transcript_path = ctx.sessionManager?.getSessionFile?.();
|
|
1096
|
+
const session_id = ctx.sessionManager?.getSessionId?.();
|
|
1097
|
+
const e = (raw ?? {}) as Record<string, unknown>;
|
|
1098
|
+
emit(event, {
|
|
1099
|
+
...(session_id ? { session_id } : {}),
|
|
1100
|
+
...(transcript_path ? { transcript_path } : {}),
|
|
1101
|
+
...("toolName" in e && typeof e.toolName === "string"
|
|
1102
|
+
? { tool_name: e.toolName }
|
|
1103
|
+
: {}),
|
|
1104
|
+
...("reason" in e ? { reason: e.reason } : {}),
|
|
1105
|
+
});
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
`;
|
|
1110
|
+
}
|
|
1111
|
+
var piAdapter = {
|
|
1112
|
+
id: "pi",
|
|
1113
|
+
agent: "pi",
|
|
1114
|
+
label: "Pi",
|
|
1115
|
+
settingsFile: ".pi/extensions/agent-insights.ts",
|
|
1116
|
+
map(payload) {
|
|
1117
|
+
const type = HOOK_MAP4[payload.event];
|
|
1118
|
+
if (!type) return void 0;
|
|
1119
|
+
const data = payload.data ?? {};
|
|
1120
|
+
const sessionId = asString4(data["session_id"]) ?? asString4(data["sessionId"]);
|
|
1121
|
+
const toolName = asString4(data["tool_name"]) ?? asString4(data["toolName"]);
|
|
1122
|
+
const transcriptPath = asString4(data["transcript_path"]) ?? asString4(data["transcriptPath"]);
|
|
1123
|
+
return {
|
|
1124
|
+
type,
|
|
1125
|
+
...sessionId !== void 0 ? { sessionId } : {},
|
|
1126
|
+
...toolName !== void 0 ? { toolName } : {},
|
|
1127
|
+
...transcriptPath !== void 0 ? { transcriptPath } : {}
|
|
1128
|
+
};
|
|
1129
|
+
},
|
|
1130
|
+
async install(repoRoot, scope = "global") {
|
|
1131
|
+
const path = resolvePath(repoRoot, scope);
|
|
1132
|
+
await mkdir5(dirname5(path), { recursive: true });
|
|
1133
|
+
await writeFile5(path, extensionSource(), "utf8");
|
|
1134
|
+
return { written: true, path };
|
|
1135
|
+
},
|
|
1136
|
+
async uninstall(repoRoot, scope = "global") {
|
|
1137
|
+
const path = resolvePath(repoRoot, scope);
|
|
1138
|
+
try {
|
|
1139
|
+
const raw = await readFile5(path, "utf8");
|
|
1140
|
+
if (!raw.includes(EXTENSION_MARKER)) {
|
|
1141
|
+
return { removed: false, path };
|
|
1142
|
+
}
|
|
1143
|
+
await unlink(path);
|
|
1144
|
+
return { removed: true, path };
|
|
1145
|
+
} catch (err) {
|
|
1146
|
+
if (err.code === "ENOENT") {
|
|
1147
|
+
return { removed: false, path };
|
|
1148
|
+
}
|
|
1149
|
+
throw err;
|
|
1150
|
+
}
|
|
1151
|
+
},
|
|
1152
|
+
async isInstalled(repoRoot, scope = "global") {
|
|
1153
|
+
const path = resolvePath(repoRoot, scope);
|
|
1154
|
+
try {
|
|
1155
|
+
const raw = await readFile5(path, "utf8");
|
|
1156
|
+
return raw.includes(EXTENSION_MARKER);
|
|
1157
|
+
} catch (err) {
|
|
1158
|
+
if (err.code === "ENOENT") return false;
|
|
1159
|
+
throw err;
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
};
|
|
1163
|
+
function resolvePath(repoRoot, scope) {
|
|
1164
|
+
return scope === "global" ? join5(homedir5(), ".pi", "agent", "extensions", EXTENSION_FILENAME) : join5(repoRoot, ".pi", "extensions", EXTENSION_FILENAME);
|
|
1165
|
+
}
|
|
1166
|
+
function asString4(v) {
|
|
899
1167
|
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
900
1168
|
}
|
|
901
1169
|
|
|
902
1170
|
// src/adapters/registry.ts
|
|
903
1171
|
var adapters = {
|
|
904
1172
|
"claude-code": claudeCodeAdapter,
|
|
905
|
-
cursor: cursorAdapter
|
|
1173
|
+
cursor: cursorAdapter,
|
|
1174
|
+
codex: codexAdapter,
|
|
1175
|
+
pi: piAdapter
|
|
906
1176
|
};
|
|
907
1177
|
var adapterList = Object.values(adapters);
|
|
908
1178
|
|
|
@@ -946,18 +1216,18 @@ init_store();
|
|
|
946
1216
|
|
|
947
1217
|
// src/transcript/store.ts
|
|
948
1218
|
init_store();
|
|
949
|
-
import { readFile as
|
|
1219
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
950
1220
|
import { put } from "@vercel/blob";
|
|
951
1221
|
|
|
952
1222
|
// src/config/bypass.ts
|
|
953
1223
|
init_paths();
|
|
954
|
-
import { chmod as chmod2, mkdir as
|
|
955
|
-
import { dirname as
|
|
1224
|
+
import { chmod as chmod2, mkdir as mkdir7, readFile as readFile7, unlink as unlink2, writeFile as writeFile7 } from "fs/promises";
|
|
1225
|
+
import { dirname as dirname7 } from "path";
|
|
956
1226
|
async function loadBypassSecret() {
|
|
957
1227
|
const fromEnv = process.env.AGENT_INSIGHTS_BYPASS_SECRET || process.env.VERCEL_AUTOMATION_BYPASS_SECRET;
|
|
958
1228
|
if (fromEnv) return fromEnv;
|
|
959
1229
|
try {
|
|
960
|
-
const raw = (await
|
|
1230
|
+
const raw = (await readFile7(bypassSecretFile(), "utf8")).trim();
|
|
961
1231
|
return raw || void 0;
|
|
962
1232
|
} catch (err) {
|
|
963
1233
|
if (err.code === "ENOENT") return void 0;
|
|
@@ -966,8 +1236,8 @@ async function loadBypassSecret() {
|
|
|
966
1236
|
}
|
|
967
1237
|
async function saveBypassSecret(secret) {
|
|
968
1238
|
const path = bypassSecretFile();
|
|
969
|
-
await
|
|
970
|
-
await
|
|
1239
|
+
await mkdir7(dirname7(path), { recursive: true });
|
|
1240
|
+
await writeFile7(path, secret.trim(), { encoding: "utf8", mode: 384 });
|
|
971
1241
|
try {
|
|
972
1242
|
await chmod2(path, 384);
|
|
973
1243
|
} catch {
|
|
@@ -975,7 +1245,7 @@ async function saveBypassSecret(secret) {
|
|
|
975
1245
|
}
|
|
976
1246
|
async function clearBypassSecret() {
|
|
977
1247
|
try {
|
|
978
|
-
await
|
|
1248
|
+
await unlink2(bypassSecretFile());
|
|
979
1249
|
} catch (err) {
|
|
980
1250
|
if (err.code !== "ENOENT") throw err;
|
|
981
1251
|
}
|
|
@@ -1005,7 +1275,7 @@ var BlobTranscriptStore = class {
|
|
|
1005
1275
|
token;
|
|
1006
1276
|
prefix;
|
|
1007
1277
|
async upload(input) {
|
|
1008
|
-
const body = await
|
|
1278
|
+
const body = await readFile8(input.transcriptPath);
|
|
1009
1279
|
const requestedPath = buildPathname(
|
|
1010
1280
|
this.prefix,
|
|
1011
1281
|
input.userHash,
|
|
@@ -1036,7 +1306,7 @@ var AnalyzerTranscriptStore = class {
|
|
|
1036
1306
|
"transcript store: not logged in \u2014 run `agent-insights login`"
|
|
1037
1307
|
);
|
|
1038
1308
|
}
|
|
1039
|
-
const body = await
|
|
1309
|
+
const body = await readFile8(input.transcriptPath);
|
|
1040
1310
|
const url = new URL("/api/sessions/upload", this.analyzerUrl).toString();
|
|
1041
1311
|
const headers = await withBypassHeader({
|
|
1042
1312
|
authorization: `Bearer ${token}`,
|
|
@@ -1155,7 +1425,7 @@ async function runDoctor(opts) {
|
|
|
1155
1425
|
}
|
|
1156
1426
|
}
|
|
1157
1427
|
for (const adapter of adapterList) {
|
|
1158
|
-
const enabled = adapter.id === "claude-code" ? cfg.agents.claudeCode.enabled : cfg.agents.cursor.enabled;
|
|
1428
|
+
const enabled = adapter.id === "claude-code" ? cfg.agents.claudeCode.enabled : adapter.id === "cursor" ? cfg.agents.cursor.enabled : adapter.id === "codex" ? cfg.agents.codex.enabled : cfg.agents.pi.enabled;
|
|
1159
1429
|
if (!enabled) continue;
|
|
1160
1430
|
const installed = await adapter.isInstalled(repoRoot, cfg.hooksScope);
|
|
1161
1431
|
checks.push({
|
|
@@ -1401,6 +1671,8 @@ async function runInit(opts) {
|
|
|
1401
1671
|
cfg.userConsent.eventTelemetry = choices.telemetry;
|
|
1402
1672
|
cfg.agents.claudeCode.enabled = choices.agents.has("claude-code");
|
|
1403
1673
|
cfg.agents.cursor.enabled = choices.agents.has("cursor");
|
|
1674
|
+
cfg.agents.codex.enabled = choices.agents.has("codex");
|
|
1675
|
+
cfg.agents.pi.enabled = choices.agents.has("pi");
|
|
1404
1676
|
await saveConfig(cfg);
|
|
1405
1677
|
log.ok(`Wrote config to ${configFile()}`);
|
|
1406
1678
|
if (choices.bypassSecret !== void 0) {
|
|
@@ -1416,8 +1688,7 @@ async function runInit(opts) {
|
|
|
1416
1688
|
const repoRoot = process.cwd();
|
|
1417
1689
|
if (installHooks) {
|
|
1418
1690
|
for (const adapter of adapterList) {
|
|
1419
|
-
|
|
1420
|
-
if (!enabled) continue;
|
|
1691
|
+
if (!isAdapterEnabled(cfg, adapter.id)) continue;
|
|
1421
1692
|
const { path } = await adapter.install(repoRoot, cfg.hooksScope);
|
|
1422
1693
|
log.ok(`Installed ${adapter.label} hooks \u2192 ${rel(path)}`);
|
|
1423
1694
|
}
|
|
@@ -1497,6 +1768,9 @@ async function safePrompt(run) {
|
|
|
1497
1768
|
throw err;
|
|
1498
1769
|
}
|
|
1499
1770
|
}
|
|
1771
|
+
var KNOWN_AGENT_IDS = new Set(
|
|
1772
|
+
adapterList.map((a) => a.id)
|
|
1773
|
+
);
|
|
1500
1774
|
function parseAgents(value) {
|
|
1501
1775
|
const out = /* @__PURE__ */ new Set();
|
|
1502
1776
|
const trimmed = (value ?? "").trim();
|
|
@@ -1504,9 +1778,9 @@ function parseAgents(value) {
|
|
|
1504
1778
|
for (const a of adapterList) out.add(a.id);
|
|
1505
1779
|
return out;
|
|
1506
1780
|
}
|
|
1507
|
-
const list = (trimmed || "
|
|
1781
|
+
const list = (trimmed || adapterList.map((a) => a.id).join(",")).split(",").map((s) => s.trim()).filter(Boolean);
|
|
1508
1782
|
for (const id of list) {
|
|
1509
|
-
if (id
|
|
1783
|
+
if (KNOWN_AGENT_IDS.has(id)) out.add(id);
|
|
1510
1784
|
else log.warn(`Unknown agent id "${id}" \u2014 skipping.`);
|
|
1511
1785
|
}
|
|
1512
1786
|
return out;
|
|
@@ -1515,6 +1789,18 @@ function rel(p) {
|
|
|
1515
1789
|
const cwd = process.cwd();
|
|
1516
1790
|
return p.startsWith(cwd) ? p.slice(cwd.length + 1) : p;
|
|
1517
1791
|
}
|
|
1792
|
+
function isAdapterEnabled(cfg, id) {
|
|
1793
|
+
switch (id) {
|
|
1794
|
+
case "claude-code":
|
|
1795
|
+
return cfg.agents.claudeCode.enabled;
|
|
1796
|
+
case "cursor":
|
|
1797
|
+
return cfg.agents.cursor.enabled;
|
|
1798
|
+
case "codex":
|
|
1799
|
+
return cfg.agents.codex.enabled;
|
|
1800
|
+
case "pi":
|
|
1801
|
+
return cfg.agents.pi.enabled;
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1518
1804
|
|
|
1519
1805
|
// src/commands/login.ts
|
|
1520
1806
|
init_oauth();
|
|
@@ -1568,7 +1854,7 @@ async function runLogout() {
|
|
|
1568
1854
|
|
|
1569
1855
|
// src/commands/status.ts
|
|
1570
1856
|
import { existsSync } from "fs";
|
|
1571
|
-
import { join as
|
|
1857
|
+
import { join as join6 } from "path";
|
|
1572
1858
|
import kleur5 from "kleur";
|
|
1573
1859
|
init_paths();
|
|
1574
1860
|
async function runStatus(opts) {
|
|
@@ -1594,8 +1880,8 @@ async function runStatus(opts) {
|
|
|
1594
1880
|
enabled: cfg.enabled,
|
|
1595
1881
|
consent: cfg.userConsent,
|
|
1596
1882
|
vercel: {
|
|
1597
|
-
linked: existsSync(
|
|
1598
|
-
envFile: existsSync(
|
|
1883
|
+
linked: existsSync(join6(repoRoot, ".vercel/project.json")),
|
|
1884
|
+
envFile: existsSync(join6(repoRoot, ".env.local"))
|
|
1599
1885
|
},
|
|
1600
1886
|
adapters: adapterStatus
|
|
1601
1887
|
});
|
|
@@ -1619,9 +1905,9 @@ async function runStatus(opts) {
|
|
|
1619
1905
|
log.info("");
|
|
1620
1906
|
log.info(kleur5.bold("Vercel"));
|
|
1621
1907
|
log.info(
|
|
1622
|
-
` linked ${yn(existsSync(
|
|
1908
|
+
` linked ${yn(existsSync(join6(repoRoot, ".vercel/project.json")))}`
|
|
1623
1909
|
);
|
|
1624
|
-
log.info(` .env.local ${yn(existsSync(
|
|
1910
|
+
log.info(` .env.local ${yn(existsSync(join6(repoRoot, ".env.local")))}`);
|
|
1625
1911
|
}
|
|
1626
1912
|
function yn(v) {
|
|
1627
1913
|
return v ? kleur5.green("yes") : kleur5.dim("no");
|
|
@@ -1631,7 +1917,7 @@ function yn(v) {
|
|
|
1631
1917
|
var program = new Command();
|
|
1632
1918
|
program.name("agent-insights").description(
|
|
1633
1919
|
"Internal CLI for AI coding agent observability (Claude Code, Cursor)."
|
|
1634
|
-
).version("0.0.
|
|
1920
|
+
).version("0.0.16");
|
|
1635
1921
|
program.command("login").description("Authenticate with Vercel (Sign in with Vercel).").action(async () => {
|
|
1636
1922
|
await runLogin();
|
|
1637
1923
|
});
|