slait.dev 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.
Files changed (59) hide show
  1. package/README.md +149 -0
  2. package/dist/commands/config.d.ts +2 -0
  3. package/dist/commands/config.js +22 -0
  4. package/dist/commands/config.js.map +1 -0
  5. package/dist/commands/hooks.d.ts +17 -0
  6. package/dist/commands/hooks.js +372 -0
  7. package/dist/commands/hooks.js.map +1 -0
  8. package/dist/commands/init.d.ts +3 -0
  9. package/dist/commands/init.js +41 -0
  10. package/dist/commands/init.js.map +1 -0
  11. package/dist/commands/setup.d.ts +7 -0
  12. package/dist/commands/setup.js +85 -0
  13. package/dist/commands/setup.js.map +1 -0
  14. package/dist/commands/status.d.ts +1 -0
  15. package/dist/commands/status.js +29 -0
  16. package/dist/commands/status.js.map +1 -0
  17. package/dist/commands/upload.d.ts +6 -0
  18. package/dist/commands/upload.js +75 -0
  19. package/dist/commands/upload.js.map +1 -0
  20. package/dist/index.d.ts +2 -0
  21. package/dist/index.js +89 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/lib/api.d.ts +62 -0
  24. package/dist/lib/api.js +92 -0
  25. package/dist/lib/api.js.map +1 -0
  26. package/dist/lib/claude-artifacts.d.ts +18 -0
  27. package/dist/lib/claude-artifacts.js +181 -0
  28. package/dist/lib/claude-artifacts.js.map +1 -0
  29. package/dist/lib/claude-hooks.d.ts +13 -0
  30. package/dist/lib/claude-hooks.js +87 -0
  31. package/dist/lib/claude-hooks.js.map +1 -0
  32. package/dist/lib/config.d.ts +10 -0
  33. package/dist/lib/config.js +52 -0
  34. package/dist/lib/config.js.map +1 -0
  35. package/dist/lib/cursor-artifacts.d.ts +13 -0
  36. package/dist/lib/cursor-artifacts.js +83 -0
  37. package/dist/lib/cursor-artifacts.js.map +1 -0
  38. package/dist/lib/cursor-hooks.d.ts +14 -0
  39. package/dist/lib/cursor-hooks.js +112 -0
  40. package/dist/lib/cursor-hooks.js.map +1 -0
  41. package/dist/lib/discover.d.ts +8 -0
  42. package/dist/lib/discover.js +65 -0
  43. package/dist/lib/discover.js.map +1 -0
  44. package/dist/lib/format.d.ts +17 -0
  45. package/dist/lib/format.js +134 -0
  46. package/dist/lib/format.js.map +1 -0
  47. package/dist/lib/project-link.d.ts +11 -0
  48. package/dist/lib/project-link.js +32 -0
  49. package/dist/lib/project-link.js.map +1 -0
  50. package/dist/lib/session-events.d.ts +8 -0
  51. package/dist/lib/session-events.js +44 -0
  52. package/dist/lib/session-events.js.map +1 -0
  53. package/dist/lib/state.d.ts +11 -0
  54. package/dist/lib/state.js +31 -0
  55. package/dist/lib/state.js.map +1 -0
  56. package/dist/lib/transcript.d.ts +15 -0
  57. package/dist/lib/transcript.js +44 -0
  58. package/dist/lib/transcript.js.map +1 -0
  59. package/package.json +40 -0
@@ -0,0 +1,65 @@
1
+ import { readdirSync, statSync } from "fs";
2
+ import { join } from "path";
3
+ import { homedir } from "os";
4
+ const CURSOR_BASE = join(homedir(), ".cursor", "projects");
5
+ const CLAUDE_BASE = join(homedir(), "Library", "Application Support", "Claude");
6
+ function* walkJsonl(dir) {
7
+ try {
8
+ const entries = readdirSync(dir, { withFileTypes: true });
9
+ for (const e of entries) {
10
+ const full = join(dir, e.name);
11
+ if (e.isDirectory() && e.name !== "subagents" && e.name !== "node_modules") {
12
+ yield* walkJsonl(full);
13
+ }
14
+ else if (e.isFile() && e.name.endsWith(".jsonl")) {
15
+ yield { path: full, mtime: statSync(full).mtime };
16
+ }
17
+ }
18
+ }
19
+ catch {
20
+ // Skip inaccessible dirs
21
+ }
22
+ }
23
+ export function discoverCursor(projectPath) {
24
+ const files = [];
25
+ if (projectPath) {
26
+ const transcriptsDir = join(projectPath, "agent-transcripts");
27
+ for (const { path: fp, mtime } of walkJsonl(transcriptsDir)) {
28
+ files.push({ path: fp, source: "cursor", mtime });
29
+ }
30
+ }
31
+ else {
32
+ try {
33
+ for (const p of readdirSync(CURSOR_BASE, { withFileTypes: true })) {
34
+ if (!p.isDirectory())
35
+ continue;
36
+ const transcriptsDir = join(CURSOR_BASE, p.name, "agent-transcripts");
37
+ for (const { path: fp, mtime } of walkJsonl(transcriptsDir)) {
38
+ files.push({ path: fp, source: "cursor", mtime });
39
+ }
40
+ }
41
+ }
42
+ catch {
43
+ // ~/.cursor/projects doesn't exist
44
+ }
45
+ }
46
+ return files.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
47
+ }
48
+ export function discoverClaude() {
49
+ const files = [];
50
+ try {
51
+ for (const e of readdirSync(CLAUDE_BASE, { withFileTypes: true })) {
52
+ const full = join(CLAUDE_BASE, e.name);
53
+ if (e.isFile() &&
54
+ e.name.endsWith(".json") &&
55
+ e.name.includes("claude_chat_history")) {
56
+ files.push({ path: full, source: "claude", mtime: statSync(full).mtime });
57
+ }
58
+ }
59
+ }
60
+ catch {
61
+ // Claude not installed
62
+ }
63
+ return files.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
64
+ }
65
+ //# sourceMappingURL=discover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.js","sourceRoot":"","sources":["../../src/lib/discover.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAA;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAA;AAS5B,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC,CAAA;AAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,qBAAqB,EAAE,QAAQ,CAAC,CAAA;AAE/E,QAAQ,CAAC,CAAC,SAAS,CAAC,GAAW;IAC7B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;QACzD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAA;YAC9B,IAAI,CAAC,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;gBAC3E,KAAK,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;YACxB,CAAC;iBAAM,IAAI,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACnD,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAA;YACnD,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,yBAAyB;IAC3B,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,WAAoB;IACjD,MAAM,KAAK,GAAqB,EAAE,CAAA;IAElC,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,cAAc,GAAG,IAAI,CAAC,WAAW,EAAE,mBAAmB,CAAC,CAAA;QAC7D,KAAK,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,SAAS,CAAC,cAAc,CAAC,EAAE,CAAC;YAC5D,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAA;QACnD,CAAC;IACH,CAAC;SAAM,CAAC;QACN,IAAI,CAAC;YACH,KAAK,MAAM,CAAC,IAAI,WAAW,CAAC,WAAW,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;gBAClE,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE;oBAAE,SAAQ;gBAC9B,MAAM,cAAc,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAA;gBACrE,KAAK,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,SAAS,CAAC,cAAc,CAAC,EAAE,CAAC;oBAC5D,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAA;gBACnD,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,mCAAmC;QACrC,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;AACpE,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,MAAM,KAAK,GAAqB,EAAE,CAAA;IAClC,IAAI,CAAC;QACH,KAAK,MAAM,CAAC,IAAI,WAAW,CAAC,WAAW,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;YAClE,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,CAAA;YACtC,IACE,CAAC,CAAC,MAAM,EAAE;gBACV,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;gBACxB,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,qBAAqB,CAAC,EACtC,CAAC;gBACD,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,CAAA;YAC3E,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,uBAAuB;IACzB,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;AACpE,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Read a transcript file and convert to the chatLog string format the API accepts.
3
+ */
4
+ export declare function fileToChatLog(filePath: string, source: "cursor" | "claude"): string;
5
+ /**
6
+ * Convert raw Cursor JSONL content to a chatLog JSON string.
7
+ * Preserves tool_use signals as synthetic text markers for analysis.
8
+ */
9
+ export declare function cursorJsonlToChatLog(raw: string): string;
10
+ /**
11
+ * Convert raw Claude JSON content to a chatLog JSON string.
12
+ */
13
+ export declare function claudeJsonToChatLog(raw: string): string;
14
+ /**
15
+ * Count user+assistant turn pairs in a chatLog string.
16
+ */
17
+ export declare function countTurns(chatLog: string): number;
@@ -0,0 +1,134 @@
1
+ import { readFileSync } from "fs";
2
+ /**
3
+ * Read a transcript file and convert to the chatLog string format the API accepts.
4
+ */
5
+ export function fileToChatLog(filePath, source) {
6
+ const raw = readFileSync(filePath, "utf-8");
7
+ return source === "cursor" ? cursorJsonlToChatLog(raw) : claudeJsonToChatLog(raw);
8
+ }
9
+ /**
10
+ * Extract synthetic text from non-text content blocks (tool_use, etc.)
11
+ * so that slash commands, @ references, and tool names are preserved
12
+ * in the chat log for downstream analysis modules.
13
+ */
14
+ function extractNonTextSignals(blocks) {
15
+ const signals = [];
16
+ for (const block of blocks) {
17
+ if (block.type === "text")
18
+ continue;
19
+ if (block.type === "tool_use" && typeof block.name === "string") {
20
+ // Preserve tool names — they often encode slash commands or MCP calls
21
+ signals.push(`[TOOL_USE: ${block.name}]`);
22
+ if (block.input && typeof block.input === "object") {
23
+ const input = block.input;
24
+ // Preserve file paths / descriptions from tool inputs
25
+ if (typeof input.file_path === "string")
26
+ signals.push(`[@${input.file_path}]`);
27
+ if (typeof input.command === "string")
28
+ signals.push(`[CMD: ${input.command}]`);
29
+ if (typeof input.pattern === "string")
30
+ signals.push(`[GREP: ${input.pattern}]`);
31
+ if (typeof input.target_mode_id === "string")
32
+ signals.push(`[/${input.target_mode_id}]`);
33
+ }
34
+ }
35
+ }
36
+ return signals.join("\n");
37
+ }
38
+ /**
39
+ * Convert raw Cursor JSONL content to a chatLog JSON string.
40
+ * Preserves tool_use signals as synthetic text markers for analysis.
41
+ */
42
+ export function cursorJsonlToChatLog(raw) {
43
+ const lines = raw.trim().split("\n").filter(Boolean);
44
+ const messages = [];
45
+ for (const line of lines) {
46
+ try {
47
+ const obj = JSON.parse(line);
48
+ const role = obj.role === "assistant" ? "assistant" : "user";
49
+ const parts = [];
50
+ if (obj.message?.content) {
51
+ const textContent = obj.message.content
52
+ .filter((c) => c?.type === "text" && typeof c.text === "string")
53
+ .map((c) => c.text)
54
+ .join("\n");
55
+ if (textContent.trim())
56
+ parts.push(textContent);
57
+ const toolSignals = extractNonTextSignals(obj.message.content);
58
+ if (toolSignals.trim())
59
+ parts.push(toolSignals);
60
+ }
61
+ const combined = parts.join("\n");
62
+ if (combined.trim()) {
63
+ messages.push({ role, content: combined });
64
+ }
65
+ }
66
+ catch {
67
+ // Skip malformed lines
68
+ }
69
+ }
70
+ return JSON.stringify(messages);
71
+ }
72
+ /**
73
+ * Convert raw Claude JSON content to a chatLog JSON string.
74
+ */
75
+ export function claudeJsonToChatLog(raw) {
76
+ try {
77
+ const parsed = JSON.parse(raw);
78
+ const messages = [];
79
+ const processMessages = (msgs) => {
80
+ for (const msg of msgs) {
81
+ const m = msg;
82
+ const role = m.role === "assistant" || m.role === "ai" ? "assistant" : "user";
83
+ let content = "";
84
+ if (typeof m.content === "string") {
85
+ content = m.content;
86
+ }
87
+ else if (m.message && typeof m.message === "object") {
88
+ const msgContent = m.message.content;
89
+ if (Array.isArray(msgContent)) {
90
+ content = msgContent
91
+ .filter((p) => p && typeof p.text === "string")
92
+ .map((p) => p.text)
93
+ .join("\n");
94
+ }
95
+ }
96
+ else if (typeof m.text === "string") {
97
+ content = m.text;
98
+ }
99
+ if (content.trim())
100
+ messages.push({ role, content });
101
+ }
102
+ };
103
+ if (Array.isArray(parsed)) {
104
+ processMessages(parsed);
105
+ }
106
+ else if (parsed.messages && Array.isArray(parsed.messages)) {
107
+ processMessages(parsed.messages);
108
+ }
109
+ if (messages.length > 0)
110
+ return JSON.stringify(messages);
111
+ }
112
+ catch {
113
+ // Fall through
114
+ }
115
+ // If JSONL format (Claude Code uses this too)
116
+ const lines = raw.trim().split("\n").filter(Boolean);
117
+ if (lines.length > 1) {
118
+ return cursorJsonlToChatLog(raw);
119
+ }
120
+ return raw;
121
+ }
122
+ /**
123
+ * Count user+assistant turn pairs in a chatLog string.
124
+ */
125
+ export function countTurns(chatLog) {
126
+ try {
127
+ const messages = JSON.parse(chatLog);
128
+ return messages.filter((m) => m.role === "user").length;
129
+ }
130
+ catch {
131
+ return 0;
132
+ }
133
+ }
134
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/lib/format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAA;AAEjC;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,MAA2B;IACzE,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IAC3C,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAA;AACnF,CAAC;AASD;;;;GAIG;AACH,SAAS,qBAAqB,CAAC,MAAsB;IACnD,MAAM,OAAO,GAAa,EAAE,CAAA;IAE5B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;YAAE,SAAQ;QAEnC,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAChE,sEAAsE;YACtE,OAAO,CAAC,IAAI,CAAC,cAAc,KAAK,CAAC,IAAI,GAAG,CAAC,CAAA;YAEzC,IAAI,KAAK,CAAC,KAAK,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;gBACnD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAgC,CAAA;gBACpD,sDAAsD;gBACtD,IAAI,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ;oBAAE,OAAO,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,SAAS,GAAG,CAAC,CAAA;gBAC9E,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;oBAAE,OAAO,CAAC,IAAI,CAAC,SAAS,KAAK,CAAC,OAAO,GAAG,CAAC,CAAA;gBAC9E,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;oBAAE,OAAO,CAAC,IAAI,CAAC,UAAU,KAAK,CAAC,OAAO,GAAG,CAAC,CAAA;gBAC/E,IAAI,OAAO,KAAK,CAAC,cAAc,KAAK,QAAQ;oBAAE,OAAO,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,cAAc,GAAG,CAAC,CAAA;YAC1F,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAC3B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAW;IAC9C,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IACpD,MAAM,QAAQ,GAA6C,EAAE,CAAA;IAE7D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAG1B,CAAA;YACD,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAA;YAC5D,MAAM,KAAK,GAAa,EAAE,CAAA;YAE1B,IAAI,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC;gBACzB,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO;qBACpC,MAAM,CACL,CAAC,CAAC,EAAuC,EAAE,CACzC,CAAC,EAAE,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CACnD;qBACA,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;qBAClB,IAAI,CAAC,IAAI,CAAC,CAAA;gBACb,IAAI,WAAW,CAAC,IAAI,EAAE;oBAAE,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;gBAE/C,MAAM,WAAW,GAAG,qBAAqB,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;gBAC9D,IAAI,WAAW,CAAC,IAAI,EAAE;oBAAE,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;YACjD,CAAC;YAED,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACjC,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;gBACpB,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAA;YAC5C,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,uBAAuB;QACzB,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;AACjC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAC7C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC9B,MAAM,QAAQ,GAA6C,EAAE,CAAA;QAE7D,MAAM,eAAe,GAAG,CAAC,IAAe,EAAE,EAAE;YAC1C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,MAAM,CAAC,GAAG,GAA8B,CAAA;gBACxC,MAAM,IAAI,GACR,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAA;gBAClE,IAAI,OAAO,GAAG,EAAE,CAAA;gBAChB,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;oBAClC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAA;gBACrB,CAAC;qBAAM,IAAI,CAAC,CAAC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;oBACtD,MAAM,UAAU,GAAI,CAAC,CAAC,OAAmC,CAAC,OAAO,CAAA;oBACjE,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;wBAC9B,OAAO,GAAG,UAAU;6BACjB,MAAM,CACL,CAAC,CAAU,EAAE,EAAE,CACb,CAAC,IAAI,OAAQ,CAA6B,CAAC,IAAI,KAAK,QAAQ,CAC/D;6BACA,GAAG,CAAC,CAAC,CAAU,EAAE,EAAE,CAAE,CAA4B,CAAC,IAAI,CAAC;6BACvD,IAAI,CAAC,IAAI,CAAC,CAAA;oBACf,CAAC;gBACH,CAAC;qBAAM,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBACtC,OAAO,GAAG,CAAC,CAAC,IAAI,CAAA;gBAClB,CAAC;gBACD,IAAI,OAAO,CAAC,IAAI,EAAE;oBAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAA;YACtD,CAAC;QACH,CAAC,CAAA;QAED,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1B,eAAe,CAAC,MAAM,CAAC,CAAA;QACzB,CAAC;aAAM,IAAI,MAAM,CAAC,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7D,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;QAClC,CAAC;QAED,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,eAAe;IACjB,CAAC;IAED,8CAA8C;IAC9C,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IACpD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,oBAAoB,CAAC,GAAG,CAAC,CAAA;IAClC,CAAC;IAED,OAAO,GAAG,CAAA;AACZ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,OAAe;IACxC,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAA4B,CAAA;QAC/D,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,MAAM,CAAA;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAA;IACV,CAAC;AACH,CAAC"}
@@ -0,0 +1,11 @@
1
+ export interface ProjectLink {
2
+ projectId: string;
3
+ projectName: string;
4
+ localPath: string;
5
+ source: "cursor" | "claude";
6
+ linkedAt: string;
7
+ }
8
+ export declare function hashPath(dirPath: string): string;
9
+ export declare function getProjectLink(dirPath: string): ProjectLink | null;
10
+ export declare function saveProjectLink(link: ProjectLink): void;
11
+ export declare function isProjectLinked(dirPath: string): boolean;
@@ -0,0 +1,32 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
2
+ import { join } from "path";
3
+ import { createHash } from "crypto";
4
+ import { getConfigDir } from "./config.js";
5
+ function projectsDir() {
6
+ return join(getConfigDir(), "projects");
7
+ }
8
+ export function hashPath(dirPath) {
9
+ return createHash("sha256").update(dirPath).digest("hex").slice(0, 16);
10
+ }
11
+ function linkFilePath(dirPath) {
12
+ return join(projectsDir(), `${hashPath(dirPath)}.json`);
13
+ }
14
+ export function getProjectLink(dirPath) {
15
+ const filePath = linkFilePath(dirPath);
16
+ try {
17
+ return JSON.parse(readFileSync(filePath, "utf-8"));
18
+ }
19
+ catch {
20
+ return null;
21
+ }
22
+ }
23
+ export function saveProjectLink(link) {
24
+ const dir = projectsDir();
25
+ mkdirSync(dir, { recursive: true });
26
+ const filePath = linkFilePath(link.localPath);
27
+ writeFileSync(filePath, JSON.stringify(link, null, 2) + "\n");
28
+ }
29
+ export function isProjectLinked(dirPath) {
30
+ return existsSync(linkFilePath(dirPath));
31
+ }
32
+ //# sourceMappingURL=project-link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-link.js","sourceRoot":"","sources":["../../src/lib/project-link.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,IAAI,CAAA;AACvE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAU1C,SAAS,WAAW;IAClB,OAAO,IAAI,CAAC,YAAY,EAAE,EAAE,UAAU,CAAC,CAAA;AACzC,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;AACxE,CAAC;AAED,SAAS,YAAY,CAAC,OAAe;IACnC,OAAO,IAAI,CAAC,WAAW,EAAE,EAAE,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;AACzD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,MAAM,QAAQ,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;IACtC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAgB,CAAA;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAiB;IAC/C,MAAM,GAAG,GAAG,WAAW,EAAE,CAAA;IACzB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACnC,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IAC7C,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;AAC/D,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,OAAO,UAAU,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAA;AAC1C,CAAC"}
@@ -0,0 +1,8 @@
1
+ export interface SessionEvent {
2
+ hook: string;
3
+ ts: string;
4
+ [key: string]: unknown;
5
+ }
6
+ export declare function appendSessionEvent(conversationId: string, event: SessionEvent): void;
7
+ export declare function readSessionEvents(conversationId: string): SessionEvent[];
8
+ export declare function deleteSessionEvents(conversationId: string): void;
@@ -0,0 +1,44 @@
1
+ import { readFileSync, appendFileSync, mkdirSync, unlinkSync } from "fs";
2
+ import { join } from "path";
3
+ import { getConfigDir } from "./config.js";
4
+ function sessionsDir() {
5
+ return join(getConfigDir(), "sessions");
6
+ }
7
+ function eventsFilePath(conversationId) {
8
+ return join(sessionsDir(), `${conversationId}.events.jsonl`);
9
+ }
10
+ export function appendSessionEvent(conversationId, event) {
11
+ const dir = sessionsDir();
12
+ mkdirSync(dir, { recursive: true });
13
+ appendFileSync(eventsFilePath(conversationId), JSON.stringify(event) + "\n");
14
+ }
15
+ export function readSessionEvents(conversationId) {
16
+ try {
17
+ const raw = readFileSync(eventsFilePath(conversationId), "utf-8");
18
+ return raw
19
+ .trim()
20
+ .split("\n")
21
+ .filter(Boolean)
22
+ .map((line) => {
23
+ try {
24
+ return JSON.parse(line);
25
+ }
26
+ catch {
27
+ return null;
28
+ }
29
+ })
30
+ .filter((e) => e !== null);
31
+ }
32
+ catch {
33
+ return [];
34
+ }
35
+ }
36
+ export function deleteSessionEvents(conversationId) {
37
+ try {
38
+ unlinkSync(eventsFilePath(conversationId));
39
+ }
40
+ catch {
41
+ // Already removed or never created
42
+ }
43
+ }
44
+ //# sourceMappingURL=session-events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-events.js","sourceRoot":"","sources":["../../src/lib/session-events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAiB,cAAc,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,IAAI,CAAA;AACvF,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAQ1C,SAAS,WAAW;IAClB,OAAO,IAAI,CAAC,YAAY,EAAE,EAAE,UAAU,CAAC,CAAA;AACzC,CAAC;AAED,SAAS,cAAc,CAAC,cAAsB;IAC5C,OAAO,IAAI,CAAC,WAAW,EAAE,EAAE,GAAG,cAAc,eAAe,CAAC,CAAA;AAC9D,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,cAAsB,EAAE,KAAmB;IAC5E,MAAM,GAAG,GAAG,WAAW,EAAE,CAAA;IACzB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACnC,cAAc,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAA;AAC9E,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,cAAsB;IACtD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,CAAA;QACjE,OAAO,GAAG;aACP,IAAI,EAAE;aACN,KAAK,CAAC,IAAI,CAAC;aACX,MAAM,CAAC,OAAO,CAAC;aACf,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YACZ,IAAI,CAAC;gBACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAiB,CAAA;YACzC,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,CAAA;YACb,CAAC;QACH,CAAC,CAAC;aACD,MAAM,CAAC,CAAC,CAAC,EAAqB,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAA;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAA;IACX,CAAC;AACH,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,cAAsB;IACxD,IAAI,CAAC;QACH,UAAU,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC,CAAA;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,mCAAmC;IACrC,CAAC;AACH,CAAC"}
@@ -0,0 +1,11 @@
1
+ export interface SessionState {
2
+ conversationId: string;
3
+ dashboardSessionId: string;
4
+ projectId: string;
5
+ source: "cursor" | "claude";
6
+ workspacePath: string;
7
+ startedAt: string;
8
+ }
9
+ export declare function saveSessionState(state: SessionState): void;
10
+ export declare function loadSessionState(conversationId: string): SessionState | null;
11
+ export declare function removeSessionState(conversationId: string): void;
@@ -0,0 +1,31 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, unlinkSync } from "fs";
2
+ import { join } from "path";
3
+ import { getConfigDir } from "./config.js";
4
+ function stateDir() {
5
+ return join(getConfigDir(), "state");
6
+ }
7
+ function stateFilePath(conversationId) {
8
+ return join(stateDir(), `${conversationId}.json`);
9
+ }
10
+ export function saveSessionState(state) {
11
+ const dir = stateDir();
12
+ mkdirSync(dir, { recursive: true });
13
+ writeFileSync(stateFilePath(state.conversationId), JSON.stringify(state, null, 2) + "\n");
14
+ }
15
+ export function loadSessionState(conversationId) {
16
+ try {
17
+ return JSON.parse(readFileSync(stateFilePath(conversationId), "utf-8"));
18
+ }
19
+ catch {
20
+ return null;
21
+ }
22
+ }
23
+ export function removeSessionState(conversationId) {
24
+ try {
25
+ unlinkSync(stateFilePath(conversationId));
26
+ }
27
+ catch {
28
+ // Already removed
29
+ }
30
+ }
31
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/lib/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,IAAI,CAAA;AACvE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAW1C,SAAS,QAAQ;IACf,OAAO,IAAI,CAAC,YAAY,EAAE,EAAE,OAAO,CAAC,CAAA;AACtC,CAAC;AAED,SAAS,aAAa,CAAC,cAAsB;IAC3C,OAAO,IAAI,CAAC,QAAQ,EAAE,EAAE,GAAG,cAAc,OAAO,CAAC,CAAA;AACnD,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAmB;IAClD,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAA;IACtB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACnC,aAAa,CAAC,aAAa,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;AAC3F,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,cAAsB;IACrD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,aAAa,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,CAAiB,CAAA;IACzF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,cAAsB;IACvD,IAAI,CAAC;QACH,UAAU,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC,CAAA;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,kBAAkB;IACpB,CAAC;AACH,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Sanitize a path the same way Cursor does for its project directory names.
3
+ * Strips leading `/`, replaces all non-alphanumeric chars with `-`.
4
+ */
5
+ export declare function sanitizePathForCursor(path: string): string;
6
+ /**
7
+ * Resolve the transcript file path for a Cursor session.
8
+ * Cursor IDE uses nested layout: <dir>/<id>/<id>.jsonl
9
+ * Cursor CLI uses flat layout: <dir>/<id>.jsonl
10
+ */
11
+ export declare function resolveTranscriptPath(workspacePath: string, conversationId: string): string | null;
12
+ /**
13
+ * Resolve Claude Code transcript path for a session.
14
+ */
15
+ export declare function resolveClaudeTranscriptPath(workspacePath: string, sessionId: string): string | null;
@@ -0,0 +1,44 @@
1
+ import { existsSync } from "fs";
2
+ import { join } from "path";
3
+ import { homedir } from "os";
4
+ /**
5
+ * Sanitize a path the same way Cursor does for its project directory names.
6
+ * Strips leading `/`, replaces all non-alphanumeric chars with `-`.
7
+ */
8
+ export function sanitizePathForCursor(path) {
9
+ return path.replace(/^\//, "").replace(/[^a-zA-Z0-9]/g, "-");
10
+ }
11
+ /**
12
+ * Resolve the transcript file path for a Cursor session.
13
+ * Cursor IDE uses nested layout: <dir>/<id>/<id>.jsonl
14
+ * Cursor CLI uses flat layout: <dir>/<id>.jsonl
15
+ */
16
+ export function resolveTranscriptPath(workspacePath, conversationId) {
17
+ const sanitized = sanitizePathForCursor(workspacePath);
18
+ const sessionDir = join(homedir(), ".cursor", "projects", sanitized, "agent-transcripts");
19
+ // Nested layout (IDE): <dir>/<id>/<id>.jsonl
20
+ const nested = join(sessionDir, conversationId, `${conversationId}.jsonl`);
21
+ if (existsSync(nested))
22
+ return nested;
23
+ // Flat layout (CLI): <dir>/<id>.jsonl
24
+ const flat = join(sessionDir, `${conversationId}.jsonl`);
25
+ if (existsSync(flat))
26
+ return flat;
27
+ // Check if nested directory exists (file may be written momentarily)
28
+ const nestedDir = join(sessionDir, conversationId);
29
+ if (existsSync(nestedDir))
30
+ return nested;
31
+ return null;
32
+ }
33
+ /**
34
+ * Resolve Claude Code transcript path for a session.
35
+ */
36
+ export function resolveClaudeTranscriptPath(workspacePath, sessionId) {
37
+ const sanitized = sanitizePathForCursor(workspacePath);
38
+ const claudeDir = join(homedir(), ".claude", "projects", sanitized);
39
+ const jsonlPath = join(claudeDir, `${sessionId}.jsonl`);
40
+ if (existsSync(jsonlPath))
41
+ return jsonlPath;
42
+ return null;
43
+ }
44
+ //# sourceMappingURL=transcript.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transcript.js","sourceRoot":"","sources":["../../src/lib/transcript.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAA;AAC/B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAA;AAE5B;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC,CAAA;AAC9D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CACnC,aAAqB,EACrB,cAAsB;IAEtB,MAAM,SAAS,GAAG,qBAAqB,CAAC,aAAa,CAAC,CAAA;IACtD,MAAM,UAAU,GAAG,IAAI,CACrB,OAAO,EAAE,EACT,SAAS,EACT,UAAU,EACV,SAAS,EACT,mBAAmB,CACpB,CAAA;IAED,6CAA6C;IAC7C,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,EAAE,GAAG,cAAc,QAAQ,CAAC,CAAA;IAC1E,IAAI,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAA;IAErC,sCAAsC;IACtC,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,EAAE,GAAG,cAAc,QAAQ,CAAC,CAAA;IACxD,IAAI,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAEjC,qEAAqE;IACrE,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAA;IAClD,IAAI,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,MAAM,CAAA;IAExC,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,2BAA2B,CACzC,aAAqB,EACrB,SAAiB;IAEjB,MAAM,SAAS,GAAG,qBAAqB,CAAC,aAAa,CAAC,CAAA;IACtD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,CAAC,CAAA;IAEnE,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,GAAG,SAAS,QAAQ,CAAC,CAAA;IACvD,IAAI,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAA;IAE3C,OAAO,IAAI,CAAA;AACb,CAAC"}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "slait.dev",
3
+ "version": "0.1.0",
4
+ "description": "Automatically capture your Cursor and Claude AI coding sessions for analysis on the Slait dashboard",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "slait",
9
+ "ai",
10
+ "cursor",
11
+ "claude",
12
+ "coding-sessions",
13
+ "developer-tools",
14
+ "cli"
15
+ ],
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/slait-dev/slait-cli"
19
+ },
20
+ "bin": {
21
+ "slait": "dist/index.js"
22
+ },
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "scripts": {
27
+ "build": "tsc && chmod +x dist/index.js",
28
+ "dev": "tsc --watch"
29
+ },
30
+ "dependencies": {
31
+ "commander": "^13.1.0"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^22",
35
+ "typescript": "5.9.3"
36
+ },
37
+ "engines": {
38
+ "node": ">=18"
39
+ }
40
+ }