@herbertgao/pi-extensions 2026.8.9 → 2026.8.10

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 (194) hide show
  1. package/README.md +3 -0
  2. package/node_modules/resume-from/LICENSE +21 -0
  3. package/node_modules/resume-from/README.md +161 -0
  4. package/node_modules/resume-from/dist/adapters/claude-code/adapter.d.ts +25 -0
  5. package/node_modules/resume-from/dist/adapters/claude-code/adapter.js +60 -0
  6. package/node_modules/resume-from/dist/adapters/claude-code/contract.d.ts +8 -0
  7. package/node_modules/resume-from/dist/adapters/claude-code/contract.js +4 -0
  8. package/node_modules/resume-from/dist/adapters/claude-code/entries.d.ts +30 -0
  9. package/node_modules/resume-from/dist/adapters/claude-code/entries.js +107 -0
  10. package/node_modules/resume-from/dist/adapters/claude-code/index.d.ts +3 -0
  11. package/node_modules/resume-from/dist/adapters/claude-code/index.js +2 -0
  12. package/node_modules/resume-from/dist/adapters/claude-code/layout.d.ts +29 -0
  13. package/node_modules/resume-from/dist/adapters/claude-code/layout.js +80 -0
  14. package/node_modules/resume-from/dist/adapters/claude-code/readback.d.ts +9 -0
  15. package/node_modules/resume-from/dist/adapters/claude-code/readback.js +39 -0
  16. package/node_modules/resume-from/dist/adapters/claude-code/reader.d.ts +37 -0
  17. package/node_modules/resume-from/dist/adapters/claude-code/reader.js +415 -0
  18. package/node_modules/resume-from/dist/adapters/claude-code/redaction.d.ts +26 -0
  19. package/node_modules/resume-from/dist/adapters/claude-code/redaction.js +132 -0
  20. package/node_modules/resume-from/dist/adapters/claude-code/validation.d.ts +4 -0
  21. package/node_modules/resume-from/dist/adapters/claude-code/validation.js +39 -0
  22. package/node_modules/resume-from/dist/adapters/claude-code/writer.d.ts +23 -0
  23. package/node_modules/resume-from/dist/adapters/claude-code/writer.js +306 -0
  24. package/node_modules/resume-from/dist/adapters/codex/contract.d.ts +15 -0
  25. package/node_modules/resume-from/dist/adapters/codex/contract.js +4 -0
  26. package/node_modules/resume-from/dist/adapters/codex/index.d.ts +6 -0
  27. package/node_modules/resume-from/dist/adapters/codex/index.js +44 -0
  28. package/node_modules/resume-from/dist/adapters/codex/read.d.ts +28 -0
  29. package/node_modules/resume-from/dist/adapters/codex/read.js +270 -0
  30. package/node_modules/resume-from/dist/adapters/codex/readback.d.ts +9 -0
  31. package/node_modules/resume-from/dist/adapters/codex/readback.js +47 -0
  32. package/node_modules/resume-from/dist/adapters/codex/redaction.d.ts +26 -0
  33. package/node_modules/resume-from/dist/adapters/codex/redaction.js +132 -0
  34. package/node_modules/resume-from/dist/adapters/codex/rollout.d.ts +75 -0
  35. package/node_modules/resume-from/dist/adapters/codex/rollout.js +185 -0
  36. package/node_modules/resume-from/dist/adapters/codex/validation.d.ts +7 -0
  37. package/node_modules/resume-from/dist/adapters/codex/validation.js +78 -0
  38. package/node_modules/resume-from/dist/adapters/codex/write.d.ts +17 -0
  39. package/node_modules/resume-from/dist/adapters/codex/write.js +141 -0
  40. package/node_modules/resume-from/dist/adapters/contract.d.ts +74 -0
  41. package/node_modules/resume-from/dist/adapters/contract.js +4 -0
  42. package/node_modules/resume-from/dist/adapters/pi/adapter.d.ts +15 -0
  43. package/node_modules/resume-from/dist/adapters/pi/adapter.js +209 -0
  44. package/node_modules/resume-from/dist/adapters/pi/contract.d.ts +24 -0
  45. package/node_modules/resume-from/dist/adapters/pi/contract.js +4 -0
  46. package/node_modules/resume-from/dist/adapters/pi/format.d.ts +162 -0
  47. package/node_modules/resume-from/dist/adapters/pi/format.js +223 -0
  48. package/node_modules/resume-from/dist/adapters/pi/index.d.ts +7 -0
  49. package/node_modules/resume-from/dist/adapters/pi/index.js +10 -0
  50. package/node_modules/resume-from/dist/adapters/pi/parse.d.ts +41 -0
  51. package/node_modules/resume-from/dist/adapters/pi/parse.js +371 -0
  52. package/node_modules/resume-from/dist/adapters/pi/redaction.d.ts +26 -0
  53. package/node_modules/resume-from/dist/adapters/pi/redaction.js +132 -0
  54. package/node_modules/resume-from/dist/adapters/pi/serialize.d.ts +22 -0
  55. package/node_modules/resume-from/dist/adapters/pi/serialize.js +87 -0
  56. package/node_modules/resume-from/dist/adapters/pi/validate.d.ts +14 -0
  57. package/node_modules/resume-from/dist/adapters/pi/validate.js +134 -0
  58. package/node_modules/resume-from/dist/bin.d.ts +35 -0
  59. package/node_modules/resume-from/dist/bin.js +135 -0
  60. package/node_modules/resume-from/dist/contract.d.ts +10 -0
  61. package/node_modules/resume-from/dist/contract.js +4 -0
  62. package/node_modules/resume-from/dist/host/agents.d.ts +29 -0
  63. package/node_modules/resume-from/dist/host/agents.js +20 -0
  64. package/node_modules/resume-from/dist/host/cli/args.d.ts +21 -0
  65. package/node_modules/resume-from/dist/host/cli/args.js +187 -0
  66. package/node_modules/resume-from/dist/host/cli/contract.d.ts +27 -0
  67. package/node_modules/resume-from/dist/host/cli/contract.js +4 -0
  68. package/node_modules/resume-from/dist/host/cli/index.d.ts +2 -0
  69. package/node_modules/resume-from/dist/host/cli/index.js +1 -0
  70. package/node_modules/resume-from/dist/host/cli/presentation.d.ts +2 -0
  71. package/node_modules/resume-from/dist/host/cli/presentation.js +11 -0
  72. package/node_modules/resume-from/dist/host/cli/render.d.ts +3 -0
  73. package/node_modules/resume-from/dist/host/cli/render.js +70 -0
  74. package/node_modules/resume-from/dist/host/cli/runner.d.ts +8 -0
  75. package/node_modules/resume-from/dist/host/cli/runner.js +125 -0
  76. package/node_modules/resume-from/dist/host/contract.d.ts +42 -0
  77. package/node_modules/resume-from/dist/host/contract.js +4 -0
  78. package/node_modules/resume-from/dist/host/entry.d.ts +52 -0
  79. package/node_modules/resume-from/dist/host/entry.js +91 -0
  80. package/node_modules/resume-from/dist/host/index.d.ts +11 -0
  81. package/node_modules/resume-from/dist/host/index.js +10 -0
  82. package/node_modules/resume-from/dist/host/pi-extension/command.d.ts +18 -0
  83. package/node_modules/resume-from/dist/host/pi-extension/command.js +103 -0
  84. package/node_modules/resume-from/dist/host/pi-extension/contract.d.ts +34 -0
  85. package/node_modules/resume-from/dist/host/pi-extension/contract.js +4 -0
  86. package/node_modules/resume-from/dist/host/pi-extension/index.d.ts +6 -0
  87. package/node_modules/resume-from/dist/host/pi-extension/index.js +4 -0
  88. package/node_modules/resume-from/dist/host/pi-extension/picker.d.ts +16 -0
  89. package/node_modules/resume-from/dist/host/pi-extension/picker.js +35 -0
  90. package/node_modules/resume-from/dist/host/pi-extension/presentation.d.ts +2 -0
  91. package/node_modules/resume-from/dist/host/pi-extension/presentation.js +11 -0
  92. package/node_modules/resume-from/dist/host/pi-extension/register.d.ts +25 -0
  93. package/node_modules/resume-from/dist/host/pi-extension/register.js +11 -0
  94. package/node_modules/resume-from/dist/host/pi-extension/ui.d.ts +20 -0
  95. package/node_modules/resume-from/dist/host/pi-extension/ui.js +1 -0
  96. package/node_modules/resume-from/dist/host/profile.d.ts +8 -0
  97. package/node_modules/resume-from/dist/host/profile.js +31 -0
  98. package/node_modules/resume-from/dist/host/registry.d.ts +8 -0
  99. package/node_modules/resume-from/dist/host/registry.js +33 -0
  100. package/node_modules/resume-from/dist/host/wiring.d.ts +41 -0
  101. package/node_modules/resume-from/dist/host/wiring.js +67 -0
  102. package/node_modules/resume-from/dist/import/confirmation.d.ts +6 -0
  103. package/node_modules/resume-from/dist/import/confirmation.js +27 -0
  104. package/node_modules/resume-from/dist/import/contract.d.ts +41 -0
  105. package/node_modules/resume-from/dist/import/contract.js +4 -0
  106. package/node_modules/resume-from/dist/import/discovery/contract.d.ts +58 -0
  107. package/node_modules/resume-from/dist/import/discovery/contract.js +4 -0
  108. package/node_modules/resume-from/dist/import/discovery/errors.d.ts +9 -0
  109. package/node_modules/resume-from/dist/import/discovery/errors.js +12 -0
  110. package/node_modules/resume-from/dist/import/discovery/finder.d.ts +10 -0
  111. package/node_modules/resume-from/dist/import/discovery/finder.js +129 -0
  112. package/node_modules/resume-from/dist/import/discovery/homes.d.ts +23 -0
  113. package/node_modules/resume-from/dist/import/discovery/homes.js +83 -0
  114. package/node_modules/resume-from/dist/import/discovery/index.d.ts +2 -0
  115. package/node_modules/resume-from/dist/import/discovery/index.js +2 -0
  116. package/node_modules/resume-from/dist/import/discovery/ordering.d.ts +3 -0
  117. package/node_modules/resume-from/dist/import/discovery/ordering.js +21 -0
  118. package/node_modules/resume-from/dist/import/errors.d.ts +22 -0
  119. package/node_modules/resume-from/dist/import/errors.js +20 -0
  120. package/node_modules/resume-from/dist/import/index.d.ts +3 -0
  121. package/node_modules/resume-from/dist/import/index.js +5 -0
  122. package/node_modules/resume-from/dist/import/landing/contract.d.ts +44 -0
  123. package/node_modules/resume-from/dist/import/landing/contract.js +4 -0
  124. package/node_modules/resume-from/dist/import/landing/errors.d.ts +15 -0
  125. package/node_modules/resume-from/dist/import/landing/errors.js +15 -0
  126. package/node_modules/resume-from/dist/import/landing/handover.d.ts +5 -0
  127. package/node_modules/resume-from/dist/import/landing/handover.js +12 -0
  128. package/node_modules/resume-from/dist/import/landing/index.d.ts +4 -0
  129. package/node_modules/resume-from/dist/import/landing/index.js +4 -0
  130. package/node_modules/resume-from/dist/import/landing/lander.d.ts +7 -0
  131. package/node_modules/resume-from/dist/import/landing/lander.js +133 -0
  132. package/node_modules/resume-from/dist/import/landing/marker.d.ts +9 -0
  133. package/node_modules/resume-from/dist/import/landing/marker.js +46 -0
  134. package/node_modules/resume-from/dist/import/pipeline.d.ts +17 -0
  135. package/node_modules/resume-from/dist/import/pipeline.js +134 -0
  136. package/node_modules/resume-from/dist/import/preview/builder.d.ts +6 -0
  137. package/node_modules/resume-from/dist/import/preview/builder.js +76 -0
  138. package/node_modules/resume-from/dist/import/preview/contract.d.ts +39 -0
  139. package/node_modules/resume-from/dist/import/preview/contract.js +4 -0
  140. package/node_modules/resume-from/dist/import/preview/format.d.ts +19 -0
  141. package/node_modules/resume-from/dist/import/preview/format.js +45 -0
  142. package/node_modules/resume-from/dist/import/preview/index.d.ts +2 -0
  143. package/node_modules/resume-from/dist/import/preview/index.js +1 -0
  144. package/node_modules/resume-from/dist/import/preview/warnings.d.ts +11 -0
  145. package/node_modules/resume-from/dist/import/preview/warnings.js +80 -0
  146. package/node_modules/resume-from/dist/import/transfer/contract.d.ts +48 -0
  147. package/node_modules/resume-from/dist/import/transfer/contract.js +4 -0
  148. package/node_modules/resume-from/dist/import/transfer/index.d.ts +2 -0
  149. package/node_modules/resume-from/dist/import/transfer/index.js +1 -0
  150. package/node_modules/resume-from/dist/import/transfer/rules.d.ts +3 -0
  151. package/node_modules/resume-from/dist/import/transfer/rules.js +228 -0
  152. package/node_modules/resume-from/dist/import/wiring.d.ts +24 -0
  153. package/node_modules/resume-from/dist/import/wiring.js +27 -0
  154. package/node_modules/resume-from/dist/index.d.ts +36 -0
  155. package/node_modules/resume-from/dist/index.js +36 -0
  156. package/node_modules/resume-from/dist/platform/config/contract.d.ts +35 -0
  157. package/node_modules/resume-from/dist/platform/config/contract.js +4 -0
  158. package/node_modules/resume-from/dist/platform/config/defaults.d.ts +16 -0
  159. package/node_modules/resume-from/dist/platform/config/defaults.js +22 -0
  160. package/node_modules/resume-from/dist/platform/config/errors.d.ts +9 -0
  161. package/node_modules/resume-from/dist/platform/config/errors.js +12 -0
  162. package/node_modules/resume-from/dist/platform/config/index.d.ts +5 -0
  163. package/node_modules/resume-from/dist/platform/config/index.js +6 -0
  164. package/node_modules/resume-from/dist/platform/config/loader.d.ts +10 -0
  165. package/node_modules/resume-from/dist/platform/config/loader.js +47 -0
  166. package/node_modules/resume-from/dist/platform/config/paths.d.ts +13 -0
  167. package/node_modules/resume-from/dist/platform/config/paths.js +37 -0
  168. package/node_modules/resume-from/dist/platform/config/validate.d.ts +9 -0
  169. package/node_modules/resume-from/dist/platform/config/validate.js +117 -0
  170. package/node_modules/resume-from/dist/platform/repo/contract.d.ts +30 -0
  171. package/node_modules/resume-from/dist/platform/repo/contract.js +4 -0
  172. package/node_modules/resume-from/dist/platform/repo/git.d.ts +21 -0
  173. package/node_modules/resume-from/dist/platform/repo/git.js +74 -0
  174. package/node_modules/resume-from/dist/platform/repo/index.d.ts +2 -0
  175. package/node_modules/resume-from/dist/platform/repo/index.js +1 -0
  176. package/node_modules/resume-from/dist/platform/repo/reader.d.ts +8 -0
  177. package/node_modules/resume-from/dist/platform/repo/reader.js +130 -0
  178. package/node_modules/resume-from/dist/platform/store/contract.d.ts +31 -0
  179. package/node_modules/resume-from/dist/platform/store/contract.js +4 -0
  180. package/node_modules/resume-from/dist/platform/store/file-committer.d.ts +3 -0
  181. package/node_modules/resume-from/dist/platform/store/file-committer.js +391 -0
  182. package/node_modules/resume-from/dist/platform/store/index.d.ts +2 -0
  183. package/node_modules/resume-from/dist/platform/store/index.js +2 -0
  184. package/node_modules/resume-from/dist/platform/tokens/contract.d.ts +12 -0
  185. package/node_modules/resume-from/dist/platform/tokens/contract.js +4 -0
  186. package/node_modules/resume-from/dist/platform/tokens/estimator.d.ts +12 -0
  187. package/node_modules/resume-from/dist/platform/tokens/estimator.js +111 -0
  188. package/node_modules/resume-from/dist/platform/tokens/index.d.ts +2 -0
  189. package/node_modules/resume-from/dist/platform/tokens/index.js +1 -0
  190. package/node_modules/resume-from/dist/session/contract.d.ts +109 -0
  191. package/node_modules/resume-from/dist/session/contract.js +4 -0
  192. package/node_modules/resume-from/package.json +80 -0
  193. package/node_modules/resume-from/shims/pi/extensions/resume-from.js +118 -0
  194. package/package.json +7 -3
package/README.md CHANGED
@@ -8,6 +8,8 @@ Aggregate installer for HerbertGao-maintained Pi extensions and pinned upstream
8
8
  pi install npm:@herbertgao/pi-extensions
9
9
  ```
10
10
 
11
+ Requires Node.js 24 or newer.
12
+
11
13
  The package bundles 11 active `@herbertgao/*` child packages, including `@herbertgao/pi-cc-extensions`, plus the following upstream packages under their original names:
12
14
 
13
15
  - `@dietrichgebert/ponytail@4.9.0`
@@ -19,6 +21,7 @@ The package bundles 11 active `@herbertgao/*` child packages, including `@herber
19
21
  - `pi-lens@4.1.1`
20
22
  - `pi-web-access@0.24.2`
21
23
  - `remote-pi@0.7.0`
24
+ - `resume-from@0.2.0`
22
25
  - `@czottmann/pi-automode@1.12.0`
23
26
 
24
27
  Pi loads their extensions and skills through `node_modules/` paths inside one package root. The upstream companions are pinned and bundled, not forked or renamed.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alexei Led
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,161 @@
1
+ # resume-from
2
+
3
+ [![Release](https://github.com/alexei-led/resume-from/actions/workflows/release.yml/badge.svg)](https://github.com/alexei-led/resume-from/actions/workflows/release.yml)
4
+ [![npm](https://img.shields.io/npm/v/resume-from)](https://www.npmjs.com/package/resume-from)
5
+
6
+ ![A source coding-agent session is normalized, filtered, previewed, and written as a new native session while the source remains unchanged.](assets/resume-from-card.svg)
7
+
8
+ Continue an AI coding session in another terminal agent or another profile of the same agent.
9
+
10
+ `resume-from` converts saved sessions between **Pi**, **Claude Code**, and **Codex**. Use it when you want a different model, provider, tool harness, or account without rebuilding the task context by hand.
11
+
12
+ ## Why this exists
13
+
14
+ Coding agents save conversations in different locations and vendor-specific formats. A Codex thread is not a Pi session. A Claude Code session cannot be opened directly by Codex.
15
+
16
+ You can ask the next agent to read another tool's raw session file, but then the model must understand that format and decide which internal data is safe or useful. `resume-from` performs that conversion before the target agent starts:
17
+
18
+ 1. Find sessions for the current Git repository.
19
+ 2. Parse the source agent's format into a common conversation model.
20
+ 3. Remove tool-result bodies and vendor-only state.
21
+ 4. Fit the useful history to the target context budget.
22
+ 5. Show exactly what will be imported.
23
+ 6. After confirmation, write a new session in the target agent's native format.
24
+
25
+ The source session is never changed.
26
+
27
+ ## Use it when
28
+
29
+ - **You want another model or provider.** Start the target agent with the model you want, then import the session.
30
+ - **You want another harness.** Keep the task context while changing terminal UI, tools, permissions, extensions, or agent behavior.
31
+ - **You hit a rate or usage limit.** Continue through another installed agent or account instead of waiting or reconstructing the task.
32
+ - **You need another profile.** Move between work, personal, or team homes, including two profiles of the same agent.
33
+ - **The current context is too large.** Import a budgeted history into a fresh native session.
34
+ - **You want an independent handoff.** Create a target-side copy for continued work or review while preserving the original session.
35
+
36
+ `resume-from` moves conversation context. It does not copy the repository or migrate a running process. The target agent must have access to the same working tree.
37
+
38
+ ## Supported transfers
39
+
40
+ Every source-to-target direction is supported, including transfers within the same agent:
41
+
42
+ | Source \ Target | Pi | Claude Code | Codex |
43
+ | --------------- | :-: | :---------: | :---: |
44
+ | **Pi** | ✓ | ✓ | ✓ |
45
+ | **Claude Code** | ✓ | ✓ | ✓ |
46
+ | **Codex** | ✓ | ✓ | ✓ |
47
+
48
+ The landing behavior depends on the target:
49
+
50
+ | Target | After confirmation |
51
+ | --------------- | ---------------------------------------------------------------- |
52
+ | **Pi** | Writes and opens the imported session in the current Pi process. |
53
+ | **Claude Code** | Writes the session and prints `claude --resume <session-id>`. |
54
+ | **Codex** | Writes the thread and prints `codex resume <thread-id>`. |
55
+
56
+ ## What crosses the boundary
57
+
58
+ The importer keeps the parts needed to continue the task, subject to the target token budget:
59
+
60
+ - User prompts and agent replies.
61
+ - Compaction summaries.
62
+ - Tool activity as **non-replayable plain text**: tool name, recorded arguments, and a one-line outcome.
63
+ - Changed-file paths derived from mutating tool activity.
64
+ - Source provenance and a summary of anything dropped.
65
+
66
+ It excludes:
67
+
68
+ - Tool-result bodies, which may be large, stale, or sensitive.
69
+ - Replayable tool-call structures.
70
+ - Hidden reasoning, system prompts, environment blocks, API keys, telemetry, and vendor process state.
71
+
72
+ A dropped result is marked in the imported conversation. The target agent can reread the current file or rerun a command when it needs fresh state.
73
+
74
+ ### Context budget
75
+
76
+ By default, the imported history may use up to 30% of the target context window. The first request, recent turns, summaries, and changed-file records are pinned. If older unpinned turns must be removed, the preview reports them. If the pinned content alone does not fit, the import stops before writing.
77
+
78
+ ## Install in the destination agent
79
+
80
+ Install `resume-from` in the agent that will receive the new session.
81
+
82
+ ### Pi
83
+
84
+ ```sh
85
+ pi install npm:resume-from
86
+ ```
87
+
88
+ Restart Pi, then run `/resume-from`.
89
+
90
+ ### Claude Code
91
+
92
+ ```sh
93
+ claude plugin marketplace add alexei-led/resume-from
94
+ claude plugin install resume-from@alexei-led-resume-from
95
+ ```
96
+
97
+ ### Codex
98
+
99
+ ```sh
100
+ codex plugin marketplace add alexei-led/resume-from
101
+ codex plugin add resume-from@alexei-led-resume-from
102
+ ```
103
+
104
+ The Codex prompt runs the matching published `resume-from` package through `npx`. Its first use needs npm registry access.
105
+
106
+ ## Run the first transfer
107
+
108
+ Run the command from the Git repository that owns the source session.
109
+
110
+ ### In Pi
111
+
112
+ 1. Run `/resume-from`.
113
+ 2. Select a source session in the native picker.
114
+ 3. Read the preview.
115
+ 4. Confirm.
116
+
117
+ Pi opens the imported session with an empty prompt. You decide when to continue.
118
+
119
+ ### In Claude Code or Codex
120
+
121
+ 1. Run `/resume-from` to list matching sessions.
122
+ 2. Run `/resume-from <row>` to preview one.
123
+ 3. Run the token-bearing `/resume-from <row> --confirm <token>` command printed by the preview.
124
+ 4. Run the native resume command printed by the tool.
125
+
126
+ You can also select an exact session ID or file path. Selection by path does not bypass the current-repository check.
127
+
128
+ ## Safety properties
129
+
130
+ - **Read-only source:** source homes and session files are not modified.
131
+ - **Explicit write:** cancellation or a blocked preview writes nothing.
132
+ - **Add-only target:** the importer creates target files; it does not replace or delete existing sessions.
133
+ - **Native validation:** the new file is validated and read back before it is reported as openable.
134
+ - **No model call during transfer:** conversion, filtering, budgeting, and writing are deterministic local operations.
135
+ - **Visible provenance:** the imported session identifies its source and states what was dropped without putting that marker in model context. Pi renders it as a transcript entry; Claude Code uses its native metadata entry; Codex prints it in the CLI because no safe durable out-of-context entry is verified.
136
+
137
+ ## Documentation
138
+
139
+ The user and maintainer documentation is organized in [`docs/`](docs/README.md).
140
+
141
+ ## Development
142
+
143
+ Development requires Node 24 LTS and pnpm 11.20.0.
144
+
145
+ ```sh
146
+ pnpm install
147
+ pnpm build
148
+ pnpm test
149
+ pnpm typecheck
150
+ pnpm lint
151
+ ```
152
+
153
+ Run the command help after a build:
154
+
155
+ ```sh
156
+ node dist/bin.js --help
157
+ ```
158
+
159
+ ## License
160
+
161
+ MIT.
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The Claude Code adapter: the port of `src/adapters/`, filled in with what this module knows
3
+ * about Claude Code.
4
+ */
5
+ import type { AgentAdapter, ClaudeCodeAdapterFactory } from "./contract.js";
6
+ /** Claude Code's context window, used when configuration overrides none (FR-18). */
7
+ export declare const DEFAULT_WINDOW_TOKENS = 200000;
8
+ /** The command the landing hands back, because Claude Code cannot move the user (C-2, FR-45). */
9
+ export declare const HANDOVER_COMMAND = "claude --resume";
10
+ export interface ClaudeCodeAdapterDeps {
11
+ /**
12
+ * The repository the import runs in. Claude Code keys a session to a repository, and no
13
+ * argument of the port names one, so the working directory is the signal.
14
+ */
15
+ cwd: string;
16
+ env: Record<string, string | undefined>;
17
+ homeDir: string;
18
+ }
19
+ /**
20
+ * Build the adapter. `deps` exists for the tests of this module: the shipped factory below
21
+ * passes the real process values.
22
+ */
23
+ export declare function createClaudeCodeAdapter(deps?: Partial<ClaudeCodeAdapterDeps>): AgentAdapter;
24
+ /** The only export of this module (FR-57). */
25
+ export declare const claudeCodeAdapter: ClaudeCodeAdapterFactory;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * The Claude Code adapter: the port of `src/adapters/`, filled in with what this module knows
3
+ * about Claude Code.
4
+ */
5
+ import { resolveDefaultHome } from "./layout.js";
6
+ import { readBack } from "./readback.js";
7
+ import { listSessions, loadSession } from "./reader.js";
8
+ import { serialize, validate } from "./writer.js";
9
+ /** Claude Code's context window, used when configuration overrides none (FR-18). */
10
+ export const DEFAULT_WINDOW_TOKENS = 200_000;
11
+ /** The command the landing hands back, because Claude Code cannot move the user (C-2, FR-45). */
12
+ export const HANDOVER_COMMAND = "claude --resume";
13
+ /**
14
+ * Build the adapter. `deps` exists for the tests of this module: the shipped factory below
15
+ * passes the real process values.
16
+ */
17
+ export function createClaudeCodeAdapter(deps = {}) {
18
+ const cwd = deps.cwd ?? process.cwd();
19
+ const env = deps.env ?? process.env;
20
+ // Resolved once: capabilities() is pure, synchronous, and equal on every call.
21
+ const capabilities = Object.freeze({
22
+ agent: "claude-code",
23
+ roles: ["source", "target"],
24
+ selection: "numbered-list",
25
+ landing: "create-only",
26
+ provenance: "out-of-context-entry",
27
+ defaultHome: deps.homeDir === undefined ? resolveDefaultHome(env) : resolveDefaultHome(env, deps.homeDir),
28
+ defaultWindowTokens: DEFAULT_WINDOW_TOKENS,
29
+ });
30
+ return {
31
+ capabilities() {
32
+ return capabilities;
33
+ },
34
+ listSessions(home) {
35
+ return listSessions(home);
36
+ },
37
+ loadSession(descriptor) {
38
+ return loadSession(descriptor);
39
+ },
40
+ serialize(session, target, marker) {
41
+ return serialize(session, target, marker, { cwd });
42
+ },
43
+ validate(serialized) {
44
+ return validate(serialized);
45
+ },
46
+ readBack(home, sessionId) {
47
+ return readBack(home, sessionId);
48
+ },
49
+ switchTo(_home, sessionId, _runtime) {
50
+ return Promise.reject(new Error('Claude Code declares landing "create-only": it cannot move the user into a session ' +
51
+ `(C-2). Run: ${HANDOVER_COMMAND} ${sessionId}`));
52
+ },
53
+ };
54
+ }
55
+ /** The only export of this module (FR-57). */
56
+ export const claudeCodeAdapter = {
57
+ create() {
58
+ return createClaudeCodeAdapter();
59
+ },
60
+ };
@@ -0,0 +1,8 @@
1
+ import type { AdapterRole, AgentAdapter, AgentCapabilities, AgentRuntime, Bytes, LandingLevel, PendingFile, ProvenanceSupport, SelectionLevel, SerializedSession, StoredSessionFacts, SwitchOutcome, ValidationDefect } from "../contract.js";
2
+ export type { AdapterRole, AgentAdapter, AgentCapabilities, AgentRuntime, Bytes, LandingLevel, PendingFile, ProvenanceSupport, SelectionLevel, SerializedSession, StoredSessionFacts, SwitchOutcome, ValidationDefect, };
3
+ import type { AgentId, CanonicalSession, CanonicalTurn, HomePath, ProvenanceMarker, RepoSnapshot, SessionDescriptor, SessionId, SessionRef, SourceProvenance, TargetProfile, ToolCallRecord, ToolEffect, TurnKind, TurnRole } from "../../session/contract.js";
4
+ export type { AgentId, CanonicalSession, CanonicalTurn, HomePath, ProvenanceMarker, RepoSnapshot, SessionDescriptor, SessionId, SessionRef, SourceProvenance, TargetProfile, ToolCallRecord, ToolEffect, TurnKind, TurnRole, };
5
+ /** Builds the Claude Code adapter. The only export of this module (FR-57). */
6
+ export interface ClaudeCodeAdapterFactory {
7
+ create(): AgentAdapter;
8
+ }
@@ -0,0 +1,4 @@
1
+ // GENERATED from src/adapters/claude-code/module.md — the Public Contract section is the normative home.
2
+ // Declarations only: no behaviour, no defaults. If this file and module.md disagree,
3
+ // the document wins and this file is corrected.
4
+ export {};
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The Claude Code session file: one JSON object per line.
3
+ *
4
+ * The store holds ten entry types (C-3). This module reads the few that carry turns and writes
5
+ * exactly two, `user` and `assistant` (C-9) — fewer types, fewer ways to corrupt the store.
6
+ */
7
+ export declare const ENTRY_TYPE_USER = "user";
8
+ export declare const ENTRY_TYPE_ASSISTANT = "assistant";
9
+ export declare const ENTRY_TYPE_SUMMARY = "summary";
10
+ /** The only entry types `serialize` writes (C-9). */
11
+ export declare const WRITTEN_ENTRY_TYPES: readonly string[];
12
+ export type RawEntry = Record<string, unknown>;
13
+ export interface JsonlParse {
14
+ entries: RawEntry[];
15
+ /** Set when a line is not a complete entry — the file is cut mid-entry. */
16
+ unreadable: string | null;
17
+ }
18
+ export interface ActiveEntryPath {
19
+ entries: RawEntry[];
20
+ unreadable: string | null;
21
+ }
22
+ /** Parse a session file. A line that is not a complete entry makes the whole file unreadable. */
23
+ export declare function parseJsonl(text: string): JsonlParse;
24
+ /** Follow the last non-sidechain UUID record to the root of Claude's active transcript. */
25
+ export declare function resolveActiveEntryPath(entries: RawEntry[]): ActiveEntryPath;
26
+ export declare function asString(value: unknown): string | null;
27
+ export declare function asObject(value: unknown): Record<string, unknown> | null;
28
+ export declare function asArray(value: unknown): unknown[] | null;
29
+ /** ISO-8601 UTC, the vocabulary every timestamp of the canonical model uses. */
30
+ export declare function toIsoUtc(value: unknown): string | null;
@@ -0,0 +1,107 @@
1
+ /**
2
+ * The Claude Code session file: one JSON object per line.
3
+ *
4
+ * The store holds ten entry types (C-3). This module reads the few that carry turns and writes
5
+ * exactly two, `user` and `assistant` (C-9) — fewer types, fewer ways to corrupt the store.
6
+ */
7
+ export const ENTRY_TYPE_USER = "user";
8
+ export const ENTRY_TYPE_ASSISTANT = "assistant";
9
+ export const ENTRY_TYPE_SUMMARY = "summary";
10
+ /** The only entry types `serialize` writes (C-9). */
11
+ export const WRITTEN_ENTRY_TYPES = [ENTRY_TYPE_USER, ENTRY_TYPE_ASSISTANT];
12
+ /** Parse a session file. A line that is not a complete entry makes the whole file unreadable. */
13
+ export function parseJsonl(text) {
14
+ const entries = [];
15
+ const lines = text.split("\n");
16
+ for (let i = 0; i < lines.length; i++) {
17
+ const line = lines[i] ?? "";
18
+ if (line.trim() === "")
19
+ continue;
20
+ let value;
21
+ try {
22
+ value = JSON.parse(line);
23
+ }
24
+ catch {
25
+ return { entries, unreadable: `line ${i + 1} is not a complete entry` };
26
+ }
27
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
28
+ return { entries, unreadable: `line ${i + 1} is not an entry object` };
29
+ }
30
+ entries.push(value);
31
+ }
32
+ return { entries, unreadable: null };
33
+ }
34
+ /** Follow the last non-sidechain UUID record to the root of Claude's active transcript. */
35
+ export function resolveActiveEntryPath(entries) {
36
+ const byUuid = new Map();
37
+ const mainEntries = [];
38
+ for (const entry of entries) {
39
+ if (entry.isSidechain === true || entry.uuid === undefined)
40
+ continue;
41
+ const uuid = asString(entry.uuid);
42
+ if (uuid === null)
43
+ return { entries: [], unreadable: "entry has a malformed uuid" };
44
+ if (byUuid.has(uuid)) {
45
+ return { entries: [], unreadable: `duplicate entry uuid ${uuid}` };
46
+ }
47
+ if (entry.parentUuid !== null && typeof entry.parentUuid !== "string") {
48
+ return {
49
+ entries: [],
50
+ unreadable: `entry ${uuid} has a malformed parentUuid`,
51
+ };
52
+ }
53
+ byUuid.set(uuid, entry);
54
+ mainEntries.push(entry);
55
+ }
56
+ const leaf = mainEntries[mainEntries.length - 1];
57
+ if (leaf === undefined)
58
+ return { entries: [], unreadable: null };
59
+ const reversed = [];
60
+ const visited = new Set();
61
+ let current = leaf;
62
+ while (current !== undefined) {
63
+ const uuid = asString(current.uuid);
64
+ if (uuid === null)
65
+ return { entries: [], unreadable: "active entry has no uuid" };
66
+ if (visited.has(uuid)) {
67
+ return {
68
+ entries: [],
69
+ unreadable: `entry graph contains a cycle at ${uuid}`,
70
+ };
71
+ }
72
+ visited.add(uuid);
73
+ reversed.push(current);
74
+ if (current.parentUuid === null)
75
+ break;
76
+ const parent = byUuid.get(current.parentUuid);
77
+ if (parent === undefined) {
78
+ return {
79
+ entries: [],
80
+ unreadable: `entry ${uuid} refers to missing parent ${String(current.parentUuid)}`,
81
+ };
82
+ }
83
+ current = parent;
84
+ }
85
+ return { entries: reversed.reverse(), unreadable: null };
86
+ }
87
+ export function asString(value) {
88
+ return typeof value === "string" && value !== "" ? value : null;
89
+ }
90
+ export function asObject(value) {
91
+ if (typeof value !== "object" || value === null || Array.isArray(value))
92
+ return null;
93
+ return value;
94
+ }
95
+ export function asArray(value) {
96
+ return Array.isArray(value) ? value : null;
97
+ }
98
+ /** ISO-8601 UTC, the vocabulary every timestamp of the canonical model uses. */
99
+ export function toIsoUtc(value) {
100
+ const text = asString(value);
101
+ if (text === null)
102
+ return null;
103
+ const parsed = Date.parse(text);
104
+ if (!Number.isFinite(parsed))
105
+ return null;
106
+ return new Date(parsed).toISOString();
107
+ }
@@ -0,0 +1,3 @@
1
+ /** The Claude Code adapter. Building it is the only thing this module offers (FR-57). */
2
+ export { claudeCodeAdapter } from "./adapter.js";
3
+ export type { ClaudeCodeAdapterFactory } from "./contract.js";
@@ -0,0 +1,2 @@
1
+ /** The Claude Code adapter. Building it is the only thing this module offers (FR-57). */
2
+ export { claudeCodeAdapter } from "./adapter.js";
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Where Claude Code keeps its sessions inside a home.
3
+ *
4
+ * A home holds `projects/<encoded repository path>/<session id>.jsonl`. The encoding and the
5
+ * default home are facts about the installed version, not assumptions: the live boundary test
6
+ * T-CC-16 lands a real session and compares it with what this file computes.
7
+ */
8
+ import type { HomePath, SessionId } from "./contract.js";
9
+ /** Session files live one directory per repository, under this directory of the home. */
10
+ export declare const PROJECTS_DIR = "projects";
11
+ export declare const SESSION_FILE_SUFFIX = ".jsonl";
12
+ /** Claude Code reads this before falling back to ~/.claude. It is how `~/.claude-team` is named. */
13
+ export declare const CONFIG_DIR_ENV = "CLAUDE_CONFIG_DIR";
14
+ export declare const DEFAULT_HOME_BASENAME = ".claude";
15
+ /** The home used when the user names none (FR-3). Absolute, resolved. */
16
+ export declare function resolveDefaultHome(env?: Record<string, string | undefined>, userHome?: string): HomePath;
17
+ /**
18
+ * The project directory name of a repository: the absolute path with every character that is
19
+ * not a letter or a digit replaced by a dash.
20
+ */
21
+ export declare function encodeProjectPath(repoPath: string): string;
22
+ export declare function projectsDir(home: HomePath): string;
23
+ export declare function projectDir(home: HomePath, repoPath: string): string;
24
+ export declare function sessionFilePath(home: HomePath, repoPath: string, id: SessionId): string;
25
+ export declare function sessionIdOf(filePath: string): SessionId;
26
+ /** Every session file of a home, across all of its project directories. */
27
+ export declare function listSessionFiles(home: HomePath): Promise<string[]>;
28
+ /** The file of one session id, or null when the home does not hold it. */
29
+ export declare function findSessionFile(home: HomePath, id: SessionId): Promise<string | null>;
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Where Claude Code keeps its sessions inside a home.
3
+ *
4
+ * A home holds `projects/<encoded repository path>/<session id>.jsonl`. The encoding and the
5
+ * default home are facts about the installed version, not assumptions: the live boundary test
6
+ * T-CC-16 lands a real session and compares it with what this file computes.
7
+ */
8
+ import { readdir } from "node:fs/promises";
9
+ import { homedir } from "node:os";
10
+ import path from "node:path";
11
+ /** Session files live one directory per repository, under this directory of the home. */
12
+ export const PROJECTS_DIR = "projects";
13
+ export const SESSION_FILE_SUFFIX = ".jsonl";
14
+ /** Claude Code reads this before falling back to ~/.claude. It is how `~/.claude-team` is named. */
15
+ export const CONFIG_DIR_ENV = "CLAUDE_CONFIG_DIR";
16
+ export const DEFAULT_HOME_BASENAME = ".claude";
17
+ /** The home used when the user names none (FR-3). Absolute, resolved. */
18
+ export function resolveDefaultHome(env = process.env, userHome = homedir()) {
19
+ const configured = env[CONFIG_DIR_ENV];
20
+ if (configured !== undefined && configured !== "" && path.isAbsolute(configured)) {
21
+ return path.resolve(configured);
22
+ }
23
+ return path.join(path.resolve(userHome), DEFAULT_HOME_BASENAME);
24
+ }
25
+ /**
26
+ * The project directory name of a repository: the absolute path with every character that is
27
+ * not a letter or a digit replaced by a dash.
28
+ */
29
+ export function encodeProjectPath(repoPath) {
30
+ return path.resolve(repoPath).replace(/[^a-zA-Z0-9]/g, "-");
31
+ }
32
+ export function projectsDir(home) {
33
+ return path.join(home, PROJECTS_DIR);
34
+ }
35
+ export function projectDir(home, repoPath) {
36
+ return path.join(projectsDir(home), encodeProjectPath(repoPath));
37
+ }
38
+ export function sessionFilePath(home, repoPath, id) {
39
+ return path.join(projectDir(home, repoPath), `${id}${SESSION_FILE_SUFFIX}`);
40
+ }
41
+ export function sessionIdOf(filePath) {
42
+ return path.basename(filePath, SESSION_FILE_SUFFIX);
43
+ }
44
+ /** Every session file of a home, across all of its project directories. */
45
+ export async function listSessionFiles(home) {
46
+ const root = projectsDir(home);
47
+ let projects;
48
+ try {
49
+ projects = (await readdir(root, { withFileTypes: true }))
50
+ .filter((item) => item.isDirectory())
51
+ .map((item) => item.name);
52
+ }
53
+ catch {
54
+ return [];
55
+ }
56
+ const files = [];
57
+ for (const project of projects) {
58
+ const dir = path.join(root, project);
59
+ try {
60
+ for (const item of await readdir(dir, { withFileTypes: true })) {
61
+ if (item.isFile() && item.name.endsWith(SESSION_FILE_SUFFIX)) {
62
+ files.push(path.join(dir, item.name));
63
+ }
64
+ }
65
+ }
66
+ catch {
67
+ // A project directory that cannot be read holds no session this tool can offer.
68
+ }
69
+ }
70
+ return files;
71
+ }
72
+ /** The file of one session id, or null when the home does not hold it. */
73
+ export async function findSessionFile(home, id) {
74
+ const wanted = `${id}${SESSION_FILE_SUFFIX}`;
75
+ for (const file of await listSessionFiles(home)) {
76
+ if (path.basename(file) === wanted)
77
+ return file;
78
+ }
79
+ return null;
80
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Reading a committed session back.
3
+ *
4
+ * C-9's own caution is why this is not optional: a throwaway directory holding one session does
5
+ * not prove a real store is safe, so the import compares what it wrote with what the store holds
6
+ * (FR-52) and whether the native resume list can open it (FR-51).
7
+ */
8
+ import type { HomePath, SessionId, StoredSessionFacts } from "./contract.js";
9
+ export declare function readBack(home: HomePath, sessionId: SessionId): Promise<StoredSessionFacts>;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Reading a committed session back.
3
+ *
4
+ * C-9's own caution is why this is not optional: a throwaway directory holding one session does
5
+ * not prove a real store is safe, so the import compares what it wrote with what the store holds
6
+ * (FR-52) and whether the native resume list can open it (FR-51).
7
+ */
8
+ import { readFile } from "node:fs/promises";
9
+ import { parseJsonl } from "./entries.js";
10
+ import { findSessionFile } from "./layout.js";
11
+ import { readSessionText } from "./reader.js";
12
+ import { isStructurallyOpenable } from "./validation.js";
13
+ export async function readBack(home, sessionId) {
14
+ const absent = {
15
+ sessionId,
16
+ itemCount: 0,
17
+ openable: false,
18
+ };
19
+ const file = await findSessionFile(home, sessionId);
20
+ if (file === null)
21
+ return absent;
22
+ let text;
23
+ try {
24
+ text = await readFile(file, "utf8");
25
+ }
26
+ catch {
27
+ return absent;
28
+ }
29
+ const { entries, unreadable } = parseJsonl(text);
30
+ const read = unreadable === null ? readSessionText(text) : null;
31
+ return {
32
+ sessionId,
33
+ itemCount: entries.length,
34
+ openable: unreadable === null &&
35
+ read?.unreadable === null &&
36
+ read.turns.length > 0 &&
37
+ isStructurallyOpenable(entries, sessionId),
38
+ };
39
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Reading a Claude Code session into the neutral vocabulary.
3
+ *
4
+ * Two rules shape everything here: no result body crosses (FR-24) and no system prompt,
5
+ * environment value, token or hidden reasoning crosses (FR-28, NG-7). Both are enforced by
6
+ * what this file builds, not by what a caller does with it.
7
+ */
8
+ import type { CanonicalSession, CanonicalTurn, HomePath, SessionDescriptor } from "./contract.js";
9
+ /** FR-25: every dropped body is marked in text the model can read. */
10
+ export declare const DROPPED_MARKER = "(content dropped: imported session, may be stale)";
11
+ export declare const UNREADABLE_TITLE = "(unreadable session file)";
12
+ export declare const UNTITLED = "(no user message)";
13
+ export interface SessionReadResult {
14
+ turns: CanonicalTurn[];
15
+ /**
16
+ * Entries this adapter does not read: the other entry types of C-3, hidden reasoning, and
17
+ * everything FR-28 excludes. `AgentAdapter` has no field for this count, so it is reported
18
+ * here and asserted by T-CC-15.
19
+ */
20
+ skipped: number;
21
+ /** Set when the file is cut mid-entry. Then the session is not readable at all. */
22
+ unreadable: string | null;
23
+ title: string;
24
+ startedAt: string | null;
25
+ updatedAt: string | null;
26
+ repoPath: string | null;
27
+ branch: string | null;
28
+ changedPaths: string[];
29
+ }
30
+ /** The redacted source arguments, shortened so one call stays one line. */
31
+ export declare function renderArguments(input: unknown): string;
32
+ /** Read a whole session file into canonical turns. Pure: it takes the text, not a path. */
33
+ export declare function readSessionText(text: string): SessionReadResult;
34
+ /** One row per session file of the home, newest first (FR-11, FR-14). */
35
+ export declare function listSessions(home: HomePath): Promise<SessionDescriptor[]>;
36
+ /** One session in the neutral vocabulary. A file cut mid-entry is reported, never guessed at. */
37
+ export declare function loadSession(descriptor: SessionDescriptor): Promise<CanonicalSession>;