shadok-ai 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 (66) hide show
  1. package/README.md +196 -0
  2. package/dist/args.d.ts +8 -0
  3. package/dist/args.js +17 -0
  4. package/dist/args.js.map +1 -0
  5. package/dist/channels.d.ts +61 -0
  6. package/dist/channels.js +169 -0
  7. package/dist/channels.js.map +1 -0
  8. package/dist/cli.d.ts +2 -0
  9. package/dist/cli.js +100 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/config.d.ts +31 -0
  12. package/dist/config.js +59 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/detect.d.ts +17 -0
  15. package/dist/detect.js +41 -0
  16. package/dist/detect.js.map +1 -0
  17. package/dist/extract.d.ts +55 -0
  18. package/dist/extract.js +269 -0
  19. package/dist/extract.js.map +1 -0
  20. package/dist/main.d.ts +2 -0
  21. package/dist/main.js +90 -0
  22. package/dist/main.js.map +1 -0
  23. package/dist/pace.d.ts +33 -0
  24. package/dist/pace.js +53 -0
  25. package/dist/pace.js.map +1 -0
  26. package/dist/retry.d.ts +15 -0
  27. package/dist/retry.js +40 -0
  28. package/dist/retry.js.map +1 -0
  29. package/dist/secrets.d.ts +21 -0
  30. package/dist/secrets.js +71 -0
  31. package/dist/secrets.js.map +1 -0
  32. package/dist/server.d.ts +1 -0
  33. package/dist/server.js +854 -0
  34. package/dist/server.js.map +1 -0
  35. package/dist/session.d.ts +74 -0
  36. package/dist/session.js +244 -0
  37. package/dist/session.js.map +1 -0
  38. package/dist/setup-prompt.d.ts +6 -0
  39. package/dist/setup-prompt.js +25 -0
  40. package/dist/setup-prompt.js.map +1 -0
  41. package/dist/supervisor.d.ts +55 -0
  42. package/dist/supervisor.js +61 -0
  43. package/dist/supervisor.js.map +1 -0
  44. package/dist/tail.d.ts +66 -0
  45. package/dist/tail.js +244 -0
  46. package/dist/tail.js.map +1 -0
  47. package/dist/telegram.d.ts +48 -0
  48. package/dist/telegram.js +565 -0
  49. package/dist/telegram.js.map +1 -0
  50. package/dist/tmux.d.ts +69 -0
  51. package/dist/tmux.js +267 -0
  52. package/dist/tmux.js.map +1 -0
  53. package/dist/update-flag.d.ts +13 -0
  54. package/dist/update-flag.js +36 -0
  55. package/dist/update-flag.js.map +1 -0
  56. package/dist/updater.d.ts +18 -0
  57. package/dist/updater.js +45 -0
  58. package/dist/updater.js.map +1 -0
  59. package/dist/usage.d.ts +23 -0
  60. package/dist/usage.js +125 -0
  61. package/dist/usage.js.map +1 -0
  62. package/dist/worktree.d.ts +63 -0
  63. package/dist/worktree.js +153 -0
  64. package/dist/worktree.js.map +1 -0
  65. package/package.json +44 -0
  66. package/public/index.html +2583 -0
package/dist/tail.js ADDED
@@ -0,0 +1,244 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ /** Max characters of a tool result to stream (long outputs are truncated). */
5
+ const MAX_RESULT = 4000;
6
+ /**
7
+ * Path of the .jsonl transcript Claude Code writes for a session.
8
+ *
9
+ * The obvious path is `<encoded cwd>/<id>.jsonl`, but Claude derives the
10
+ * encoded dir from the cwd it was LAUNCHED with. After a repo/worktree move
11
+ * (or the shadok-ai rename) a still-running agent keeps writing under its
12
+ * original encoded dir, which no longer matches the current cwd — so the tail
13
+ * would watch a stale file and nothing streams. To be immune to that drift we
14
+ * prefer the newest `<id>.jsonl` found anywhere under ~/.claude/projects; the
15
+ * file being actively appended is always the newest. Falls back to the
16
+ * cwd-derived path for a brand-new session whose file doesn't exist yet.
17
+ */
18
+ export function sessionFilePath(cwd, sessionId) {
19
+ const newest = newestTranscriptById(sessionId);
20
+ if (newest)
21
+ return newest;
22
+ const encoded = path.resolve(cwd).replace(/[^a-zA-Z0-9]/g, "-");
23
+ return path.join(os.homedir(), ".claude", "projects", encoded, sessionId + ".jsonl");
24
+ }
25
+ /** The most-recently-modified `<id>.jsonl` across all project dirs, or null. */
26
+ export function newestTranscriptById(sessionId) {
27
+ const root = path.join(os.homedir(), ".claude", "projects");
28
+ let best = null;
29
+ let bestMtime = -1;
30
+ let dirs;
31
+ try {
32
+ dirs = fs.readdirSync(root);
33
+ }
34
+ catch {
35
+ return null;
36
+ }
37
+ for (const d of dirs) {
38
+ const f = path.join(root, d, sessionId + ".jsonl");
39
+ try {
40
+ const m = fs.statSync(f).mtimeMs;
41
+ if (m > bestMtime) {
42
+ bestMtime = m;
43
+ best = f;
44
+ }
45
+ }
46
+ catch {
47
+ /* no such file in this dir */
48
+ }
49
+ }
50
+ return best;
51
+ }
52
+ /** One-line summary of a tool_use block (e.g. `Read auth.ts`, `Bash: npm test`). */
53
+ function toolSummary(input) {
54
+ if (!input || typeof input !== "object")
55
+ return "";
56
+ const v = input.file_path ?? input.path ?? input.command ?? input.pattern ?? input.query ?? input.url ?? input.description;
57
+ if (typeof v !== "string")
58
+ return "";
59
+ const s = v.replace(/\s+/g, " ").trim();
60
+ return s.length > 80 ? s.slice(0, 79) + "…" : s;
61
+ }
62
+ /**
63
+ * Tails a session .jsonl: watches for appended lines and yields the assistant
64
+ * `text` and `tool_use` blocks as they are written — the authoritative,
65
+ * untruncated content, streamed at message granularity.
66
+ *
67
+ * `onEvent` fires for each new block. Returns a stop() function.
68
+ * Starts from the current end of file, so only NEW turns are streamed
69
+ * (existing history is replayed separately via loadHistory).
70
+ */
71
+ export function tailSession(file, onEvent, intervalMs = 250) {
72
+ let pos = 0;
73
+ try {
74
+ pos = fs.statSync(file).size; // start at EOF: only stream what comes next
75
+ }
76
+ catch {
77
+ pos = 0; // file not written yet (new session) — stream from the start
78
+ }
79
+ let buf = "";
80
+ let stopped = false;
81
+ const read = () => {
82
+ if (stopped)
83
+ return;
84
+ let size;
85
+ try {
86
+ size = fs.statSync(file).size;
87
+ }
88
+ catch {
89
+ return; // file not there yet
90
+ }
91
+ if (size < pos) {
92
+ // File shrank/rotated (rare) — reset.
93
+ pos = 0;
94
+ buf = "";
95
+ }
96
+ if (size === pos)
97
+ return;
98
+ let chunk = "";
99
+ try {
100
+ const fd = fs.openSync(file, "r");
101
+ try {
102
+ const b = Buffer.alloc(size - pos);
103
+ const n = fs.readSync(fd, b, 0, b.length, pos);
104
+ chunk = b.toString("utf8", 0, n);
105
+ pos += n;
106
+ }
107
+ finally {
108
+ fs.closeSync(fd);
109
+ }
110
+ }
111
+ catch {
112
+ return;
113
+ }
114
+ buf += chunk;
115
+ let nl;
116
+ while ((nl = buf.indexOf("\n")) >= 0) {
117
+ const line = buf.slice(0, nl);
118
+ buf = buf.slice(nl + 1);
119
+ emitLine(line, onEvent);
120
+ }
121
+ };
122
+ const timer = setInterval(read, intervalMs);
123
+ return () => {
124
+ stopped = true;
125
+ clearInterval(timer);
126
+ };
127
+ }
128
+ function emitLine(line, onEvent) {
129
+ for (const ev of parseLine(line))
130
+ onEvent(ev);
131
+ }
132
+ /**
133
+ * Pure parser for one transcript line → the events it yields. Exported for
134
+ * tests; `tailSession` streams these live. Returns [] for anything that isn't
135
+ * a streamable assistant/user event.
136
+ */
137
+ export function parseLine(line) {
138
+ if (!line.trim())
139
+ return [];
140
+ let e;
141
+ try {
142
+ e = JSON.parse(line);
143
+ }
144
+ catch {
145
+ return [];
146
+ }
147
+ if (e.isMeta || !Array.isArray(e.message?.content))
148
+ return [];
149
+ const out = [];
150
+ if (e.type === "assistant") {
151
+ const usage = parseUsage(e.message);
152
+ if (usage)
153
+ out.push({ kind: "usage", messageId: e.message.id ?? e.uuid, usage });
154
+ for (const block of e.message.content) {
155
+ if (block?.type === "text" && typeof block.text === "string" && block.text.trim()) {
156
+ out.push({ kind: "text", text: block.text });
157
+ }
158
+ else if (block?.type === "tool_use" && typeof block.name === "string") {
159
+ out.push({
160
+ kind: "tool",
161
+ id: typeof block.id === "string" ? block.id : "",
162
+ name: block.name,
163
+ summary: toolSummary(block.input),
164
+ });
165
+ }
166
+ // `thinking` blocks are intentionally skipped.
167
+ }
168
+ }
169
+ else if (e.type === "user") {
170
+ // User events carry tool results (command output, file reads…). The real
171
+ // user prompt is echoed by the server itself, so only results are emitted.
172
+ for (const block of e.message.content) {
173
+ if (block?.type !== "tool_result")
174
+ continue;
175
+ const text = resultText(block.content);
176
+ if (text) {
177
+ out.push({
178
+ kind: "result",
179
+ toolUseId: typeof block.tool_use_id === "string" ? block.tool_use_id : "",
180
+ text,
181
+ isError: !!block.is_error,
182
+ });
183
+ }
184
+ }
185
+ }
186
+ return out;
187
+ }
188
+ export function parseUsage(message) {
189
+ const u = message?.usage;
190
+ if (!u || typeof u !== "object")
191
+ return null;
192
+ return {
193
+ input: u.input_tokens ?? 0,
194
+ output: u.output_tokens ?? 0,
195
+ cacheCreation: u.cache_creation_input_tokens ?? 0,
196
+ cacheRead: u.cache_read_input_tokens ?? 0,
197
+ };
198
+ }
199
+ /**
200
+ * Sums the token usage already written to a transcript, keyed by message id.
201
+ * Streaming writes several records per message with the same id and growing
202
+ * counts — keeping the last record per id yields each message's final usage.
203
+ */
204
+ export function scanUsage(file) {
205
+ const map = new Map();
206
+ let raw;
207
+ try {
208
+ raw = fs.readFileSync(file, "utf8");
209
+ }
210
+ catch {
211
+ return map; // new session: nothing written yet
212
+ }
213
+ for (const line of raw.split("\n")) {
214
+ if (!line.trim())
215
+ continue;
216
+ let e;
217
+ try {
218
+ e = JSON.parse(line);
219
+ }
220
+ catch {
221
+ continue;
222
+ }
223
+ if (e.type !== "assistant")
224
+ continue;
225
+ const usage = parseUsage(e.message);
226
+ if (usage)
227
+ map.set(e.message.id ?? e.uuid, usage);
228
+ }
229
+ return map;
230
+ }
231
+ /** Flattens a tool_result's content (string or block array) to display text. */
232
+ export function resultText(content) {
233
+ let s = "";
234
+ if (typeof content === "string")
235
+ s = content;
236
+ else if (Array.isArray(content))
237
+ s = content
238
+ .filter((b) => b?.type === "text" && typeof b.text === "string")
239
+ .map((b) => b.text)
240
+ .join("\n");
241
+ s = s.trimEnd();
242
+ return s.length > MAX_RESULT ? s.slice(0, MAX_RESULT) + "\n… (truncated)" : s;
243
+ }
244
+ //# sourceMappingURL=tail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tail.js","sourceRoot":"","sources":["../src/tail.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /** The binding key for a chat + optional forum topic thread. */
2
+ export declare function bindKey(chat: {
3
+ id: number;
4
+ type: string;
5
+ }, threadId?: number): string;
6
+ /** Split text into Telegram-sized chunks, preferring line boundaries. */
7
+ export declare function chunk(text: string, max?: number): string[];
8
+ /** Parse a leading bot command: "/spawn foo" → {cmd:"spawn", arg:"foo"}. */
9
+ export declare function parseCommand(text: string): {
10
+ cmd: string;
11
+ arg: string;
12
+ } | null;
13
+ /** Telegram's "typing…" indicator lives ~5 s per sendChatAction and dies on
14
+ * every sendMessage — a single beat can't cover a turn that runs for minutes.
15
+ * This keeps it alive: an immediate beat, then one every `intervalMs` until
16
+ * stop(). start() while beating is a no-op; stop() is idempotent. */
17
+ export declare function makeTyping(beat: () => void, intervalMs?: number): {
18
+ start: () => void;
19
+ stop: () => void;
20
+ };
21
+ interface DialogOption {
22
+ n: number;
23
+ label: string;
24
+ hint?: string;
25
+ checked?: boolean;
26
+ }
27
+ interface Dialog {
28
+ question: string;
29
+ options: DialogOption[];
30
+ multi: boolean;
31
+ }
32
+ /** Inline keyboard for a TUI dialog. Single-select → one button per option
33
+ * (choose); multi-select → toggle buttons (with ☑/☐) + a Submit row. */
34
+ export declare function dialogKeyboard(d: Dialog): {
35
+ inline_keyboard: {
36
+ text: string;
37
+ callback_data: string;
38
+ }[][];
39
+ };
40
+ /** Parse a dialog callback_data → an action for the WS. */
41
+ export declare function parseCallback(data: string): {
42
+ kind: "choose" | "toggle" | "confirm";
43
+ n?: number;
44
+ } | null;
45
+ export declare function renameTelegramTopic(chatId: number, threadId: number, name: string): void;
46
+ export declare function closeTelegramTopic(chatId: number, threadId: number): void;
47
+ export declare function startTelegram(port: number): void;
48
+ export {};