@co0ontty/wand 1.6.2 → 1.9.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.
package/dist/storage.js CHANGED
@@ -12,6 +12,18 @@ function parseStoredMessages(raw) {
12
12
  return undefined;
13
13
  }
14
14
  }
15
+ function parseQueuedMessages(raw) {
16
+ if (!raw) {
17
+ return undefined;
18
+ }
19
+ try {
20
+ const parsed = JSON.parse(raw);
21
+ return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : undefined;
22
+ }
23
+ catch {
24
+ return undefined;
25
+ }
26
+ }
15
27
  function parseStructuredState(raw) {
16
28
  if (!raw) {
17
29
  return undefined;
@@ -23,6 +35,206 @@ function parseStructuredState(raw) {
23
35
  return undefined;
24
36
  }
25
37
  }
38
+ function inferSessionProvider(row) {
39
+ if (row.provider === "claude" || row.provider === "codex") {
40
+ return row.provider;
41
+ }
42
+ if (row.runner === "claude-cli" || row.runner === "claude-cli-print") {
43
+ return "claude";
44
+ }
45
+ return /^claude\b/.test(row.command.trim()) ? "claude" : undefined;
46
+ }
47
+ function parseWorktreeInfo(raw) {
48
+ if (!raw) {
49
+ return undefined;
50
+ }
51
+ try {
52
+ const parsed = JSON.parse(raw);
53
+ if (parsed
54
+ && typeof parsed === "object"
55
+ && typeof parsed.branch === "string"
56
+ && typeof parsed.path === "string") {
57
+ return parsed;
58
+ }
59
+ }
60
+ catch {
61
+ return undefined;
62
+ }
63
+ return undefined;
64
+ }
65
+ function parseWorktreeMergeInfo(raw) {
66
+ if (!raw) {
67
+ return undefined;
68
+ }
69
+ try {
70
+ const parsed = JSON.parse(raw);
71
+ if (parsed && typeof parsed === "object") {
72
+ return parsed;
73
+ }
74
+ }
75
+ catch {
76
+ return undefined;
77
+ }
78
+ return undefined;
79
+ }
80
+ function serializeWorktreeMergeInfo(info) {
81
+ return info ? JSON.stringify(info) : null;
82
+ }
83
+ function serializeWorktreeInfo(info) {
84
+ return info ? JSON.stringify(info) : null;
85
+ }
86
+ function normalizeWorktreeMergeStatus(raw) {
87
+ if (raw === "ready" || raw === "checking" || raw === "merging" || raw === "merged" || raw === "failed") {
88
+ return raw;
89
+ }
90
+ return undefined;
91
+ }
92
+ function getWorktreeMergeStatusValue(snapshot) {
93
+ return snapshot.worktreeMergeStatus ?? null;
94
+ }
95
+ function getWorktreeMergeInfoValue(snapshot) {
96
+ return serializeWorktreeMergeInfo(snapshot.worktreeMergeInfo);
97
+ }
98
+ function getWorktreeInfoValue(snapshot) {
99
+ return serializeWorktreeInfo(snapshot.worktree);
100
+ }
101
+ function mapWorktreeMergeFields(row) {
102
+ return {
103
+ worktreeMergeStatus: normalizeWorktreeMergeStatus(row.worktree_merge_status),
104
+ worktreeMergeInfo: parseWorktreeMergeInfo(row.worktree_merge_info) ?? null,
105
+ };
106
+ }
107
+ function sessionSelectFields() {
108
+ return `id, provider, session_kind, runner, command, cwd, mode, status, exit_code, started_at, ended_at, output, archived, archived_at, claude_session_id, messages, queued_messages, structured_state
109
+ , resumed_from_session_id, resumed_to_session_id, auto_recovered, worktree_enabled, worktree_info, worktree_merge_status, worktree_merge_info`;
110
+ }
111
+ function sessionPersistFields() {
112
+ return `id, command, cwd, mode, status, exit_code, started_at, ended_at, output
113
+ , archived, archived_at, claude_session_id, provider, session_kind, runner, messages, queued_messages, structured_state
114
+ , resumed_from_session_id, resumed_to_session_id, auto_recovered, worktree_enabled, worktree_info, worktree_merge_status, worktree_merge_info`;
115
+ }
116
+ function sessionPersistAssignments() {
117
+ return `command = excluded.command,
118
+ cwd = excluded.cwd,
119
+ mode = excluded.mode,
120
+ status = excluded.status,
121
+ exit_code = excluded.exit_code,
122
+ started_at = excluded.started_at,
123
+ ended_at = excluded.ended_at,
124
+ output = excluded.output,
125
+ archived = excluded.archived,
126
+ archived_at = excluded.archived_at,
127
+ claude_session_id = excluded.claude_session_id,
128
+ provider = excluded.provider,
129
+ session_kind = excluded.session_kind,
130
+ runner = excluded.runner,
131
+ messages = excluded.messages,
132
+ queued_messages = excluded.queued_messages,
133
+ structured_state = excluded.structured_state,
134
+ resumed_from_session_id = excluded.resumed_from_session_id,
135
+ resumed_to_session_id = excluded.resumed_to_session_id,
136
+ auto_recovered = excluded.auto_recovered,
137
+ worktree_enabled = excluded.worktree_enabled,
138
+ worktree_info = excluded.worktree_info,
139
+ worktree_merge_status = excluded.worktree_merge_status,
140
+ worktree_merge_info = excluded.worktree_merge_info`;
141
+ }
142
+ function sessionMetadataAssignments() {
143
+ return `command = ?, cwd = ?, mode = ?, status = ?, exit_code = ?,
144
+ started_at = ?, ended_at = ?, output = ?,
145
+ archived = ?, archived_at = ?, claude_session_id = ?,
146
+ provider = ?, session_kind = ?, runner = ?, structured_state = ?,
147
+ resumed_from_session_id = ?, resumed_to_session_id = ?, auto_recovered = ?,
148
+ worktree_enabled = ?, worktree_info = ?, worktree_merge_status = ?, worktree_merge_info = ?`;
149
+ }
150
+ function sessionPersistValues(snapshot) {
151
+ return [
152
+ snapshot.id,
153
+ snapshot.command,
154
+ snapshot.cwd,
155
+ snapshot.mode,
156
+ snapshot.status,
157
+ snapshot.exitCode,
158
+ snapshot.startedAt,
159
+ snapshot.endedAt,
160
+ snapshot.output,
161
+ snapshot.archived ? 1 : 0,
162
+ snapshot.archivedAt,
163
+ snapshot.claudeSessionId,
164
+ snapshot.provider ?? null,
165
+ snapshot.sessionKind ?? "pty",
166
+ snapshot.runner ?? null,
167
+ snapshot.messages ? JSON.stringify(snapshot.messages) : null,
168
+ snapshot.queuedMessages ? JSON.stringify(snapshot.queuedMessages) : null,
169
+ snapshot.structuredState ? JSON.stringify(snapshot.structuredState) : null,
170
+ snapshot.resumedFromSessionId ?? null,
171
+ snapshot.resumedToSessionId ?? null,
172
+ snapshot.autoRecovered ? 1 : 0,
173
+ snapshot.worktreeEnabled ? 1 : 0,
174
+ getWorktreeInfoValue(snapshot),
175
+ getWorktreeMergeStatusValue(snapshot),
176
+ getWorktreeMergeInfoValue(snapshot),
177
+ ];
178
+ }
179
+ function sessionMetadataValues(snapshot) {
180
+ return [
181
+ snapshot.command,
182
+ snapshot.cwd,
183
+ snapshot.mode,
184
+ snapshot.status,
185
+ snapshot.exitCode,
186
+ snapshot.startedAt,
187
+ snapshot.endedAt,
188
+ snapshot.output,
189
+ snapshot.archived ? 1 : 0,
190
+ snapshot.archivedAt,
191
+ snapshot.claudeSessionId,
192
+ snapshot.provider ?? null,
193
+ snapshot.sessionKind ?? "pty",
194
+ snapshot.runner ?? null,
195
+ snapshot.structuredState ? JSON.stringify(snapshot.structuredState) : null,
196
+ snapshot.resumedFromSessionId ?? null,
197
+ snapshot.resumedToSessionId ?? null,
198
+ snapshot.autoRecovered ? 1 : 0,
199
+ snapshot.worktreeEnabled ? 1 : 0,
200
+ getWorktreeInfoValue(snapshot),
201
+ getWorktreeMergeStatusValue(snapshot),
202
+ getWorktreeMergeInfoValue(snapshot),
203
+ snapshot.id,
204
+ ];
205
+ }
206
+ function mapSessionCore(row) {
207
+ const provider = inferSessionProvider(row);
208
+ return {
209
+ id: row.id,
210
+ sessionKind: row.session_kind ?? "pty",
211
+ provider,
212
+ runner: row.runner ?? undefined,
213
+ command: row.command,
214
+ cwd: row.cwd,
215
+ mode: row.mode,
216
+ status: row.status,
217
+ exitCode: row.exit_code,
218
+ startedAt: row.started_at,
219
+ endedAt: row.ended_at,
220
+ output: row.output,
221
+ archived: Boolean(row.archived),
222
+ archivedAt: row.archived_at,
223
+ claudeSessionId: row.claude_session_id,
224
+ messages: parseStoredMessages(row.messages),
225
+ queuedMessages: parseQueuedMessages(row.queued_messages),
226
+ structuredState: parseStructuredState(row.structured_state),
227
+ resumedFromSessionId: row.resumed_from_session_id ?? undefined,
228
+ resumedToSessionId: row.resumed_to_session_id ?? undefined,
229
+ autoRecovered: Boolean(row.auto_recovered),
230
+ worktreeEnabled: Boolean(row.worktree_enabled),
231
+ worktree: parseWorktreeInfo(row.worktree_info) ?? null,
232
+ ...mapWorktreeMergeFields(row),
233
+ };
234
+ }
235
+ function sessionRowQuery(base) {
236
+ return `${base} ${sessionSelectFields()}`;
237
+ }
26
238
  export const DEFAULT_DB_FILE = "wand.db";
27
239
  export function resolveDatabasePath(configPath) {
28
240
  return path.resolve(path.dirname(configPath), DEFAULT_DB_FILE);
@@ -46,13 +258,19 @@ const INIT_SQL = `
46
258
  archived INTEGER NOT NULL DEFAULT 0,
47
259
  archived_at TEXT,
48
260
  claude_session_id TEXT,
261
+ provider TEXT,
49
262
  session_kind TEXT NOT NULL DEFAULT 'pty',
50
263
  runner TEXT,
51
264
  messages TEXT,
265
+ queued_messages TEXT,
52
266
  structured_state TEXT,
53
267
  resumed_from_session_id TEXT,
54
268
  resumed_to_session_id TEXT,
55
- auto_recovered INTEGER NOT NULL DEFAULT 0
269
+ auto_recovered INTEGER NOT NULL DEFAULT 0,
270
+ worktree_enabled INTEGER NOT NULL DEFAULT 0,
271
+ worktree_info TEXT,
272
+ worktree_merge_status TEXT,
273
+ worktree_merge_info TEXT
56
274
  );
57
275
 
58
276
  CREATE TABLE IF NOT EXISTS app_config (
@@ -142,30 +360,11 @@ export class WandStorage {
142
360
  try {
143
361
  this.db
144
362
  .prepare(`INSERT INTO command_sessions (
145
- id, command, cwd, mode, status, exit_code, started_at, ended_at, output
146
- , archived, archived_at, claude_session_id, session_kind, runner, messages, structured_state
147
- , resumed_from_session_id, resumed_to_session_id, auto_recovered
148
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
363
+ ${sessionPersistFields()}
364
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
149
365
  ON CONFLICT(id) DO UPDATE SET
150
- command = excluded.command,
151
- cwd = excluded.cwd,
152
- mode = excluded.mode,
153
- status = excluded.status,
154
- exit_code = excluded.exit_code,
155
- started_at = excluded.started_at,
156
- ended_at = excluded.ended_at,
157
- output = excluded.output,
158
- archived = excluded.archived,
159
- archived_at = excluded.archived_at,
160
- claude_session_id = excluded.claude_session_id,
161
- session_kind = excluded.session_kind,
162
- runner = excluded.runner,
163
- messages = excluded.messages,
164
- structured_state = excluded.structured_state,
165
- resumed_from_session_id = excluded.resumed_from_session_id,
166
- resumed_to_session_id = excluded.resumed_to_session_id,
167
- auto_recovered = excluded.auto_recovered`)
168
- .run(snapshot.id, snapshot.command, snapshot.cwd, snapshot.mode, snapshot.status, snapshot.exitCode, snapshot.startedAt, snapshot.endedAt, snapshot.output, snapshot.archived ? 1 : 0, snapshot.archivedAt, snapshot.claudeSessionId, snapshot.sessionKind ?? "pty", snapshot.runner ?? null, snapshot.messages ? JSON.stringify(snapshot.messages) : null, snapshot.structuredState ? JSON.stringify(snapshot.structuredState) : null, snapshot.resumedFromSessionId ?? null, snapshot.resumedToSessionId ?? null, snapshot.autoRecovered ? 1 : 0);
366
+ ${sessionPersistAssignments()}`)
367
+ .run(...sessionPersistValues(snapshot));
169
368
  this.db.exec("COMMIT");
170
369
  }
171
370
  catch (error) {
@@ -181,18 +380,13 @@ export class WandStorage {
181
380
  saveSessionMetadata(snapshot) {
182
381
  this.db
183
382
  .prepare(`UPDATE command_sessions SET
184
- command = ?, cwd = ?, mode = ?, status = ?, exit_code = ?,
185
- started_at = ?, ended_at = ?, output = ?,
186
- archived = ?, archived_at = ?, claude_session_id = ?,
187
- session_kind = ?, runner = ?, structured_state = ?,
188
- resumed_from_session_id = ?, resumed_to_session_id = ?, auto_recovered = ?
383
+ ${sessionMetadataAssignments()}
189
384
  WHERE id = ?`)
190
- .run(snapshot.command, snapshot.cwd, snapshot.mode, snapshot.status, snapshot.exitCode, snapshot.startedAt, snapshot.endedAt, snapshot.output, snapshot.archived ? 1 : 0, snapshot.archivedAt, snapshot.claudeSessionId, snapshot.sessionKind ?? "pty", snapshot.runner ?? null, snapshot.structuredState ? JSON.stringify(snapshot.structuredState) : null, snapshot.resumedFromSessionId ?? null, snapshot.resumedToSessionId ?? null, snapshot.autoRecovered ? 1 : 0, snapshot.id);
385
+ .run(...sessionMetadataValues(snapshot));
191
386
  }
192
387
  getSession(id) {
193
388
  const row = this.db
194
- .prepare(`SELECT id, session_kind, runner, command, cwd, mode, status, exit_code, started_at, ended_at, output, archived, archived_at, claude_session_id, messages, structured_state
195
- , resumed_from_session_id, resumed_to_session_id, auto_recovered
389
+ .prepare(`${sessionRowQuery("SELECT")}
196
390
  FROM command_sessions
197
391
  WHERE id = ?`)
198
392
  .get(id);
@@ -200,8 +394,7 @@ export class WandStorage {
200
394
  }
201
395
  getLatestSessionByClaudeSessionId(claudeSessionId) {
202
396
  const row = this.db
203
- .prepare(`SELECT id, session_kind, runner, command, cwd, mode, status, exit_code, started_at, ended_at, output, archived, archived_at, claude_session_id, messages, structured_state
204
- , resumed_from_session_id, resumed_to_session_id, auto_recovered
397
+ .prepare(`${sessionRowQuery("SELECT")}
205
398
  FROM command_sessions
206
399
  WHERE claude_session_id = ?
207
400
  ORDER BY started_at DESC
@@ -211,35 +404,27 @@ export class WandStorage {
211
404
  }
212
405
  loadSessions() {
213
406
  const rows = this.db
214
- .prepare(`SELECT id, session_kind, runner, command, cwd, mode, status, exit_code, started_at, ended_at, output, archived, archived_at, claude_session_id, messages, structured_state
215
- , resumed_from_session_id, resumed_to_session_id, auto_recovered
407
+ .prepare(`${sessionRowQuery("SELECT")}
216
408
  FROM command_sessions
217
409
  ORDER BY started_at DESC`)
218
410
  .all();
219
411
  return rows.map((row) => this.mapSessionRow(row));
220
412
  }
221
413
  mapSessionRow(row) {
222
- return {
223
- id: row.id,
224
- sessionKind: row.session_kind ?? "pty",
225
- runner: row.runner ?? undefined,
226
- command: row.command,
227
- cwd: row.cwd,
228
- mode: row.mode,
229
- status: row.status,
230
- exitCode: row.exit_code,
231
- startedAt: row.started_at,
232
- endedAt: row.ended_at,
233
- output: row.output,
234
- archived: Boolean(row.archived),
235
- archivedAt: row.archived_at,
236
- claudeSessionId: row.claude_session_id,
237
- messages: parseStoredMessages(row.messages),
238
- structuredState: parseStructuredState(row.structured_state),
239
- resumedFromSessionId: row.resumed_from_session_id ?? undefined,
240
- resumedToSessionId: row.resumed_to_session_id ?? undefined,
241
- autoRecovered: Boolean(row.auto_recovered)
414
+ return mapSessionCore(row);
415
+ }
416
+ updateSessionWorktreeMergeState(id, status, info) {
417
+ const current = this.getSession(id);
418
+ if (!current) {
419
+ return null;
420
+ }
421
+ const updated = {
422
+ ...current,
423
+ worktreeMergeStatus: status,
424
+ worktreeMergeInfo: info,
242
425
  };
426
+ this.saveSessionMetadata(updated);
427
+ return updated;
243
428
  }
244
429
  deleteSession(id) {
245
430
  this.db.prepare("DELETE FROM command_sessions WHERE id = ?").run(id);
@@ -257,6 +442,9 @@ function ensureCommandSessionSchema(db) {
257
442
  if (!names.has("claude_session_id")) {
258
443
  db.exec("ALTER TABLE command_sessions ADD COLUMN claude_session_id TEXT");
259
444
  }
445
+ if (!names.has("provider")) {
446
+ db.exec("ALTER TABLE command_sessions ADD COLUMN provider TEXT");
447
+ }
260
448
  if (!names.has("session_kind")) {
261
449
  db.exec("ALTER TABLE command_sessions ADD COLUMN session_kind TEXT NOT NULL DEFAULT 'pty'");
262
450
  }
@@ -266,6 +454,9 @@ function ensureCommandSessionSchema(db) {
266
454
  if (!names.has("messages")) {
267
455
  db.exec("ALTER TABLE command_sessions ADD COLUMN messages TEXT");
268
456
  }
457
+ if (!names.has("queued_messages")) {
458
+ db.exec("ALTER TABLE command_sessions ADD COLUMN queued_messages TEXT");
459
+ }
269
460
  if (!names.has("structured_state")) {
270
461
  db.exec("ALTER TABLE command_sessions ADD COLUMN structured_state TEXT");
271
462
  }
@@ -275,7 +466,16 @@ function ensureCommandSessionSchema(db) {
275
466
  if (!names.has("resumed_to_session_id")) {
276
467
  db.exec("ALTER TABLE command_sessions ADD COLUMN resumed_to_session_id TEXT");
277
468
  }
278
- if (!names.has("auto_recovered")) {
279
- db.exec("ALTER TABLE command_sessions ADD COLUMN auto_recovered INTEGER NOT NULL DEFAULT 0");
469
+ if (!names.has("worktree_enabled")) {
470
+ db.exec("ALTER TABLE command_sessions ADD COLUMN worktree_enabled INTEGER NOT NULL DEFAULT 0");
471
+ }
472
+ if (!names.has("worktree_info")) {
473
+ db.exec("ALTER TABLE command_sessions ADD COLUMN worktree_info TEXT");
474
+ }
475
+ if (!names.has("worktree_merge_status")) {
476
+ db.exec("ALTER TABLE command_sessions ADD COLUMN worktree_merge_status TEXT");
477
+ }
478
+ if (!names.has("worktree_merge_info")) {
479
+ db.exec("ALTER TABLE command_sessions ADD COLUMN worktree_merge_info TEXT");
280
480
  }
281
481
  }
@@ -5,6 +5,7 @@ interface CreateStructuredSessionOptions {
5
5
  mode: ExecutionMode;
6
6
  prompt?: string;
7
7
  runner?: SessionRunner;
8
+ worktreeEnabled?: boolean;
8
9
  }
9
10
  export declare class StructuredSessionManager {
10
11
  private readonly storage;
@@ -29,6 +30,10 @@ export declare class StructuredSessionManager {
29
30
  stop(id: string): SessionSnapshot;
30
31
  delete(id: string): void;
31
32
  private requireSession;
33
+ private buildQueuedPlaceholderTurns;
34
+ private buildRenderableMessages;
35
+ private emitStructuredSnapshot;
36
+ private flushNextQueuedMessage;
32
37
  private emit;
33
38
  private resolvePermission;
34
39
  private incrementApprovalStats;