@sagmans/dsh-tui 0.3.0 → 0.5.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 (253) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +407 -53
  3. package/cordis.patch.yml +14 -0
  4. package/lib/agent/present.js +1 -1
  5. package/lib/agent/present.js.map +1 -1
  6. package/lib/agent/projections.d.ts +22 -6
  7. package/lib/agent/projections.d.ts.map +1 -1
  8. package/lib/agent/projections.js +21 -10
  9. package/lib/agent/projections.js.map +1 -1
  10. package/lib/agent/prompt-history.d.ts +89 -0
  11. package/lib/agent/prompt-history.d.ts.map +1 -0
  12. package/lib/agent/prompt-history.js +382 -0
  13. package/lib/agent/prompt-history.js.map +1 -0
  14. package/lib/agent/status.d.ts +6 -0
  15. package/lib/agent/status.d.ts.map +1 -1
  16. package/lib/agent/status.js +1 -0
  17. package/lib/agent/status.js.map +1 -1
  18. package/lib/cards.d.ts +83 -13
  19. package/lib/cards.d.ts.map +1 -1
  20. package/lib/cards.js +96 -28
  21. package/lib/cards.js.map +1 -1
  22. package/lib/export.d.ts +3 -3
  23. package/lib/export.d.ts.map +1 -1
  24. package/lib/export.js +23 -13
  25. package/lib/export.js.map +1 -1
  26. package/lib/fold-cursor.d.ts +12 -5
  27. package/lib/fold-cursor.d.ts.map +1 -1
  28. package/lib/fold-cursor.js +16 -5
  29. package/lib/fold-cursor.js.map +1 -1
  30. package/lib/gates.d.ts.map +1 -1
  31. package/lib/gates.js +42 -9
  32. package/lib/gates.js.map +1 -1
  33. package/lib/herdr/client.d.ts +57 -0
  34. package/lib/herdr/client.d.ts.map +1 -0
  35. package/lib/herdr/client.js +218 -0
  36. package/lib/herdr/client.js.map +1 -0
  37. package/lib/herdr/constants.d.ts +93 -0
  38. package/lib/herdr/constants.d.ts.map +1 -0
  39. package/lib/herdr/constants.js +79 -0
  40. package/lib/herdr/constants.js.map +1 -0
  41. package/lib/herdr/reporter.d.ts +60 -0
  42. package/lib/herdr/reporter.d.ts.map +1 -0
  43. package/lib/herdr/reporter.js +217 -0
  44. package/lib/herdr/reporter.js.map +1 -0
  45. package/lib/herdr/state.d.ts +57 -0
  46. package/lib/herdr/state.d.ts.map +1 -0
  47. package/lib/herdr/state.js +62 -0
  48. package/lib/herdr/state.js.map +1 -0
  49. package/lib/index.d.ts.map +1 -1
  50. package/lib/index.js +700 -81
  51. package/lib/index.js.map +1 -1
  52. package/lib/injection.d.ts +9 -0
  53. package/lib/injection.d.ts.map +1 -0
  54. package/lib/injection.js +128 -0
  55. package/lib/injection.js.map +1 -0
  56. package/lib/input/actions.d.ts +19 -2
  57. package/lib/input/actions.d.ts.map +1 -1
  58. package/lib/input/actions.js +60 -11
  59. package/lib/input/actions.js.map +1 -1
  60. package/lib/input/cancel.d.ts +55 -0
  61. package/lib/input/cancel.d.ts.map +1 -0
  62. package/lib/input/cancel.js +47 -0
  63. package/lib/input/cancel.js.map +1 -0
  64. package/lib/input/completion.d.ts +5 -2
  65. package/lib/input/completion.d.ts.map +1 -1
  66. package/lib/input/completion.js +79 -4
  67. package/lib/input/completion.js.map +1 -1
  68. package/lib/input/file-search.d.ts +105 -0
  69. package/lib/input/file-search.d.ts.map +1 -0
  70. package/lib/input/file-search.js +665 -0
  71. package/lib/input/file-search.js.map +1 -0
  72. package/lib/input/fuzzy.d.ts +42 -0
  73. package/lib/input/fuzzy.d.ts.map +1 -0
  74. package/lib/input/fuzzy.js +289 -0
  75. package/lib/input/fuzzy.js.map +1 -0
  76. package/lib/input/ghost.d.ts +46 -0
  77. package/lib/input/ghost.d.ts.map +1 -0
  78. package/lib/input/ghost.js +58 -0
  79. package/lib/input/ghost.js.map +1 -0
  80. package/lib/input/keymap.d.ts +3 -1
  81. package/lib/input/keymap.d.ts.map +1 -1
  82. package/lib/input/keymap.js +16 -3
  83. package/lib/input/keymap.js.map +1 -1
  84. package/lib/input/match.d.ts +12 -4
  85. package/lib/input/match.d.ts.map +1 -1
  86. package/lib/input/match.js +50 -51
  87. package/lib/input/match.js.map +1 -1
  88. package/lib/input/submission.d.ts +34 -1
  89. package/lib/input/submission.d.ts.map +1 -1
  90. package/lib/input/submission.js +33 -5
  91. package/lib/input/submission.js.map +1 -1
  92. package/lib/keys-command.d.ts +19 -3
  93. package/lib/keys-command.d.ts.map +1 -1
  94. package/lib/keys-command.js +34 -31
  95. package/lib/keys-command.js.map +1 -1
  96. package/lib/settings-notice.d.ts +5 -0
  97. package/lib/settings-notice.d.ts.map +1 -1
  98. package/lib/settings-notice.js +9 -7
  99. package/lib/settings-notice.js.map +1 -1
  100. package/lib/stash/lock.d.ts +72 -0
  101. package/lib/stash/lock.d.ts.map +1 -0
  102. package/lib/stash/lock.js +374 -0
  103. package/lib/stash/lock.js.map +1 -0
  104. package/lib/stash/paths.d.ts +22 -0
  105. package/lib/stash/paths.d.ts.map +1 -0
  106. package/lib/stash/paths.js +84 -0
  107. package/lib/stash/paths.js.map +1 -0
  108. package/lib/stash/private-fs.d.ts +61 -0
  109. package/lib/stash/private-fs.d.ts.map +1 -0
  110. package/lib/stash/private-fs.js +379 -0
  111. package/lib/stash/private-fs.js.map +1 -0
  112. package/lib/stash/schema.d.ts +56 -0
  113. package/lib/stash/schema.d.ts.map +1 -0
  114. package/lib/stash/schema.js +108 -0
  115. package/lib/stash/schema.js.map +1 -0
  116. package/lib/stash/store.d.ts +92 -0
  117. package/lib/stash/store.d.ts.map +1 -0
  118. package/lib/stash/store.js +262 -0
  119. package/lib/stash/store.js.map +1 -0
  120. package/lib/stash.d.ts +122 -0
  121. package/lib/stash.d.ts.map +1 -0
  122. package/lib/stash.js +353 -0
  123. package/lib/stash.js.map +1 -0
  124. package/lib/terminal/external-editor.d.ts +93 -0
  125. package/lib/terminal/external-editor.d.ts.map +1 -0
  126. package/lib/terminal/external-editor.js +263 -0
  127. package/lib/terminal/external-editor.js.map +1 -0
  128. package/lib/terminal/host-writes.d.ts +39 -0
  129. package/lib/terminal/host-writes.d.ts.map +1 -0
  130. package/lib/terminal/host-writes.js +136 -0
  131. package/lib/terminal/host-writes.js.map +1 -0
  132. package/lib/terminal/signals.d.ts +25 -0
  133. package/lib/terminal/signals.d.ts.map +1 -0
  134. package/lib/terminal/signals.js +39 -0
  135. package/lib/terminal/signals.js.map +1 -0
  136. package/lib/terminal/warning-screen.d.ts +19 -1
  137. package/lib/terminal/warning-screen.d.ts.map +1 -1
  138. package/lib/terminal/warning-screen.js +37 -1
  139. package/lib/terminal/warning-screen.js.map +1 -1
  140. package/lib/terminal-text.d.ts +72 -0
  141. package/lib/terminal-text.d.ts.map +1 -0
  142. package/lib/terminal-text.js +603 -0
  143. package/lib/terminal-text.js.map +1 -0
  144. package/lib/text.d.ts +45 -6
  145. package/lib/text.d.ts.map +1 -1
  146. package/lib/text.js +106 -46
  147. package/lib/text.js.map +1 -1
  148. package/lib/theme-command.d.ts +7 -4
  149. package/lib/theme-command.d.ts.map +1 -1
  150. package/lib/theme-command.js +32 -12
  151. package/lib/theme-command.js.map +1 -1
  152. package/lib/theme-files.d.ts +110 -0
  153. package/lib/theme-files.d.ts.map +1 -0
  154. package/lib/theme-files.js +352 -0
  155. package/lib/theme-files.js.map +1 -0
  156. package/lib/theme-schema.d.ts +121 -0
  157. package/lib/theme-schema.d.ts.map +1 -0
  158. package/lib/theme-schema.js +87 -0
  159. package/lib/theme-schema.js.map +1 -0
  160. package/lib/theme-settings.d.ts +72 -10
  161. package/lib/theme-settings.d.ts.map +1 -1
  162. package/lib/theme-settings.js +162 -45
  163. package/lib/theme-settings.js.map +1 -1
  164. package/lib/theme-tokens.d.ts +39 -7
  165. package/lib/theme-tokens.d.ts.map +1 -1
  166. package/lib/theme-tokens.js +104 -13
  167. package/lib/theme-tokens.js.map +1 -1
  168. package/lib/theme.d.ts +24 -2
  169. package/lib/theme.d.ts.map +1 -1
  170. package/lib/theme.js +49 -6
  171. package/lib/theme.js.map +1 -1
  172. package/lib/todo-guard.d.ts +116 -0
  173. package/lib/todo-guard.d.ts.map +1 -0
  174. package/lib/todo-guard.js +259 -0
  175. package/lib/todo-guard.js.map +1 -0
  176. package/lib/tool-display.d.ts +57 -0
  177. package/lib/tool-display.d.ts.map +1 -0
  178. package/lib/tool-display.js +51 -0
  179. package/lib/tool-display.js.map +1 -0
  180. package/lib/transcript.d.ts +28 -0
  181. package/lib/transcript.d.ts.map +1 -1
  182. package/lib/transcript.js +126 -37
  183. package/lib/transcript.js.map +1 -1
  184. package/lib/ui/diff.d.ts +49 -0
  185. package/lib/ui/diff.d.ts.map +1 -0
  186. package/lib/ui/diff.js +208 -0
  187. package/lib/ui/diff.js.map +1 -0
  188. package/lib/ui/dock.d.ts.map +1 -1
  189. package/lib/ui/dock.js +9 -6
  190. package/lib/ui/dock.js.map +1 -1
  191. package/lib/ui/editor.d.ts +41 -1
  192. package/lib/ui/editor.d.ts.map +1 -1
  193. package/lib/ui/editor.js +100 -4
  194. package/lib/ui/editor.js.map +1 -1
  195. package/lib/ui/frame.d.ts +16 -5
  196. package/lib/ui/frame.d.ts.map +1 -1
  197. package/lib/ui/frame.js +32 -13
  198. package/lib/ui/frame.js.map +1 -1
  199. package/lib/ui/history-picker.d.ts +15 -0
  200. package/lib/ui/history-picker.d.ts.map +1 -0
  201. package/lib/ui/history-picker.js +26 -0
  202. package/lib/ui/history-picker.js.map +1 -0
  203. package/lib/ui/keymap-picker.d.ts +16 -0
  204. package/lib/ui/keymap-picker.d.ts.map +1 -0
  205. package/lib/ui/keymap-picker.js +41 -0
  206. package/lib/ui/keymap-picker.js.map +1 -0
  207. package/lib/ui/layout.d.ts +33 -0
  208. package/lib/ui/layout.d.ts.map +1 -0
  209. package/lib/ui/layout.js +33 -0
  210. package/lib/ui/layout.js.map +1 -0
  211. package/lib/ui/markdown.d.ts +25 -3
  212. package/lib/ui/markdown.d.ts.map +1 -1
  213. package/lib/ui/markdown.js +12 -8
  214. package/lib/ui/markdown.js.map +1 -1
  215. package/lib/ui/picker-card.d.ts +42 -0
  216. package/lib/ui/picker-card.d.ts.map +1 -0
  217. package/lib/ui/picker-card.js +148 -0
  218. package/lib/ui/picker-card.js.map +1 -0
  219. package/lib/ui/picker.d.ts +50 -2
  220. package/lib/ui/picker.d.ts.map +1 -1
  221. package/lib/ui/picker.js +66 -7
  222. package/lib/ui/picker.js.map +1 -1
  223. package/lib/ui/prompt.d.ts +12 -2
  224. package/lib/ui/prompt.d.ts.map +1 -1
  225. package/lib/ui/prompt.js +25 -2
  226. package/lib/ui/prompt.js.map +1 -1
  227. package/lib/ui/rows.d.ts +8 -6
  228. package/lib/ui/rows.d.ts.map +1 -1
  229. package/lib/ui/rows.js +10 -18
  230. package/lib/ui/rows.js.map +1 -1
  231. package/lib/ui/stash-picker.d.ts +43 -0
  232. package/lib/ui/stash-picker.d.ts.map +1 -0
  233. package/lib/ui/stash-picker.js +78 -0
  234. package/lib/ui/stash-picker.js.map +1 -0
  235. package/lib/ui/status.d.ts +2 -0
  236. package/lib/ui/status.d.ts.map +1 -1
  237. package/lib/ui/status.js +25 -16
  238. package/lib/ui/status.js.map +1 -1
  239. package/lib/ui/theme-picker.d.ts +18 -0
  240. package/lib/ui/theme-picker.d.ts.map +1 -0
  241. package/lib/ui/theme-picker.js +68 -0
  242. package/lib/ui/theme-picker.js.map +1 -0
  243. package/lib/ui/view.d.ts +146 -22
  244. package/lib/ui/view.d.ts.map +1 -1
  245. package/lib/ui/view.js +461 -154
  246. package/lib/ui/view.js.map +1 -1
  247. package/lib/work.d.ts +3 -1
  248. package/lib/work.d.ts.map +1 -1
  249. package/lib/work.js +9 -1
  250. package/lib/work.js.map +1 -1
  251. package/package.json +7 -1
  252. package/themes/deepseek-blue.yaml +183 -0
  253. package/themes/violet-orbit.yaml +178 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/stash/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,262 @@
1
+ // StashStore: on-disk CRUD for stash entries, scoped to one session.
2
+ //
3
+ // Entries are stored newest-first (`entries[0]` is `stash@{0}`), mirroring git's
4
+ // index-0-is-tip convention. Every mutation re-reads the file inside an
5
+ // exclusive lock so two surfaces in the same directory cannot lose updates to a
6
+ // read-modify-write race. Writes are atomic (exclusive temp + rename + directory
7
+ // fsync) with tight 0o600/0o700 permissions, because a stashed draft may hold
8
+ // anything the reader was about to send. A corrupt file is quarantined rather
9
+ // than overwritten, so a hand-edit mistake never silently destroys saved drafts.
10
+ import { rename, rm } from 'node:fs/promises';
11
+ import path from 'node:path';
12
+ import { StashCommittedError, withStashFileLock, withStashMutationLock, } from "./lock.js";
13
+ import { ensurePrivateDirectory, hasErrorCode, quarantinePrivateFile, readPrivateTextFile, syncDirectoryEntry, writePrivateFileExclusive, } from "./private-fs.js";
14
+ import { assertSafeEntryId, assertSafeStashText, createEmptyStashFile, createNewId, isRecord, parseStashFile, resolveBySelector, sanitizeStashText, STASH_SCHEMA_VERSION, } from "./schema.js";
15
+ /**
16
+ * A stash file larger than this is refused, never quarantined: hiding a
17
+ * legitimate bank because it grew past a cap would be worse than refusing it.
18
+ */
19
+ export const MAX_STASH_FILE_BYTES = 16_777_216;
20
+ const DUPLICATE_STASH_ID_MESSAGE = 'duplicate stash id';
21
+ const UNSUPPORTED_SCHEMA_GUIDANCE = 'Upgrade dsh-tui before using this stash, or move the stash file aside for safe recovery.';
22
+ const FILE_TOO_LARGE_MESSAGE = 'this session has no room left for another draft; drop one first';
23
+ /**
24
+ * A bank that cannot be written because it would no longer be readable.
25
+ *
26
+ * The cap is only useful if it is enforced before the write: a file that grows
27
+ * past it is refused by every later read, so letting one save through would turn
28
+ * a full bank into an unreadable one.
29
+ */
30
+ export class StashFileTooLargeError extends Error {
31
+ bytes;
32
+ constructor(bytes) {
33
+ super(FILE_TOO_LARGE_MESSAGE);
34
+ this.name = 'StashFileTooLargeError';
35
+ this.bytes = bytes;
36
+ }
37
+ }
38
+ /** Stash data written by a newer build, which this one must not touch. */
39
+ export class UnsupportedStashSchemaError extends Error {
40
+ detectedVersion;
41
+ supportedVersion;
42
+ constructor(detectedVersion, supportedVersion = STASH_SCHEMA_VERSION) {
43
+ super(`stash data uses version ${detectedVersion}; this build supports versions through ${supportedVersion}. ${UNSUPPORTED_SCHEMA_GUIDANCE}`);
44
+ this.name = 'UnsupportedStashSchemaError';
45
+ this.detectedVersion = detectedVersion;
46
+ this.supportedVersion = supportedVersion;
47
+ }
48
+ }
49
+ export class StashStore {
50
+ file;
51
+ corruptRecovery;
52
+ filePath;
53
+ paths;
54
+ now;
55
+ write;
56
+ constructor(paths, loaded, now = Date.now, write = writeStashFile) {
57
+ this.paths = paths;
58
+ this.now = now;
59
+ this.write = write;
60
+ this.file = loaded.kind === 'ready' ? loaded.file : createEmptyStashFile(paths.sessionId, now());
61
+ this.corruptRecovery =
62
+ loaded.kind === 'corrupt' && loaded.quarantinedTo !== undefined
63
+ ? { path: loaded.quarantinedTo, syncFailed: loaded.quarantineSyncFailed }
64
+ : undefined;
65
+ this.filePath = paths.file;
66
+ }
67
+ get entries() {
68
+ return this.file.entries;
69
+ }
70
+ get entryCount() {
71
+ return this.file.entries.length;
72
+ }
73
+ /** The recovery location the last read produced, if any, reported once. */
74
+ takeQuarantine() {
75
+ const recovery = this.corruptRecovery;
76
+ this.corruptRecovery = undefined;
77
+ return recovery;
78
+ }
79
+ /** Resolve a selector against the entries currently held. */
80
+ find(selector) {
81
+ return resolveBySelector(this.file.entries, selector);
82
+ }
83
+ async refresh() {
84
+ await withStashFileLock(this.filePath, () => this.reloadFresh());
85
+ }
86
+ async add(input) {
87
+ const id = input.id ?? createNewId();
88
+ assertSafeEntryId(id);
89
+ assertSafeStashText(input.text);
90
+ // A draft is stored in the form it is safe to draw again, so no path back to
91
+ // the screen has to remember to strip it.
92
+ const text = sanitizeStashText(input.text);
93
+ return withStashMutationLock(this.filePath, async () => {
94
+ await this.reloadFresh();
95
+ if (this.file.entries.some(entry => entry.id === id))
96
+ throw new Error(DUPLICATE_STASH_ID_MESSAGE);
97
+ const entry = { id, text, createdAt: input.createdAt ?? this.now() };
98
+ const next = {
99
+ ...this.file,
100
+ updatedAt: entry.createdAt,
101
+ entries: [entry, ...this.file.entries],
102
+ };
103
+ return this.persistMutation(next, { entry, index: 0 });
104
+ });
105
+ }
106
+ /** Remove one entry by its exact id, reporting what was removed. */
107
+ async removeById(id) {
108
+ assertSafeEntryId(id);
109
+ return withStashMutationLock(this.filePath, async () => {
110
+ await this.reloadFresh();
111
+ return this.removeAt(selector => selector === id);
112
+ });
113
+ }
114
+ /** Remove the entry a selector names, reporting what was removed. */
115
+ async drop(selector) {
116
+ return withStashMutationLock(this.filePath, async () => {
117
+ await this.reloadFresh();
118
+ const resolved = resolveBySelector(this.file.entries, selector);
119
+ if (resolved === undefined)
120
+ return { didPersist: false, result: undefined };
121
+ const next = {
122
+ ...this.file,
123
+ updatedAt: this.now(),
124
+ entries: this.file.entries.filter((_, index) => index !== resolved.index),
125
+ };
126
+ return this.persistMutation(next, resolved);
127
+ });
128
+ }
129
+ /**
130
+ * Remove drafts, reporting how many were dropped.
131
+ *
132
+ * Ids restrict the removal to exactly what a reader confirmed. Clearing
133
+ * whatever is on disk instead would delete drafts another surface added while
134
+ * the confirmation was on screen, which is a loss nobody agreed to.
135
+ */
136
+ async clear(ids) {
137
+ return withStashMutationLock(this.filePath, async () => {
138
+ await this.reloadFresh();
139
+ const confirmed = ids === undefined ? undefined : new Set(ids);
140
+ const kept = confirmed === undefined ? [] : this.file.entries.filter(entry => !confirmed.has(entry.id));
141
+ const removed = this.file.entries.length - kept.length;
142
+ if (removed === 0)
143
+ return { didPersist: false, result: 0 };
144
+ const next = {
145
+ ...createEmptyStashFile(this.paths.sessionId, this.now()),
146
+ updatedAt: this.now(),
147
+ entries: kept,
148
+ };
149
+ return this.persistMutation(next, removed);
150
+ });
151
+ }
152
+ async removeAt(match) {
153
+ const index = this.file.entries.findIndex(entry => match(entry.id));
154
+ const found = index < 0 ? undefined : this.file.entries[index];
155
+ if (found === undefined)
156
+ return { didPersist: false, result: undefined };
157
+ const next = {
158
+ ...this.file,
159
+ updatedAt: this.now(),
160
+ entries: this.file.entries.filter((_, position) => position !== index),
161
+ };
162
+ return this.persistMutation(next, { entry: found, index });
163
+ }
164
+ async persistMutation(next, result) {
165
+ const outcome = await this.write(this.filePath, next);
166
+ this.file = next;
167
+ if (outcome?.committed === true) {
168
+ throw new StashCommittedError(result, { phase: outcome.phase, error: outcome.error });
169
+ }
170
+ return { didPersist: true, result };
171
+ }
172
+ async reloadFresh() {
173
+ const loaded = await readCurrentStashFile(this.filePath, this.paths.sessionId, this.now());
174
+ if (loaded.kind === 'ready') {
175
+ this.file = loaded.file;
176
+ return;
177
+ }
178
+ if (loaded.kind === 'unsupported')
179
+ throw new UnsupportedStashSchemaError(loaded.version);
180
+ // Corrupt input is quarantined and replaced in memory, so later writes
181
+ // cannot resurrect entries from the invalidated snapshot.
182
+ this.file = createEmptyStashFile(this.paths.sessionId, this.now());
183
+ this.corruptRecovery =
184
+ loaded.quarantinedTo === undefined
185
+ ? undefined
186
+ : { path: loaded.quarantinedTo, syncFailed: loaded.quarantineSyncFailed };
187
+ }
188
+ }
189
+ export async function loadStashStore(paths, now = Date.now, write = writeStashFile) {
190
+ const loaded = await withStashFileLock(paths.file, () => readCurrentStashFile(paths.file, paths.sessionId, now()));
191
+ if (loaded.kind === 'unsupported')
192
+ throw new UnsupportedStashSchemaError(loaded.version);
193
+ return new StashStore(paths, loaded, now, write);
194
+ }
195
+ async function readCurrentStashFile(filePath, sessionId, now) {
196
+ let source;
197
+ try {
198
+ source = await readPrivateTextFile(filePath, 'stash file', MAX_STASH_FILE_BYTES);
199
+ }
200
+ catch (error) {
201
+ // A missing bank is an empty one; an oversized or unreadable bank is a real
202
+ // failure the caller must report rather than silently start over from.
203
+ if (hasErrorCode(error, 'ENOENT'))
204
+ return { kind: 'ready', file: createEmptyStashFile(sessionId, now) };
205
+ throw error;
206
+ }
207
+ let raw;
208
+ try {
209
+ raw = JSON.parse(source.text);
210
+ }
211
+ catch {
212
+ return await quarantineCorrupt(filePath, source.identity);
213
+ }
214
+ const parsed = parseStashFile(raw);
215
+ if (parsed !== undefined && parsed.sessionId === sessionId)
216
+ return { kind: 'ready', file: parsed };
217
+ if (isRecord(raw) &&
218
+ typeof raw.version === 'number' &&
219
+ Number.isSafeInteger(raw.version) &&
220
+ raw.version > STASH_SCHEMA_VERSION) {
221
+ return { kind: 'unsupported', version: raw.version };
222
+ }
223
+ return await quarantineCorrupt(filePath, source.identity);
224
+ }
225
+ async function quarantineCorrupt(filePath, identity) {
226
+ const quarantined = await quarantinePrivateFile(filePath, identity, `corrupt-${Date.now()}`);
227
+ return { kind: 'corrupt', quarantinedTo: quarantined.path, quarantineSyncFailed: quarantined.syncError !== undefined };
228
+ }
229
+ export async function writeStashFile(filePath, file, syncDirectory = syncDirectoryEntry, replaceFile = rename) {
230
+ const directory = path.dirname(filePath);
231
+ await ensurePrivateDirectory(directory);
232
+ const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
233
+ const data = `${JSON.stringify(file, null, 2)}\n`;
234
+ // Every later read refuses a file past this cap, so a write that would cross
235
+ // it has to be refused here instead: the alternative is a bank that saves
236
+ // successfully, clears the editor, and can never be opened again.
237
+ const bytes = Buffer.byteLength(data);
238
+ if (bytes > MAX_STASH_FILE_BYTES)
239
+ throw new StashFileTooLargeError(bytes);
240
+ let tempCreated = false;
241
+ try {
242
+ await writePrivateFileExclusive(tempPath, data);
243
+ tempCreated = true;
244
+ await replaceFile(tempPath, filePath);
245
+ tempCreated = false;
246
+ }
247
+ catch (error) {
248
+ if (tempCreated)
249
+ await rm(tempPath, { force: true });
250
+ throw error;
251
+ }
252
+ // The rename is durable once the directory naming the file is flushed. The
253
+ // directories that name the storage itself were flushed when they were created,
254
+ // so this is the entry the save is actually responsible for.
255
+ try {
256
+ await syncDirectory(directory);
257
+ }
258
+ catch (error) {
259
+ return { committed: true, phase: 'directory-sync', error };
260
+ }
261
+ }
262
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/stash/store.ts"],"names":[],"mappings":"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"}
package/lib/stash.d.ts ADDED
@@ -0,0 +1,122 @@
1
+ import type { ResolvedEntry } from './stash/schema.ts';
2
+ import { type StashWriter } from './stash/store.ts';
3
+ /**
4
+ * Everything the stash needs from the surface.
5
+ *
6
+ * The surface owns the editor and the picker, so the stash asks for text and
7
+ * hands back text; it never reaches into a component of its own. `render` is
8
+ * called after every state change so the status count follows the bank, and
9
+ * `editorIsAvailable` is what tells a stash that the bar is the reader's rather
10
+ * than a question's: writing a draft into an answer would submit it as one.
11
+ */
12
+ export interface StashHost {
13
+ /** The draft as written, with a pasted block expanded back to its text. */
14
+ getEditorText(): string;
15
+ setEditorText(text: string): void;
16
+ /** Whether the prompt bar currently belongs to the reader. */
17
+ editorIsAvailable(): boolean;
18
+ notice(message: string): void;
19
+ /** Ask the reader which entry to take, by id; undefined when they leave. */
20
+ pick(entries: readonly ResolvedEntry[], sessionLabel: string): Promise<string | undefined>;
21
+ /** Ask the reader to confirm clearing `count` drafts. */
22
+ confirm(count: number): Promise<boolean>;
23
+ render(): void;
24
+ }
25
+ export interface PromptStashOptions {
26
+ /**
27
+ * The session the surface is on now.
28
+ *
29
+ * A command reads this once, when the reader asks for it, and holds that
30
+ * session for the whole operation: the surface can move on while a command
31
+ * waits in the queue, and a command that re-read the session afterwards would
32
+ * drop or clear the drafts of the session it moved to. `open` is the one
33
+ * caller that re-reads it, because following the surface is its whole job.
34
+ */
35
+ readonly sessionId: () => string;
36
+ /** Override the storage root; tests keep it inside a scratch directory. */
37
+ readonly baseDir?: string | undefined;
38
+ readonly now?: (() => number) | undefined;
39
+ readonly write?: StashWriter | undefined;
40
+ }
41
+ export declare class PromptStash {
42
+ private readonly host;
43
+ private readonly options;
44
+ private readonly baseDir;
45
+ private readonly now;
46
+ private readonly write;
47
+ private store;
48
+ /** The session `store` was read for, so a switch can be told from a re-read. */
49
+ private scope;
50
+ private count;
51
+ /** The queue a second command waits behind, so writes never interleave. */
52
+ private tail;
53
+ constructor(host: StashHost, options: PromptStashOptions);
54
+ /** How many drafts this session holds, as last read. */
55
+ get entryCount(): number;
56
+ /**
57
+ * Read the bank of the session in force, so the status count is real before a
58
+ * command and after the surface moves to another session. A read creates
59
+ * nothing on disk, so a session that never stashes leaves no file behind.
60
+ */
61
+ open(): Promise<void>;
62
+ /**
63
+ * Persist the draft the reader is looking at, or the one named on the command
64
+ * line.
65
+ *
66
+ * An argument is written into the editor before the write is attempted —
67
+ * before the bank is even opened — because the command line it came from is
68
+ * already gone: a refused write has to leave the draft where the reader can
69
+ * retry it instead of losing the only copy. A command that names nothing — the
70
+ * chord, or a bare `/stash` — parks whatever the bar is holding.
71
+ */
72
+ stashEditor(typed?: string): Promise<void>;
73
+ /** Put a draft in an empty editor without removing it. */
74
+ apply(selector: string | undefined): Promise<void>;
75
+ /** Put a draft in an empty editor and remove it from the bank. */
76
+ pop(selector: string | undefined): Promise<void>;
77
+ /** Open the list and pop whatever the reader takes. */
78
+ list(sessionLabel: string): Promise<void>;
79
+ /** Delete one draft without using it. */
80
+ drop(selector: string | undefined): Promise<void>;
81
+ /** Confirm, then delete every draft the confirmation named. */
82
+ clear(): Promise<void>;
83
+ /**
84
+ * Put one entry in an empty editor, then remove it.
85
+ *
86
+ * The editor is filled before the entry is removed, so a crash between the two
87
+ * leaves the draft in the bank rather than only in a terminal that is gone.
88
+ */
89
+ private popFrom;
90
+ private editorIsReady;
91
+ /**
92
+ * Whether the bar is the reader's to write in.
93
+ *
94
+ * A question borrows the bar and empties it, so an empty check alone would
95
+ * let a pop write the draft into an answer, and the draft's only other copy is
96
+ * deleted right afterwards.
97
+ */
98
+ private editorIsAvailable;
99
+ private missingMessage;
100
+ /**
101
+ * The store of one session, loaded on first use.
102
+ *
103
+ * The session is passed in rather than read here: a command must act on the
104
+ * session the reader was on when they asked for it, even when the surface
105
+ * moves on before the queue reaches that command.
106
+ */
107
+ private ensure;
108
+ private run;
109
+ /** Whether the surface still shows the session a command was issued in. */
110
+ private isCurrentSession;
111
+ /**
112
+ * Stop a command whose session left the screen before the bar could be reached.
113
+ *
114
+ * The bank half has already run for the session that asked for it; writing its
115
+ * draft into whatever bar is on screen now would hand it to the wrong reader.
116
+ */
117
+ private abandonIfMoved;
118
+ private enqueue;
119
+ /** Publish the bank's size and any quarantine the last read produced. */
120
+ private sync;
121
+ }
122
+ //# sourceMappingURL=stash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stash.d.ts","sourceRoot":"","sources":["../src/stash.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,kBAAkB,CAAA;AAuBzB;;;;;;;;GAQG;AACH,MAAM,WAAW,SAAS;IACxB,2EAA2E;IAC3E,aAAa,IAAI,MAAM,CAAA;IACvB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,8DAA8D;IAC9D,iBAAiB,IAAI,OAAO,CAAA;IAC5B,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,4EAA4E;IAC5E,IAAI,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IAC1F,yDAAyD;IACzD,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACxC,MAAM,IAAI,IAAI,CAAA;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;OAQG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,MAAM,CAAA;IAChC,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACrC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,MAAM,CAAC,GAAG,SAAS,CAAA;IACzC,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;CACzC;AASD,qBAAa,WAAW;IAYpB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAZ1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA4B;IAChD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyB;IAC/C,OAAO,CAAC,KAAK,CAAwB;IACrC,gFAAgF;IAChF,OAAO,CAAC,KAAK,CAAoB;IACjC,OAAO,CAAC,KAAK,CAAI;IACjB,2EAA2E;IAC3E,OAAO,CAAC,IAAI,CAAmC;gBAG5B,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,kBAAkB;IAO9C,wDAAwD;IACxD,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED;;;;OAIG;IACH,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAWrB;;;;;;;;;OASG;IACH,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0C1C,0DAA0D;IAC1D,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBlD,kEAAkE;IAClE,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAUhD,uDAAuD;IACvD,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBzC,yCAAyC;IACzC,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBjD,+DAA+D;IAC/D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA4BtB;;;;;OAKG;YACW,OAAO;IAqBrB,OAAO,CAAC,aAAa;IAOrB;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,cAAc;IAOtB;;;;;;OAMG;YACW,MAAM;IAQpB,OAAO,CAAC,GAAG;IAYX,2EAA2E;IAC3E,OAAO,CAAC,gBAAgB;IAIxB;;;;;OAKG;IACH,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,OAAO;IAMf,yEAAyE;IACzE,OAAO,CAAC,IAAI;CAab"}