skein-cli 0.1.0-alpha.1

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 (250) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +180 -0
  3. package/bin/skein.mjs +7 -0
  4. package/dist/adapters/aider/conv-reader.d.ts +7 -0
  5. package/dist/adapters/aider/conv-reader.js +203 -0
  6. package/dist/adapters/aider/conv-reader.js.map +1 -0
  7. package/dist/adapters/aider/index.d.ts +2 -0
  8. package/dist/adapters/aider/index.js +123 -0
  9. package/dist/adapters/aider/index.js.map +1 -0
  10. package/dist/adapters/base.d.ts +71 -0
  11. package/dist/adapters/base.js +38 -0
  12. package/dist/adapters/base.js.map +1 -0
  13. package/dist/adapters/claude-code/conv-reader.d.ts +2 -0
  14. package/dist/adapters/claude-code/conv-reader.js +155 -0
  15. package/dist/adapters/claude-code/conv-reader.js.map +1 -0
  16. package/dist/adapters/claude-code/index.d.ts +3 -0
  17. package/dist/adapters/claude-code/index.js +517 -0
  18. package/dist/adapters/claude-code/index.js.map +1 -0
  19. package/dist/adapters/claude-desktop/index.d.ts +2 -0
  20. package/dist/adapters/claude-desktop/index.js +95 -0
  21. package/dist/adapters/claude-desktop/index.js.map +1 -0
  22. package/dist/adapters/codex/index.d.ts +2 -0
  23. package/dist/adapters/codex/index.js +472 -0
  24. package/dist/adapters/codex/index.js.map +1 -0
  25. package/dist/adapters/cursor/index.d.ts +2 -0
  26. package/dist/adapters/cursor/index.js +255 -0
  27. package/dist/adapters/cursor/index.js.map +1 -0
  28. package/dist/adapters/opencode/conv-reader.d.ts +3 -0
  29. package/dist/adapters/opencode/conv-reader.js +190 -0
  30. package/dist/adapters/opencode/conv-reader.js.map +1 -0
  31. package/dist/adapters/opencode/index.d.ts +2 -0
  32. package/dist/adapters/opencode/index.js +349 -0
  33. package/dist/adapters/opencode/index.js.map +1 -0
  34. package/dist/adapters/registry.d.ts +4 -0
  35. package/dist/adapters/registry.js +26 -0
  36. package/dist/adapters/registry.js.map +1 -0
  37. package/dist/cli.d.ts +3 -0
  38. package/dist/cli.js +54 -0
  39. package/dist/cli.js.map +1 -0
  40. package/dist/commands/conv.d.ts +2 -0
  41. package/dist/commands/conv.js +261 -0
  42. package/dist/commands/conv.js.map +1 -0
  43. package/dist/commands/doctor.d.ts +2 -0
  44. package/dist/commands/doctor.js +85 -0
  45. package/dist/commands/doctor.js.map +1 -0
  46. package/dist/commands/init.d.ts +2 -0
  47. package/dist/commands/init.js +38 -0
  48. package/dist/commands/init.js.map +1 -0
  49. package/dist/commands/memory.d.ts +2 -0
  50. package/dist/commands/memory.js +197 -0
  51. package/dist/commands/memory.js.map +1 -0
  52. package/dist/commands/migrate.d.ts +2 -0
  53. package/dist/commands/migrate.js +102 -0
  54. package/dist/commands/migrate.js.map +1 -0
  55. package/dist/commands/profile.d.ts +2 -0
  56. package/dist/commands/profile.js +183 -0
  57. package/dist/commands/profile.js.map +1 -0
  58. package/dist/commands/redact.d.ts +2 -0
  59. package/dist/commands/redact.js +73 -0
  60. package/dist/commands/redact.js.map +1 -0
  61. package/dist/commands/trace.d.ts +2 -0
  62. package/dist/commands/trace.js +181 -0
  63. package/dist/commands/trace.js.map +1 -0
  64. package/dist/commands/view.d.ts +13 -0
  65. package/dist/commands/view.js +184 -0
  66. package/dist/commands/view.js.map +1 -0
  67. package/dist/commands/watch.d.ts +6 -0
  68. package/dist/commands/watch.js +61 -0
  69. package/dist/commands/watch.js.map +1 -0
  70. package/dist/commands/wrap.d.ts +2 -0
  71. package/dist/commands/wrap.js +131 -0
  72. package/dist/commands/wrap.js.map +1 -0
  73. package/dist/conv/chatgpt-import.d.ts +33 -0
  74. package/dist/conv/chatgpt-import.js +145 -0
  75. package/dist/conv/chatgpt-import.js.map +1 -0
  76. package/dist/conv/cursor.d.ts +12 -0
  77. package/dist/conv/cursor.js +55 -0
  78. package/dist/conv/cursor.js.map +1 -0
  79. package/dist/conv/export.d.ts +40 -0
  80. package/dist/conv/export.js +215 -0
  81. package/dist/conv/export.js.map +1 -0
  82. package/dist/conv/replay/aider.d.ts +24 -0
  83. package/dist/conv/replay/aider.js +56 -0
  84. package/dist/conv/replay/aider.js.map +1 -0
  85. package/dist/conv/replay/claude-code.d.ts +38 -0
  86. package/dist/conv/replay/claude-code.js +80 -0
  87. package/dist/conv/replay/claude-code.js.map +1 -0
  88. package/dist/conv/replay/context.d.ts +25 -0
  89. package/dist/conv/replay/context.js +63 -0
  90. package/dist/conv/replay/context.js.map +1 -0
  91. package/dist/conv/replay/index.d.ts +22 -0
  92. package/dist/conv/replay/index.js +84 -0
  93. package/dist/conv/replay/index.js.map +1 -0
  94. package/dist/conv/search.d.ts +67 -0
  95. package/dist/conv/search.js +379 -0
  96. package/dist/conv/search.js.map +1 -0
  97. package/dist/conv/sink.d.ts +26 -0
  98. package/dist/conv/sink.js +76 -0
  99. package/dist/conv/sink.js.map +1 -0
  100. package/dist/conv/sources/aider.d.ts +13 -0
  101. package/dist/conv/sources/aider.js +95 -0
  102. package/dist/conv/sources/aider.js.map +1 -0
  103. package/dist/conv/sources/claude-code.d.ts +13 -0
  104. package/dist/conv/sources/claude-code.js +189 -0
  105. package/dist/conv/sources/claude-code.js.map +1 -0
  106. package/dist/conv/sources/codex.d.ts +15 -0
  107. package/dist/conv/sources/codex.js +175 -0
  108. package/dist/conv/sources/codex.js.map +1 -0
  109. package/dist/conv/sources/opencode.d.ts +12 -0
  110. package/dist/conv/sources/opencode.js +92 -0
  111. package/dist/conv/sources/opencode.js.map +1 -0
  112. package/dist/conv/store.d.ts +10 -0
  113. package/dist/conv/store.js +75 -0
  114. package/dist/conv/store.js.map +1 -0
  115. package/dist/conv/title.d.ts +9 -0
  116. package/dist/conv/title.js +74 -0
  117. package/dist/conv/title.js.map +1 -0
  118. package/dist/conv/watcher.d.ts +28 -0
  119. package/dist/conv/watcher.js +75 -0
  120. package/dist/conv/watcher.js.map +1 -0
  121. package/dist/conv/zip.d.ts +11 -0
  122. package/dist/conv/zip.js +82 -0
  123. package/dist/conv/zip.js.map +1 -0
  124. package/dist/hf/client.d.ts +41 -0
  125. package/dist/hf/client.js +87 -0
  126. package/dist/hf/client.js.map +1 -0
  127. package/dist/index.d.ts +5 -0
  128. package/dist/index.js +6 -0
  129. package/dist/index.js.map +1 -0
  130. package/dist/ir/index.d.ts +24 -0
  131. package/dist/ir/index.js +23 -0
  132. package/dist/ir/index.js.map +1 -0
  133. package/dist/ir/profile.d.ts +55 -0
  134. package/dist/ir/profile.js +177 -0
  135. package/dist/ir/profile.js.map +1 -0
  136. package/dist/memory/index.d.ts +41 -0
  137. package/dist/memory/index.js +109 -0
  138. package/dist/memory/index.js.map +1 -0
  139. package/dist/memory/search.d.ts +24 -0
  140. package/dist/memory/search.js +114 -0
  141. package/dist/memory/search.js.map +1 -0
  142. package/dist/memory/sinks/claude-mem.d.ts +13 -0
  143. package/dist/memory/sinks/claude-mem.js +100 -0
  144. package/dist/memory/sinks/claude-mem.js.map +1 -0
  145. package/dist/memory/sinks/codex.d.ts +12 -0
  146. package/dist/memory/sinks/codex.js +82 -0
  147. package/dist/memory/sinks/codex.js.map +1 -0
  148. package/dist/memory/sinks/context.d.ts +21 -0
  149. package/dist/memory/sinks/context.js +42 -0
  150. package/dist/memory/sinks/context.js.map +1 -0
  151. package/dist/memory/sources/chatgpt.d.ts +22 -0
  152. package/dist/memory/sources/chatgpt.js +98 -0
  153. package/dist/memory/sources/chatgpt.js.map +1 -0
  154. package/dist/memory/sources/claude-mem.d.ts +8 -0
  155. package/dist/memory/sources/claude-mem.js +104 -0
  156. package/dist/memory/sources/claude-mem.js.map +1 -0
  157. package/dist/memory/sources/codex.d.ts +8 -0
  158. package/dist/memory/sources/codex.js +77 -0
  159. package/dist/memory/sources/codex.js.map +1 -0
  160. package/dist/memory/store.d.ts +19 -0
  161. package/dist/memory/store.js +82 -0
  162. package/dist/memory/store.js.map +1 -0
  163. package/dist/proxy/http.d.ts +21 -0
  164. package/dist/proxy/http.js +205 -0
  165. package/dist/proxy/http.js.map +1 -0
  166. package/dist/proxy/recorder.d.ts +35 -0
  167. package/dist/proxy/recorder.js +221 -0
  168. package/dist/proxy/recorder.js.map +1 -0
  169. package/dist/proxy/streaming.d.ts +33 -0
  170. package/dist/proxy/streaming.js +185 -0
  171. package/dist/proxy/streaming.js.map +1 -0
  172. package/dist/redactor/entropy.d.ts +29 -0
  173. package/dist/redactor/entropy.js +98 -0
  174. package/dist/redactor/entropy.js.map +1 -0
  175. package/dist/redactor/index.d.ts +52 -0
  176. package/dist/redactor/index.js +152 -0
  177. package/dist/redactor/index.js.map +1 -0
  178. package/dist/redactor/ner.d.ts +53 -0
  179. package/dist/redactor/ner.js +97 -0
  180. package/dist/redactor/ner.js.map +1 -0
  181. package/dist/redactor/pii-patterns.d.ts +22 -0
  182. package/dist/redactor/pii-patterns.js +187 -0
  183. package/dist/redactor/pii-patterns.js.map +1 -0
  184. package/dist/redactor/secret-patterns.d.ts +27 -0
  185. package/dist/redactor/secret-patterns.js +475 -0
  186. package/dist/redactor/secret-patterns.js.map +1 -0
  187. package/dist/schema/conv.d.ts +698 -0
  188. package/dist/schema/conv.js +85 -0
  189. package/dist/schema/conv.js.map +1 -0
  190. package/dist/schema/index.d.ts +2 -0
  191. package/dist/schema/index.js +3 -0
  192. package/dist/schema/index.js.map +1 -0
  193. package/dist/schema/manifest.d.ts +1531 -0
  194. package/dist/schema/manifest.js +179 -0
  195. package/dist/schema/manifest.js.map +1 -0
  196. package/dist/schema/memory.d.ts +107 -0
  197. package/dist/schema/memory.js +45 -0
  198. package/dist/schema/memory.js.map +1 -0
  199. package/dist/schema/trace.d.ts +164 -0
  200. package/dist/schema/trace.js +89 -0
  201. package/dist/schema/trace.js.map +1 -0
  202. package/dist/trace/consent.d.ts +30 -0
  203. package/dist/trace/consent.js +60 -0
  204. package/dist/trace/consent.js.map +1 -0
  205. package/dist/trace/extract.d.ts +22 -0
  206. package/dist/trace/extract.js +168 -0
  207. package/dist/trace/extract.js.map +1 -0
  208. package/dist/trace/ml-pii.d.ts +33 -0
  209. package/dist/trace/ml-pii.js +35 -0
  210. package/dist/trace/ml-pii.js.map +1 -0
  211. package/dist/trace/push.d.ts +59 -0
  212. package/dist/trace/push.js +141 -0
  213. package/dist/trace/push.js.map +1 -0
  214. package/dist/trace/serialize.d.ts +23 -0
  215. package/dist/trace/serialize.js +67 -0
  216. package/dist/trace/serialize.js.map +1 -0
  217. package/dist/ui/banner.d.ts +2 -0
  218. package/dist/ui/banner.js +17 -0
  219. package/dist/ui/banner.js.map +1 -0
  220. package/dist/ui/box.d.ts +4 -0
  221. package/dist/ui/box.js +38 -0
  222. package/dist/ui/box.js.map +1 -0
  223. package/dist/ui/index.d.ts +5 -0
  224. package/dist/ui/index.js +5 -0
  225. package/dist/ui/index.js.map +1 -0
  226. package/dist/ui/spinner.d.ts +10 -0
  227. package/dist/ui/spinner.js +44 -0
  228. package/dist/ui/spinner.js.map +1 -0
  229. package/dist/ui/table.d.ts +9 -0
  230. package/dist/ui/table.js +55 -0
  231. package/dist/ui/table.js.map +1 -0
  232. package/dist/util/frontmatter.d.ts +6 -0
  233. package/dist/util/frontmatter.js +20 -0
  234. package/dist/util/frontmatter.js.map +1 -0
  235. package/dist/util/fs.d.ts +5 -0
  236. package/dist/util/fs.js +41 -0
  237. package/dist/util/fs.js.map +1 -0
  238. package/dist/util/ids.d.ts +3 -0
  239. package/dist/util/ids.js +16 -0
  240. package/dist/util/ids.js.map +1 -0
  241. package/dist/util/log.d.ts +13 -0
  242. package/dist/util/log.js +33 -0
  243. package/dist/util/log.js.map +1 -0
  244. package/dist/util/paths.d.ts +18 -0
  245. package/dist/util/paths.js +36 -0
  246. package/dist/util/paths.js.map +1 -0
  247. package/dist/version.d.ts +5 -0
  248. package/dist/version.js +32 -0
  249. package/dist/version.js.map +1 -0
  250. package/package.json +86 -0
@@ -0,0 +1,12 @@
1
+ export declare class CursorStore {
2
+ private readonly path;
3
+ private cache;
4
+ private loaded;
5
+ constructor(path?: string);
6
+ load(): Promise<void>;
7
+ get(key: string): number;
8
+ set(key: string, value: number): Promise<void>;
9
+ setMany(updates: Record<string, number>): Promise<void>;
10
+ keys(): string[];
11
+ private persist;
12
+ }
@@ -0,0 +1,55 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { ensureDir } from '../util/fs.js';
4
+ import { getToolPaths, SKEIN_SUBDIRS } from '../util/paths.js';
5
+ const SCHEMA = 'skein-cursor/1';
6
+ export class CursorStore {
7
+ path;
8
+ cache = {};
9
+ loaded = false;
10
+ constructor(path = cursorFilePath()) {
11
+ this.path = path;
12
+ }
13
+ async load() {
14
+ if (this.loaded)
15
+ return;
16
+ try {
17
+ const raw = await fs.readFile(this.path, 'utf8');
18
+ const parsed = JSON.parse(raw);
19
+ if (parsed.schema === SCHEMA && parsed.cursors) {
20
+ this.cache = { ...parsed.cursors };
21
+ }
22
+ }
23
+ catch (err) {
24
+ if (err.code !== 'ENOENT')
25
+ throw err;
26
+ }
27
+ this.loaded = true;
28
+ }
29
+ get(key) {
30
+ return this.cache[key] ?? 0;
31
+ }
32
+ async set(key, value) {
33
+ this.cache[key] = value;
34
+ await this.persist();
35
+ }
36
+ async setMany(updates) {
37
+ Object.assign(this.cache, updates);
38
+ await this.persist();
39
+ }
40
+ keys() {
41
+ return Object.keys(this.cache);
42
+ }
43
+ async persist() {
44
+ await ensureDir(dirname(this.path));
45
+ const tmp = `${this.path}.tmp-${process.pid}`;
46
+ const payload = { schema: SCHEMA, cursors: this.cache };
47
+ await fs.writeFile(tmp, JSON.stringify(payload), 'utf8');
48
+ await fs.rename(tmp, this.path);
49
+ }
50
+ }
51
+ function cursorFilePath() {
52
+ const paths = getToolPaths();
53
+ return join(paths.skeinDir, SKEIN_SUBDIRS.conv, 'cursors.json');
54
+ }
55
+ //# sourceMappingURL=cursor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/conv/cursor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAA;AACxC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAmB9D,MAAM,MAAM,GAAG,gBAAyB,CAAA;AAExC,MAAM,OAAO,WAAW;IAIO;IAHrB,KAAK,GAA2B,EAAE,CAAA;IAClC,MAAM,GAAG,KAAK,CAAA;IAEtB,YAA6B,OAAO,cAAc,EAAE;QAAvB,SAAI,GAAJ,IAAI,CAAmB;IAAG,CAAC;IAExD,KAAK,CAAC,IAAI;QACR,IAAI,IAAI,CAAC,MAAM;YAAE,OAAM;QACvB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;YAChD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAe,CAAA;YAC5C,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBAC/C,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE,CAAA;YACpC,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,GAAG,CAAA;QACjE,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAA;IACpB,CAAC;IAED,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAC7B,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,KAAa;QAClC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;QACvB,MAAM,IAAI,CAAC,OAAO,EAAE,CAAA;IACtB,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAA+B;QAC3C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;QAClC,MAAM,IAAI,CAAC,OAAO,EAAE,CAAA;IACtB,CAAC;IAED,IAAI;QACF,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAChC,CAAC;IAEO,KAAK,CAAC,OAAO;QACnB,MAAM,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;QACnC,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,IAAI,QAAQ,OAAO,CAAC,GAAG,EAAE,CAAA;QAC7C,MAAM,OAAO,GAAe,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,EAAE,CAAA;QACnE,MAAM,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAA;QACxD,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;IACjC,CAAC;CACF;AAED,SAAS,cAAc;IACrB,MAAM,KAAK,GAAG,YAAY,EAAE,CAAA;IAC5B,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,aAAa,CAAC,IAAI,EAAE,cAAc,CAAC,CAAA;AACjE,CAAC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Single-session exporter.
3
+ *
4
+ * Reads `~/.skein/conv/sessions/**\/*.jsonl`, locates the requested
5
+ * session id, and renders it in one of three formats:
6
+ *
7
+ * - `jsonl` — pass-through of the stored skein-conv/1 events
8
+ * - `md` — readable markdown with a YAML header and one section
9
+ * per turn, fenced code blocks for `tool_use` input
10
+ * and `tool_result` content
11
+ * - `openai` — newline-delimited JSON; each line is one chat-
12
+ * completion-shaped object with a `messages` array.
13
+ * `tool_use` blocks are flattened to OpenAI-style
14
+ * `function_call` fields so the file is drop-in for
15
+ * fine-tuning datasets.
16
+ */
17
+ export type ExportFormat = 'md' | 'jsonl' | 'openai';
18
+ export interface ExportInput {
19
+ sessionId: string;
20
+ format: ExportFormat;
21
+ }
22
+ export interface ExportResult {
23
+ text: string;
24
+ meta: SessionMeta;
25
+ }
26
+ export interface SessionMeta {
27
+ id: string;
28
+ tool: string;
29
+ toolVersion?: string;
30
+ model?: string;
31
+ project?: string;
32
+ title?: string;
33
+ startedAt?: string;
34
+ endedAt?: string;
35
+ messageCount: number;
36
+ tokensIn: number;
37
+ tokensOut: number;
38
+ }
39
+ export declare function exportSession(input: ExportInput): Promise<ExportResult>;
40
+ export declare function writeExportToFile(result: ExportResult, outPath: string): Promise<void>;
@@ -0,0 +1,215 @@
1
+ import { createReadStream, promises as fs } from 'node:fs';
2
+ import { createInterface } from 'node:readline';
3
+ import { stringify as stringifyYaml } from 'yaml';
4
+ import { CONV_SCHEMA } from '../version.js';
5
+ import { listStoredSessions } from './store.js';
6
+ export async function exportSession(input) {
7
+ const path = await locateSession(input.sessionId);
8
+ if (!path) {
9
+ throw new Error(`No captured session matches id ${input.sessionId}`);
10
+ }
11
+ const contents = await readSession(path);
12
+ switch (input.format) {
13
+ case 'jsonl':
14
+ return { text: contents.rawLines.join('\n') + '\n', meta: contents.meta };
15
+ case 'md':
16
+ return { text: renderMarkdown(contents), meta: contents.meta };
17
+ case 'openai':
18
+ return { text: renderOpenAi(contents), meta: contents.meta };
19
+ }
20
+ }
21
+ async function locateSession(sessionId) {
22
+ const all = await listStoredSessions();
23
+ const exact = all.find(p => p.endsWith(`${sessionId}.jsonl`));
24
+ if (exact)
25
+ return exact;
26
+ const prefixed = all.find(p => p.includes(`/${sessionId.slice(0, 8)}`));
27
+ return prefixed;
28
+ }
29
+ async function readSession(path) {
30
+ const events = [];
31
+ const rawLines = [];
32
+ let id;
33
+ let tool;
34
+ let toolVersion;
35
+ let model;
36
+ let project;
37
+ let title;
38
+ let startedAt;
39
+ let endedAt;
40
+ let tokensIn = 0;
41
+ let tokensOut = 0;
42
+ let messageCount = 0;
43
+ for await (const line of readLines(path)) {
44
+ rawLines.push(line);
45
+ const event = safeParseJson(line);
46
+ if (!event)
47
+ continue;
48
+ events.push(event);
49
+ if (event.type === 'session.start') {
50
+ id = event.id;
51
+ tool = event.tool;
52
+ if (event.toolVersion)
53
+ toolVersion = event.toolVersion;
54
+ if (event.model)
55
+ model = event.model;
56
+ if (event.project)
57
+ project = event.project;
58
+ if (event.title)
59
+ title = event.title;
60
+ startedAt ??= event.ts;
61
+ endedAt = event.ts;
62
+ }
63
+ else if (event.type === 'message') {
64
+ messageCount++;
65
+ endedAt = event.ts;
66
+ if (event.tokens) {
67
+ tokensIn += event.tokens.in;
68
+ tokensOut += event.tokens.out;
69
+ }
70
+ }
71
+ else if (event.type === 'session.end') {
72
+ endedAt = event.ts;
73
+ }
74
+ }
75
+ if (!id || !tool) {
76
+ throw new Error(`Session file ${path} is missing session.start metadata`);
77
+ }
78
+ return {
79
+ meta: {
80
+ id,
81
+ tool,
82
+ ...(toolVersion ? { toolVersion } : {}),
83
+ ...(model ? { model } : {}),
84
+ ...(project ? { project } : {}),
85
+ ...(title ? { title } : {}),
86
+ ...(startedAt ? { startedAt } : {}),
87
+ ...(endedAt ? { endedAt } : {}),
88
+ messageCount,
89
+ tokensIn,
90
+ tokensOut,
91
+ },
92
+ events,
93
+ rawLines,
94
+ };
95
+ }
96
+ function renderMarkdown(contents) {
97
+ const { meta, events } = contents;
98
+ const header = stringifyYaml({
99
+ schema: CONV_SCHEMA,
100
+ id: meta.id,
101
+ tool: meta.tool,
102
+ ...(meta.toolVersion ? { toolVersion: meta.toolVersion } : {}),
103
+ ...(meta.model ? { model: meta.model } : {}),
104
+ ...(meta.project ? { project: meta.project } : {}),
105
+ ...(meta.title ? { title: meta.title } : {}),
106
+ ...(meta.startedAt ? { startedAt: meta.startedAt } : {}),
107
+ ...(meta.endedAt ? { endedAt: meta.endedAt } : {}),
108
+ messageCount: meta.messageCount,
109
+ tokens: { in: meta.tokensIn, out: meta.tokensOut },
110
+ });
111
+ const lines = ['---', header.trimEnd(), '---', ''];
112
+ if (meta.title)
113
+ lines.push(`# ${meta.title}`, '');
114
+ let turn = 0;
115
+ for (const event of events) {
116
+ if (event.type !== 'message')
117
+ continue;
118
+ turn += 1;
119
+ const heading = event.role === 'user'
120
+ ? `## 👤 User · turn ${turn}`
121
+ : event.role === 'assistant'
122
+ ? `## 🤖 Assistant · turn ${turn}`
123
+ : `## ⚙ System · turn ${turn}`;
124
+ lines.push(heading);
125
+ if (event.tokens) {
126
+ lines.push(`_tokens in ${event.tokens.in} · out ${event.tokens.out}_`);
127
+ }
128
+ lines.push('');
129
+ for (const block of event.content) {
130
+ if (block.type === 'text') {
131
+ lines.push(block.text.trimEnd(), '');
132
+ }
133
+ else if (block.type === 'thinking') {
134
+ lines.push('<details><summary>thinking</summary>', '', block.thinking.trimEnd(), '', '</details>', '');
135
+ }
136
+ else if (block.type === 'tool_use') {
137
+ lines.push(`**Tool call: \`${block.name}\`**`, '', '```json', JSON.stringify(block.input, null, 2), '```', '');
138
+ }
139
+ else if (block.type === 'tool_result') {
140
+ const body = typeof block.content === 'string' ? block.content : JSON.stringify(block.content, null, 2);
141
+ lines.push(`**Tool result · ${block.tool_use_id}**`, '', '```', body.trimEnd(), '```', '');
142
+ }
143
+ else if (block.type === 'image') {
144
+ lines.push('*(image attachment)*', '');
145
+ }
146
+ }
147
+ }
148
+ return lines.join('\n').replace(/\n{3,}/g, '\n\n');
149
+ }
150
+ function renderOpenAi(contents) {
151
+ const { events, meta } = contents;
152
+ const messages = [];
153
+ let pendingToolName;
154
+ let pendingToolInput;
155
+ for (const event of events) {
156
+ if (event.type !== 'message')
157
+ continue;
158
+ const role = event.role;
159
+ let textBuffer = '';
160
+ let toolCall;
161
+ for (const block of event.content) {
162
+ if (block.type === 'text') {
163
+ textBuffer += `${textBuffer ? '\n\n' : ''}${block.text}`;
164
+ }
165
+ else if (block.type === 'thinking') {
166
+ textBuffer += `${textBuffer ? '\n\n' : ''}[thinking]\n${block.thinking}`;
167
+ }
168
+ else if (block.type === 'tool_use') {
169
+ toolCall = { name: block.name, arguments: JSON.stringify(block.input) };
170
+ pendingToolName = block.name;
171
+ pendingToolInput = JSON.stringify(block.input);
172
+ }
173
+ else if (block.type === 'tool_result') {
174
+ const body = typeof block.content === 'string' ? block.content : JSON.stringify(block.content);
175
+ textBuffer += `${textBuffer ? '\n\n' : ''}[tool_result ${block.tool_use_id}]\n${body}`;
176
+ }
177
+ }
178
+ if (toolCall) {
179
+ messages.push({ role: 'assistant', content: textBuffer, function_call: toolCall });
180
+ }
181
+ else if (textBuffer) {
182
+ messages.push({ role, content: textBuffer });
183
+ }
184
+ }
185
+ // Suppress unused-var warnings while keeping these for future expansion.
186
+ void pendingToolName;
187
+ void pendingToolInput;
188
+ const payload = {
189
+ sessionId: meta.id,
190
+ tool: meta.tool,
191
+ ...(meta.model ? { model: meta.model } : {}),
192
+ messages,
193
+ };
194
+ return JSON.stringify(payload) + '\n';
195
+ }
196
+ async function* readLines(path) {
197
+ const stream = createReadStream(path, { encoding: 'utf8' });
198
+ const rl = createInterface({ input: stream, crlfDelay: Number.POSITIVE_INFINITY });
199
+ for await (const line of rl) {
200
+ if (line.trim())
201
+ yield line;
202
+ }
203
+ }
204
+ function safeParseJson(line) {
205
+ try {
206
+ return JSON.parse(line);
207
+ }
208
+ catch {
209
+ return undefined;
210
+ }
211
+ }
212
+ export async function writeExportToFile(result, outPath) {
213
+ await fs.writeFile(outPath, result.text, 'utf8');
214
+ }
215
+ //# sourceMappingURL=export.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.js","sourceRoot":"","sources":["../../src/conv/export.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { ConvEvent } from '../../schema/conv.js';
2
+ import type { SessionMeta } from '../export.js';
3
+ /**
4
+ * Aider resume-mode replay writer.
5
+ *
6
+ * Appends a new `# aider chat started at <ts>` block to the project's
7
+ * `.aider.chat.history.md`, with quoted user prompts (`> `) and
8
+ * unquoted assistant prose. If the history file does not yet exist
9
+ * we create it.
10
+ *
11
+ * Every text payload passes through the redactor before disk write.
12
+ */
13
+ export interface AiderResumeInput {
14
+ meta: SessionMeta;
15
+ events: ConvEvent[];
16
+ projectPath: string;
17
+ }
18
+ export interface AiderResumeResult {
19
+ path: string;
20
+ userEvents: number;
21
+ assistantEvents: number;
22
+ startedAt: string;
23
+ }
24
+ export declare function writeAiderResume(input: AiderResumeInput): Promise<AiderResumeResult>;
@@ -0,0 +1,56 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { redactText } from '../../redactor/index.js';
4
+ export async function writeAiderResume(input) {
5
+ const path = join(input.projectPath, '.aider.chat.history.md');
6
+ const startedAt = formatAiderTimestamp(new Date());
7
+ const lines = ['', `# aider chat started at ${startedAt}`, ''];
8
+ let userEvents = 0;
9
+ let assistantEvents = 0;
10
+ for (const event of input.events) {
11
+ if (event.type !== 'message')
12
+ continue;
13
+ const text = extractText(event.content);
14
+ if (!text.trim())
15
+ continue;
16
+ if (event.role === 'user') {
17
+ for (const piece of text.split('\n'))
18
+ lines.push(`> ${piece}`);
19
+ lines.push('');
20
+ userEvents += 1;
21
+ }
22
+ else if (event.role === 'assistant') {
23
+ lines.push(text.trimEnd(), '');
24
+ assistantEvents += 1;
25
+ }
26
+ }
27
+ const redacted = redactText(lines.join('\n')).text;
28
+ await fs.mkdir(input.projectPath, { recursive: true }).catch(() => { });
29
+ await fs.appendFile(path, redacted, 'utf8');
30
+ return { path, userEvents, assistantEvents, startedAt };
31
+ }
32
+ function extractText(blocks) {
33
+ const parts = [];
34
+ for (const block of blocks) {
35
+ if (block.type === 'text')
36
+ parts.push(block.text);
37
+ else if (block.type === 'thinking')
38
+ parts.push(`_(thinking)_ ${block.thinking}`);
39
+ else if (block.type === 'tool_use') {
40
+ parts.push(`[tool_use ${block.name} ${JSON.stringify(block.input)}]`);
41
+ }
42
+ else if (block.type === 'tool_result') {
43
+ const body = typeof block.content === 'string' ? block.content : JSON.stringify(block.content);
44
+ parts.push(`[tool_result] ${body}`);
45
+ }
46
+ }
47
+ return parts.join('\n\n');
48
+ }
49
+ function formatAiderTimestamp(d) {
50
+ // Aider writes "YYYY-MM-DD HH:MM:SS"; we follow the same shape so the
51
+ // session-detection regex on the reader side accepts our block on a
52
+ // round trip.
53
+ const pad = (n) => String(n).padStart(2, '0');
54
+ return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())} ${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())}`;
55
+ }
56
+ //# sourceMappingURL=aider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aider.js","sourceRoot":"","sources":["../../../src/conv/replay/aider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import type { ConvEvent } from '../../schema/conv.js';
2
+ import type { SessionMeta } from '../export.js';
3
+ /**
4
+ * Claude Code resume-mode replay writer.
5
+ *
6
+ * Writes a fresh transcript file at
7
+ * `~/.claude/projects/<encoded-cwd>/<new-uuid>.jsonl` that Claude Code
8
+ * will list under its `--resume` selector. Each captured turn becomes a
9
+ * synthesized `user` / `assistant` event with the minimum field set
10
+ * Claude Code reads:
11
+ *
12
+ * - sessionId, uuid, parentUuid (chain), timestamp
13
+ * - cwd, version, userType, isSidechain
14
+ * - type ("user" | "assistant"), message.role, message.content,
15
+ * message.model (where present)
16
+ *
17
+ * Every JSON payload passes through the redactor before disk write.
18
+ */
19
+ export interface ClaudeCodeResumeInput {
20
+ meta: SessionMeta;
21
+ events: ConvEvent[];
22
+ homeDir: string;
23
+ /** Absolute path of the workspace the resumed session will run in. */
24
+ projectPath: string;
25
+ /** Optional pre-generated session id (used by tests to be deterministic). */
26
+ sessionIdOverride?: string;
27
+ }
28
+ export interface ClaudeCodeResumeResult {
29
+ sessionId: string;
30
+ path: string;
31
+ userEvents: number;
32
+ assistantEvents: number;
33
+ }
34
+ export declare function writeClaudeCodeResume(input: ClaudeCodeResumeInput): Promise<ClaudeCodeResumeResult>;
35
+ export declare function encodeProjectPath(absolute: string): string;
36
+ export declare const __testExports: {
37
+ encodeProjectPath: typeof encodeProjectPath;
38
+ };
@@ -0,0 +1,80 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { randomUUID } from 'node:crypto';
4
+ import { ensureDir, writeFileAtomic } from '../../util/fs.js';
5
+ import { redactText } from '../../redactor/index.js';
6
+ export async function writeClaudeCodeResume(input) {
7
+ const sessionId = input.sessionIdOverride ?? randomUUID();
8
+ const projectsDir = join(input.homeDir, '.claude', 'projects', encodeProjectPath(input.projectPath));
9
+ await ensureDir(projectsDir);
10
+ const filePath = join(projectsDir, `${sessionId}.jsonl`);
11
+ const lines = [];
12
+ let parentUuid = null;
13
+ let userEvents = 0;
14
+ let assistantEvents = 0;
15
+ const version = input.meta.toolVersion ?? 'skein-replay/1';
16
+ for (const event of input.events) {
17
+ if (event.type !== 'message')
18
+ continue;
19
+ if (event.role !== 'user' && event.role !== 'assistant')
20
+ continue;
21
+ const uuid = randomUUID();
22
+ const ts = event.ts;
23
+ const base = {
24
+ parentUuid,
25
+ isSidechain: false,
26
+ type: event.role,
27
+ uuid,
28
+ timestamp: ts,
29
+ userType: 'external',
30
+ sessionId,
31
+ cwd: input.projectPath,
32
+ version,
33
+ };
34
+ const messageContent = projectContent(event.content);
35
+ const messageEnvelope = {
36
+ role: event.role,
37
+ content: messageContent,
38
+ };
39
+ if (event.role === 'assistant') {
40
+ if (input.meta.model)
41
+ messageEnvelope['model'] = input.meta.model;
42
+ if (event.tokens) {
43
+ ;
44
+ messageEnvelope['usage'] = {
45
+ input_tokens: event.tokens.in,
46
+ output_tokens: event.tokens.out,
47
+ };
48
+ }
49
+ }
50
+ const payload = {
51
+ ...base,
52
+ message: messageEnvelope,
53
+ };
54
+ const { text } = redactText(JSON.stringify(payload));
55
+ lines.push(text);
56
+ parentUuid = uuid;
57
+ if (event.role === 'user')
58
+ userEvents += 1;
59
+ else
60
+ assistantEvents += 1;
61
+ }
62
+ await writeFileAtomic(filePath, `${lines.join('\n')}\n`);
63
+ return { sessionId, path: filePath, userEvents, assistantEvents };
64
+ }
65
+ export function encodeProjectPath(absolute) {
66
+ // Claude Code encodes a cwd by replacing every `/` with `-`. The leading
67
+ // slash becomes the leading dash.
68
+ return absolute.replace(/[/]/g, '-').replace(/-+/g, '-');
69
+ }
70
+ function projectContent(content) {
71
+ // Anthropic Messages content blocks are already the format Claude Code
72
+ // expects. Pass them through verbatim so tool_use / tool_result events
73
+ // resume cleanly.
74
+ return content;
75
+ }
76
+ // re-export so tests can also exercise the encoder directly
77
+ export const __testExports = { encodeProjectPath };
78
+ // Silence unused-imports for the prepare-only fs read above when callers don't need it.
79
+ void fs;
80
+ //# sourceMappingURL=claude-code.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../../src/conv/replay/claude-code.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { SessionMeta } from '../export.js';
2
+ import type { ConvEvent } from '../../schema/conv.js';
3
+ /**
4
+ * Context-mode replay renderer.
5
+ *
6
+ * Produces a single markdown file you paste into the target tool as the
7
+ * first turn of a fresh session. The file has:
8
+ *
9
+ * - a YAML frontmatter header with provenance + token totals
10
+ * - a "Prior conversation" H2
11
+ * - one section per turn, with role emojis, fenced JSON for tool_use
12
+ * input, fenced output for tool_result, collapsible details for
13
+ * thinking blocks
14
+ * - a closing "## Continue here" prompt for the new tool's first turn
15
+ *
16
+ * Works for every target — the heaviest the rendering ever needs is
17
+ * "copy-paste this into the new chat".
18
+ */
19
+ export interface ContextReplayInput {
20
+ meta: SessionMeta;
21
+ events: ConvEvent[];
22
+ /** Tool the replay is being prepared for (used in the header banner). */
23
+ targetTool: string;
24
+ }
25
+ export declare function renderContextReplay(input: ContextReplayInput): string;
@@ -0,0 +1,63 @@
1
+ import { stringify as stringifyYaml } from 'yaml';
2
+ import { CONV_SCHEMA } from '../../version.js';
3
+ export function renderContextReplay(input) {
4
+ const { meta, events, targetTool } = input;
5
+ const header = stringifyYaml({
6
+ schema: CONV_SCHEMA,
7
+ replayMode: 'context',
8
+ sourceTool: meta.tool,
9
+ targetTool,
10
+ sessionId: meta.id,
11
+ ...(meta.toolVersion ? { sourceToolVersion: meta.toolVersion } : {}),
12
+ ...(meta.model ? { sourceModel: meta.model } : {}),
13
+ ...(meta.project ? { sourceProject: meta.project } : {}),
14
+ ...(meta.title ? { sourceTitle: meta.title } : {}),
15
+ ...(meta.startedAt ? { sourceStartedAt: meta.startedAt } : {}),
16
+ ...(meta.endedAt ? { sourceEndedAt: meta.endedAt } : {}),
17
+ sourceMessageCount: meta.messageCount,
18
+ sourceTokens: { in: meta.tokensIn, out: meta.tokensOut },
19
+ });
20
+ const lines = ['---', header.trimEnd(), '---', ''];
21
+ lines.push(`# Continuing "${meta.title ?? meta.id}" in ${targetTool}`, '');
22
+ lines.push('> The prior conversation is reproduced below as context. Pick up where it left off in your next message.', '');
23
+ lines.push('## Prior conversation', '');
24
+ let turn = 0;
25
+ for (const event of events) {
26
+ if (event.type !== 'message')
27
+ continue;
28
+ turn += 1;
29
+ const heading = event.role === 'user'
30
+ ? `### 👤 User · turn ${turn}`
31
+ : event.role === 'assistant'
32
+ ? `### 🤖 Assistant · turn ${turn}`
33
+ : `### ⚙ System · turn ${turn}`;
34
+ lines.push(heading);
35
+ if (event.tokens) {
36
+ lines.push(`_tokens in ${event.tokens.in} · out ${event.tokens.out}_`);
37
+ }
38
+ lines.push('');
39
+ for (const block of event.content) {
40
+ if (block.type === 'text') {
41
+ lines.push(block.text.trimEnd(), '');
42
+ }
43
+ else if (block.type === 'thinking') {
44
+ lines.push('<details><summary>thinking</summary>', '', block.thinking.trimEnd(), '', '</details>', '');
45
+ }
46
+ else if (block.type === 'tool_use') {
47
+ lines.push(`**Tool call: \`${block.name}\`**`, '', '```json', JSON.stringify(block.input, null, 2), '```', '');
48
+ }
49
+ else if (block.type === 'tool_result') {
50
+ const body = typeof block.content === 'string' ? block.content : JSON.stringify(block.content, null, 2);
51
+ lines.push(`**Tool result · ${block.tool_use_id}**`, '', '```', body.trimEnd(), '```', '');
52
+ }
53
+ else if (block.type === 'image') {
54
+ lines.push('*(image attachment — not inlined)*', '');
55
+ }
56
+ }
57
+ }
58
+ lines.push('---', '');
59
+ lines.push('## Continue here', '');
60
+ lines.push('_(write your next message below)_', '');
61
+ return lines.join('\n').replace(/\n{3,}/g, '\n\n');
62
+ }
63
+ //# sourceMappingURL=context.js.map