@nothumanwork/nn 0.1.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/LICENSE +21 -0
- package/README.md +77 -0
- package/bin/nn.js +106 -0
- package/package.json +74 -0
- package/src/config/env.ts +31 -0
- package/src/config/paths.ts +50 -0
- package/src/config/runtime.ts +37 -0
- package/src/config/sync.ts +48 -0
- package/src/db/client.ts +333 -0
- package/src/db/libsql-native.ts +66 -0
- package/src/db/lock.ts +72 -0
- package/src/db/migrate.ts +246 -0
- package/src/db/replica-migrate.ts +162 -0
- package/src/db/schema.sql +99 -0
- package/src/export/claude.ts +92 -0
- package/src/export/codex.ts +86 -0
- package/src/export/cursor.ts +68 -0
- package/src/export/generic.ts +19 -0
- package/src/export/registry.ts +118 -0
- package/src/export/types.ts +44 -0
- package/src/hooks/ingest.ts +107 -0
- package/src/hooks/resolvers/antigravity.ts +44 -0
- package/src/hooks/resolvers/claude.ts +27 -0
- package/src/hooks/resolvers/codex.ts +65 -0
- package/src/hooks/resolvers/common.ts +21 -0
- package/src/hooks/resolvers/cursor.ts +31 -0
- package/src/hooks/resolvers/grok.ts +59 -0
- package/src/hooks/resolvers/index.ts +35 -0
- package/src/hooks/resolvers/pi.ts +72 -0
- package/src/hooks/types.ts +20 -0
- package/src/index.ts +247 -0
- package/src/ingest/jsonl.ts +38 -0
- package/src/ingest/pipeline.ts +101 -0
- package/src/install/index.ts +227 -0
- package/src/install/types.ts +85 -0
- package/src/ir/event-id.ts +26 -0
- package/src/ir/types.ts +84 -0
- package/src/providers/antigravity/index.ts +175 -0
- package/src/providers/claude/index.ts +228 -0
- package/src/providers/codex/index.ts +264 -0
- package/src/providers/copilot/index.ts +24 -0
- package/src/providers/cursor/index.ts +340 -0
- package/src/providers/grok/index.ts +146 -0
- package/src/providers/pi/index.ts +197 -0
- package/src/providers/registry.ts +31 -0
- package/src/providers/types.ts +53 -0
- package/src/sync/coordinator.ts +186 -0
- package/src/sync/turso.ts +64 -0
- package/src/types/assets.d.ts +4 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { stdin } from "bun";
|
|
4
|
+
|
|
5
|
+
import type { ProviderId } from "../ir/types.ts";
|
|
6
|
+
import { ingestIncremental } from "../ingest/pipeline.ts";
|
|
7
|
+
import {
|
|
8
|
+
flushSync,
|
|
9
|
+
pullSync,
|
|
10
|
+
scheduleIncrementalSync,
|
|
11
|
+
} from "../sync/coordinator.ts";
|
|
12
|
+
import { syncConfig } from "../config/sync.ts";
|
|
13
|
+
import { ensureProjectDir } from "../config/paths.ts";
|
|
14
|
+
import { resolveHookInvocation } from "./resolvers/index.ts";
|
|
15
|
+
import type { HookEventKind, HookPayload } from "./types.ts";
|
|
16
|
+
import { HOOK_EVENTS } from "./types.ts";
|
|
17
|
+
import { stringField } from "./resolvers/common.ts";
|
|
18
|
+
|
|
19
|
+
export { resolveHookInvocation };
|
|
20
|
+
|
|
21
|
+
function parseArgs(argv: string[]): {
|
|
22
|
+
provider?: ProviderId;
|
|
23
|
+
hookEvent?: HookEventKind;
|
|
24
|
+
} {
|
|
25
|
+
let provider: ProviderId | undefined;
|
|
26
|
+
let hookEvent: HookEventKind | undefined;
|
|
27
|
+
|
|
28
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
29
|
+
if (argv[index] === "--provider") {
|
|
30
|
+
provider = argv[index + 1] as ProviderId;
|
|
31
|
+
}
|
|
32
|
+
if (argv[index] === "--event") {
|
|
33
|
+
hookEvent = argv[index + 1] as HookEventKind;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return { provider, hookEvent };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function handleHookInvocation(
|
|
41
|
+
provider: ProviderId,
|
|
42
|
+
hookEvent: HookEventKind,
|
|
43
|
+
payload: HookPayload,
|
|
44
|
+
): Promise<void> {
|
|
45
|
+
ensureProjectDir(stringField(payload, "cwd"));
|
|
46
|
+
|
|
47
|
+
if (hookEvent === "sessionStart") {
|
|
48
|
+
if (syncConfig().pullOnSession) {
|
|
49
|
+
await pullSync();
|
|
50
|
+
}
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const invocation = resolveHookInvocation(provider, hookEvent, payload);
|
|
55
|
+
if (!invocation) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const report = await ingestIncremental(
|
|
60
|
+
invocation.provider,
|
|
61
|
+
invocation.sourcePath,
|
|
62
|
+
invocation.sessionId,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
if (!syncConfig().enabled) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (hookEvent === "postToolUse" && report.eventsInserted > 0) {
|
|
70
|
+
scheduleIncrementalSync();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (hookEvent === "turnEnd" && report.eventsInserted > 0) {
|
|
75
|
+
await flushSync();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (hookEvent === "stop") {
|
|
80
|
+
await flushSync();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function main(): Promise<number> {
|
|
85
|
+
try {
|
|
86
|
+
const { provider, hookEvent } = parseArgs(process.argv.slice(2));
|
|
87
|
+
if (!provider || !hookEvent || !HOOK_EVENTS.includes(hookEvent)) {
|
|
88
|
+
console.error("usage: ingest.ts --provider <id> --event <kind>");
|
|
89
|
+
return 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const raw = await stdin.text();
|
|
93
|
+
const payload = raw.trim() ? (JSON.parse(raw) as HookPayload) : {};
|
|
94
|
+
await handleHookInvocation(provider, hookEvent, payload);
|
|
95
|
+
console.log(JSON.stringify({}));
|
|
96
|
+
return 0;
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error("[nn-ingest] failed", error);
|
|
99
|
+
console.log(JSON.stringify({}));
|
|
100
|
+
return 0;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (import.meta.main) {
|
|
105
|
+
await main();
|
|
106
|
+
process.exit(0);
|
|
107
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
|
|
3
|
+
import { homeDir } from "../../config/paths.ts";
|
|
4
|
+
import type { HookEventKind, HookInvocation, HookPayload } from "../types.ts";
|
|
5
|
+
import { firstExistingPath, stringField } from "./common.ts";
|
|
6
|
+
|
|
7
|
+
function antigravityTranscriptPath(sessionId: string): string {
|
|
8
|
+
return join(
|
|
9
|
+
homeDir(),
|
|
10
|
+
".gemini",
|
|
11
|
+
"antigravity-cli",
|
|
12
|
+
"brain",
|
|
13
|
+
sessionId,
|
|
14
|
+
".system_generated",
|
|
15
|
+
"logs",
|
|
16
|
+
"transcript.jsonl",
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function resolveAntigravityHook(
|
|
21
|
+
payload: HookPayload,
|
|
22
|
+
hookEvent: HookEventKind,
|
|
23
|
+
): HookInvocation | null {
|
|
24
|
+
const sessionId =
|
|
25
|
+
stringField(payload, "session_id") ??
|
|
26
|
+
stringField(payload, "sessionId") ??
|
|
27
|
+
"";
|
|
28
|
+
|
|
29
|
+
const transcriptPath = firstExistingPath(
|
|
30
|
+
stringField(payload, "transcript_path"),
|
|
31
|
+
sessionId ? antigravityTranscriptPath(sessionId) : undefined,
|
|
32
|
+
);
|
|
33
|
+
if (!transcriptPath || !sessionId) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
provider: "antigravity",
|
|
39
|
+
hookEvent,
|
|
40
|
+
sessionId,
|
|
41
|
+
sourcePath: transcriptPath,
|
|
42
|
+
cwd: stringField(payload, "cwd"),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { basename } from "node:path";
|
|
2
|
+
|
|
3
|
+
import type { HookEventKind, HookInvocation, HookPayload } from "../types.ts";
|
|
4
|
+
import { firstExistingPath, stringField } from "./common.ts";
|
|
5
|
+
|
|
6
|
+
export function resolveClaudeHook(
|
|
7
|
+
payload: HookPayload,
|
|
8
|
+
hookEvent: HookEventKind,
|
|
9
|
+
): HookInvocation | null {
|
|
10
|
+
const transcriptPath = firstExistingPath(stringField(payload, "transcript_path"));
|
|
11
|
+
if (!transcriptPath) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const sessionId =
|
|
16
|
+
stringField(payload, "session_id") ??
|
|
17
|
+
stringField(payload, "sessionId") ??
|
|
18
|
+
basename(transcriptPath, ".jsonl");
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
provider: "claude",
|
|
22
|
+
hookEvent,
|
|
23
|
+
sessionId,
|
|
24
|
+
sourcePath: transcriptPath,
|
|
25
|
+
cwd: stringField(payload, "cwd"),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
2
|
+
import { basename, join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import { homeDir } from "../../config/paths.ts";
|
|
5
|
+
import type { HookEventKind, HookInvocation, HookPayload } from "../types.ts";
|
|
6
|
+
import { firstExistingPath, stringField } from "./common.ts";
|
|
7
|
+
|
|
8
|
+
function codexRoot(): string {
|
|
9
|
+
return join(homeDir(), ".codex", "sessions");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function findCodexRollout(sessionId: string): string | null {
|
|
13
|
+
if (!existsSync(codexRoot())) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const targetName = `rollout-${sessionId}.jsonl`;
|
|
18
|
+
const walk = (dir: string): string | null => {
|
|
19
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
20
|
+
const path = join(dir, entry.name);
|
|
21
|
+
if (entry.isDirectory()) {
|
|
22
|
+
const found = walk(path);
|
|
23
|
+
if (found) {
|
|
24
|
+
return found;
|
|
25
|
+
}
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (entry.isFile() && entry.name === targetName) {
|
|
29
|
+
return path;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return walk(codexRoot());
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function resolveCodexHook(
|
|
39
|
+
payload: HookPayload,
|
|
40
|
+
hookEvent: HookEventKind,
|
|
41
|
+
): HookInvocation | null {
|
|
42
|
+
const sessionId =
|
|
43
|
+
stringField(payload, "session_id") ??
|
|
44
|
+
stringField(payload, "sessionId") ??
|
|
45
|
+
"";
|
|
46
|
+
|
|
47
|
+
const transcriptPath = firstExistingPath(
|
|
48
|
+
stringField(payload, "transcript_path"),
|
|
49
|
+
sessionId ? findCodexRollout(sessionId) ?? undefined : undefined,
|
|
50
|
+
);
|
|
51
|
+
if (!transcriptPath) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const resolvedSessionId =
|
|
56
|
+
sessionId || basename(transcriptPath, ".jsonl").replace(/^rollout-/, "");
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
provider: "codex",
|
|
60
|
+
hookEvent,
|
|
61
|
+
sessionId: resolvedSessionId,
|
|
62
|
+
sourcePath: transcriptPath,
|
|
63
|
+
cwd: stringField(payload, "cwd"),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
export function stringField(
|
|
4
|
+
value: unknown,
|
|
5
|
+
key: string,
|
|
6
|
+
): string | undefined {
|
|
7
|
+
if (!value || typeof value !== "object") {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
const raw = (value as Record<string, unknown>)[key];
|
|
11
|
+
return typeof raw === "string" && raw.trim() ? raw.trim() : undefined;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function firstExistingPath(...paths: Array<string | undefined>): string | null {
|
|
15
|
+
for (const path of paths) {
|
|
16
|
+
if (path && existsSync(path)) {
|
|
17
|
+
return path;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { HookEventKind, HookInvocation, HookPayload } from "../types.ts";
|
|
2
|
+
import {
|
|
3
|
+
cursorSessionIdFromPath,
|
|
4
|
+
isTranscriptFile,
|
|
5
|
+
} from "../../providers/cursor/index.ts";
|
|
6
|
+
import { firstExistingPath, stringField } from "./common.ts";
|
|
7
|
+
|
|
8
|
+
export function resolveCursorHook(
|
|
9
|
+
payload: HookPayload,
|
|
10
|
+
hookEvent: HookEventKind,
|
|
11
|
+
): HookInvocation | null {
|
|
12
|
+
const transcriptPath = firstExistingPath(
|
|
13
|
+
stringField(payload, "transcript_path"),
|
|
14
|
+
process.env.CURSOR_TRANSCRIPT_PATH?.trim(),
|
|
15
|
+
);
|
|
16
|
+
if (!transcriptPath || !isTranscriptFile(transcriptPath)) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const sessionId =
|
|
21
|
+
stringField(payload, "conversation_id") ??
|
|
22
|
+
cursorSessionIdFromPath(transcriptPath);
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
provider: "cursor",
|
|
26
|
+
hookEvent,
|
|
27
|
+
sessionId,
|
|
28
|
+
sourcePath: transcriptPath,
|
|
29
|
+
cwd: stringField(payload, "cwd"),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import { encodeProjectSlug, homeDir } from "../../config/paths.ts";
|
|
5
|
+
import type { HookEventKind, HookInvocation, HookPayload } from "../types.ts";
|
|
6
|
+
import { firstExistingPath, stringField } from "./common.ts";
|
|
7
|
+
|
|
8
|
+
function grokRoot(): string {
|
|
9
|
+
return join(homeDir(), ".grok", "sessions");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function findGrokUpdates(sessionId: string, cwd?: string): string | null {
|
|
13
|
+
if (!existsSync(grokRoot())) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const candidates: string[] = [];
|
|
18
|
+
if (cwd) {
|
|
19
|
+
candidates.push(join(grokRoot(), encodeProjectSlug(cwd), sessionId, "updates.jsonl"));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
for (const cwdEntry of readdirSync(grokRoot(), { withFileTypes: true })) {
|
|
23
|
+
if (!cwdEntry.isDirectory()) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
candidates.push(
|
|
27
|
+
join(grokRoot(), cwdEntry.name, sessionId, "updates.jsonl"),
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return firstExistingPath(...candidates);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function resolveGrokHook(
|
|
35
|
+
payload: HookPayload,
|
|
36
|
+
hookEvent: HookEventKind,
|
|
37
|
+
): HookInvocation | null {
|
|
38
|
+
const sessionId =
|
|
39
|
+
stringField(payload, "session_id") ??
|
|
40
|
+
stringField(payload, "sessionId") ??
|
|
41
|
+
"";
|
|
42
|
+
const cwd = stringField(payload, "cwd");
|
|
43
|
+
|
|
44
|
+
const transcriptPath = firstExistingPath(
|
|
45
|
+
stringField(payload, "transcript_path"),
|
|
46
|
+
sessionId ? findGrokUpdates(sessionId, cwd) ?? undefined : undefined,
|
|
47
|
+
);
|
|
48
|
+
if (!transcriptPath || !sessionId) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
provider: "grok",
|
|
54
|
+
hookEvent,
|
|
55
|
+
sessionId,
|
|
56
|
+
sourcePath: transcriptPath,
|
|
57
|
+
cwd,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ProviderId } from "../../ir/types.ts";
|
|
2
|
+
import type { HookEventKind, HookInvocation, HookPayload } from "../types.ts";
|
|
3
|
+
import { resolveAntigravityHook } from "./antigravity.ts";
|
|
4
|
+
import { resolveClaudeHook } from "./claude.ts";
|
|
5
|
+
import { resolveCodexHook } from "./codex.ts";
|
|
6
|
+
import { resolveCursorHook } from "./cursor.ts";
|
|
7
|
+
import { resolveGrokHook } from "./grok.ts";
|
|
8
|
+
import { resolvePiHook } from "./pi.ts";
|
|
9
|
+
|
|
10
|
+
type Resolver = (
|
|
11
|
+
payload: HookPayload,
|
|
12
|
+
hookEvent: HookEventKind,
|
|
13
|
+
) => HookInvocation | null;
|
|
14
|
+
|
|
15
|
+
const resolvers: Record<ProviderId, Resolver | undefined> = {
|
|
16
|
+
cursor: resolveCursorHook,
|
|
17
|
+
claude: resolveClaudeHook,
|
|
18
|
+
codex: resolveCodexHook,
|
|
19
|
+
pi: resolvePiHook,
|
|
20
|
+
grok: resolveGrokHook,
|
|
21
|
+
antigravity: resolveAntigravityHook,
|
|
22
|
+
copilot: undefined,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function resolveHookInvocation(
|
|
26
|
+
provider: ProviderId,
|
|
27
|
+
hookEvent: HookEventKind,
|
|
28
|
+
payload: HookPayload,
|
|
29
|
+
): HookInvocation | null {
|
|
30
|
+
const resolver = resolvers[provider];
|
|
31
|
+
if (!resolver) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
return resolver(payload, hookEvent);
|
|
35
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
2
|
+
import { basename, join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import { homeDir } from "../../config/paths.ts";
|
|
5
|
+
import type { HookEventKind, HookInvocation, HookPayload } from "../types.ts";
|
|
6
|
+
import { firstExistingPath, stringField } from "./common.ts";
|
|
7
|
+
|
|
8
|
+
function piRoot(): string {
|
|
9
|
+
return join(homeDir(), ".pi", "agent", "sessions");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function findPiSessionFile(sessionId: string): string | null {
|
|
13
|
+
if (!existsSync(piRoot())) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const walk = (dir: string): string | null => {
|
|
18
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
19
|
+
const path = join(dir, entry.name);
|
|
20
|
+
if (entry.isDirectory()) {
|
|
21
|
+
const found = walk(path);
|
|
22
|
+
if (found) {
|
|
23
|
+
return found;
|
|
24
|
+
}
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const fileSessionId =
|
|
31
|
+
entry.name.replace(".jsonl", "").split("_").at(-1) ??
|
|
32
|
+
basename(entry.name, ".jsonl");
|
|
33
|
+
if (fileSessionId === sessionId) {
|
|
34
|
+
return path;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
return walk(piRoot());
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function resolvePiHook(
|
|
44
|
+
payload: HookPayload,
|
|
45
|
+
hookEvent: HookEventKind,
|
|
46
|
+
): HookInvocation | null {
|
|
47
|
+
const sessionId =
|
|
48
|
+
stringField(payload, "session_id") ??
|
|
49
|
+
stringField(payload, "sessionId") ??
|
|
50
|
+
"";
|
|
51
|
+
|
|
52
|
+
const transcriptPath = firstExistingPath(
|
|
53
|
+
stringField(payload, "transcript_path"),
|
|
54
|
+
sessionId ? findPiSessionFile(sessionId) ?? undefined : undefined,
|
|
55
|
+
);
|
|
56
|
+
if (!transcriptPath) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const resolvedSessionId =
|
|
61
|
+
sessionId ||
|
|
62
|
+
basename(transcriptPath, ".jsonl").split("_").at(-1) ||
|
|
63
|
+
basename(transcriptPath, ".jsonl");
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
provider: "pi",
|
|
67
|
+
hookEvent,
|
|
68
|
+
sessionId: resolvedSessionId,
|
|
69
|
+
sourcePath: transcriptPath,
|
|
70
|
+
cwd: stringField(payload, "cwd"),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ProviderId } from "../ir/types.ts";
|
|
2
|
+
|
|
3
|
+
export const HOOK_EVENTS = [
|
|
4
|
+
"postToolUse",
|
|
5
|
+
"turnEnd",
|
|
6
|
+
"stop",
|
|
7
|
+
"sessionStart",
|
|
8
|
+
] as const;
|
|
9
|
+
|
|
10
|
+
export type HookEventKind = (typeof HOOK_EVENTS)[number];
|
|
11
|
+
|
|
12
|
+
export interface HookInvocation {
|
|
13
|
+
provider: ProviderId;
|
|
14
|
+
hookEvent: HookEventKind;
|
|
15
|
+
sessionId: string;
|
|
16
|
+
sourcePath: string;
|
|
17
|
+
cwd?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type HookPayload = Record<string, unknown>;
|