@co0ontty/wand 1.7.0 → 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
@@ -62,6 +62,179 @@ function parseWorktreeInfo(raw) {
62
62
  }
63
63
  return undefined;
64
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
+ }
65
238
  export const DEFAULT_DB_FILE = "wand.db";
66
239
  export function resolveDatabasePath(configPath) {
67
240
  return path.resolve(path.dirname(configPath), DEFAULT_DB_FILE);
@@ -95,7 +268,9 @@ const INIT_SQL = `
95
268
  resumed_to_session_id TEXT,
96
269
  auto_recovered INTEGER NOT NULL DEFAULT 0,
97
270
  worktree_enabled INTEGER NOT NULL DEFAULT 0,
98
- worktree_info TEXT
271
+ worktree_info TEXT,
272
+ worktree_merge_status TEXT,
273
+ worktree_merge_info TEXT
99
274
  );
100
275
 
101
276
  CREATE TABLE IF NOT EXISTS app_config (
@@ -185,34 +360,11 @@ export class WandStorage {
185
360
  try {
186
361
  this.db
187
362
  .prepare(`INSERT INTO command_sessions (
188
- id, command, cwd, mode, status, exit_code, started_at, ended_at, output
189
- , archived, archived_at, claude_session_id, provider, session_kind, runner, messages, queued_messages, structured_state
190
- , resumed_from_session_id, resumed_to_session_id, auto_recovered, worktree_enabled, worktree_info
191
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
363
+ ${sessionPersistFields()}
364
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
192
365
  ON CONFLICT(id) DO UPDATE SET
193
- command = excluded.command,
194
- cwd = excluded.cwd,
195
- mode = excluded.mode,
196
- status = excluded.status,
197
- exit_code = excluded.exit_code,
198
- started_at = excluded.started_at,
199
- ended_at = excluded.ended_at,
200
- output = excluded.output,
201
- archived = excluded.archived,
202
- archived_at = excluded.archived_at,
203
- claude_session_id = excluded.claude_session_id,
204
- provider = excluded.provider,
205
- session_kind = excluded.session_kind,
206
- runner = excluded.runner,
207
- messages = excluded.messages,
208
- queued_messages = excluded.queued_messages,
209
- structured_state = excluded.structured_state,
210
- resumed_from_session_id = excluded.resumed_from_session_id,
211
- resumed_to_session_id = excluded.resumed_to_session_id,
212
- auto_recovered = excluded.auto_recovered,
213
- worktree_enabled = excluded.worktree_enabled,
214
- worktree_info = excluded.worktree_info`)
215
- .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.provider ?? null, snapshot.sessionKind ?? "pty", snapshot.runner ?? null, snapshot.messages ? JSON.stringify(snapshot.messages) : null, snapshot.queuedMessages ? JSON.stringify(snapshot.queuedMessages) : null, snapshot.structuredState ? JSON.stringify(snapshot.structuredState) : null, snapshot.resumedFromSessionId ?? null, snapshot.resumedToSessionId ?? null, snapshot.autoRecovered ? 1 : 0, snapshot.worktreeEnabled ? 1 : 0, snapshot.worktree ? JSON.stringify(snapshot.worktree) : null);
366
+ ${sessionPersistAssignments()}`)
367
+ .run(...sessionPersistValues(snapshot));
216
368
  this.db.exec("COMMIT");
217
369
  }
218
370
  catch (error) {
@@ -228,19 +380,13 @@ export class WandStorage {
228
380
  saveSessionMetadata(snapshot) {
229
381
  this.db
230
382
  .prepare(`UPDATE command_sessions SET
231
- command = ?, cwd = ?, mode = ?, status = ?, exit_code = ?,
232
- started_at = ?, ended_at = ?, output = ?,
233
- archived = ?, archived_at = ?, claude_session_id = ?,
234
- provider = ?, session_kind = ?, runner = ?, structured_state = ?,
235
- resumed_from_session_id = ?, resumed_to_session_id = ?, auto_recovered = ?,
236
- worktree_enabled = ?, worktree_info = ?
383
+ ${sessionMetadataAssignments()}
237
384
  WHERE id = ?`)
238
- .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.provider ?? null, snapshot.sessionKind ?? "pty", snapshot.runner ?? null, snapshot.structuredState ? JSON.stringify(snapshot.structuredState) : null, snapshot.resumedFromSessionId ?? null, snapshot.resumedToSessionId ?? null, snapshot.autoRecovered ? 1 : 0, snapshot.worktreeEnabled ? 1 : 0, snapshot.worktree ? JSON.stringify(snapshot.worktree) : null, snapshot.id);
385
+ .run(...sessionMetadataValues(snapshot));
239
386
  }
240
387
  getSession(id) {
241
388
  const row = this.db
242
- .prepare(`SELECT 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
243
- , resumed_from_session_id, resumed_to_session_id, auto_recovered, worktree_enabled, worktree_info
389
+ .prepare(`${sessionRowQuery("SELECT")}
244
390
  FROM command_sessions
245
391
  WHERE id = ?`)
246
392
  .get(id);
@@ -248,8 +394,7 @@ export class WandStorage {
248
394
  }
249
395
  getLatestSessionByClaudeSessionId(claudeSessionId) {
250
396
  const row = this.db
251
- .prepare(`SELECT 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
252
- , resumed_from_session_id, resumed_to_session_id, auto_recovered, worktree_enabled, worktree_info
397
+ .prepare(`${sessionRowQuery("SELECT")}
253
398
  FROM command_sessions
254
399
  WHERE claude_session_id = ?
255
400
  ORDER BY started_at DESC
@@ -259,40 +404,27 @@ export class WandStorage {
259
404
  }
260
405
  loadSessions() {
261
406
  const rows = this.db
262
- .prepare(`SELECT 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
263
- , resumed_from_session_id, resumed_to_session_id, auto_recovered, worktree_enabled, worktree_info
407
+ .prepare(`${sessionRowQuery("SELECT")}
264
408
  FROM command_sessions
265
409
  ORDER BY started_at DESC`)
266
410
  .all();
267
411
  return rows.map((row) => this.mapSessionRow(row));
268
412
  }
269
413
  mapSessionRow(row) {
270
- const provider = inferSessionProvider(row);
271
- return {
272
- id: row.id,
273
- sessionKind: row.session_kind ?? "pty",
274
- provider,
275
- runner: row.runner ?? undefined,
276
- command: row.command,
277
- cwd: row.cwd,
278
- mode: row.mode,
279
- status: row.status,
280
- exitCode: row.exit_code,
281
- startedAt: row.started_at,
282
- endedAt: row.ended_at,
283
- output: row.output,
284
- archived: Boolean(row.archived),
285
- archivedAt: row.archived_at,
286
- claudeSessionId: row.claude_session_id,
287
- messages: parseStoredMessages(row.messages),
288
- queuedMessages: parseQueuedMessages(row.queued_messages),
289
- structuredState: parseStructuredState(row.structured_state),
290
- resumedFromSessionId: row.resumed_from_session_id ?? undefined,
291
- resumedToSessionId: row.resumed_to_session_id ?? undefined,
292
- autoRecovered: Boolean(row.auto_recovered),
293
- worktreeEnabled: Boolean(row.worktree_enabled),
294
- worktree: parseWorktreeInfo(row.worktree_info) ?? null
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,
295
425
  };
426
+ this.saveSessionMetadata(updated);
427
+ return updated;
296
428
  }
297
429
  deleteSession(id) {
298
430
  this.db.prepare("DELETE FROM command_sessions WHERE id = ?").run(id);
@@ -340,4 +472,10 @@ function ensureCommandSessionSchema(db) {
340
472
  if (!names.has("worktree_info")) {
341
473
  db.exec("ALTER TABLE command_sessions ADD COLUMN worktree_info TEXT");
342
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");
480
+ }
343
481
  }
package/dist/types.d.ts CHANGED
@@ -47,6 +47,21 @@ export interface StructuredChatPersonaConfig {
47
47
  user?: StructuredChatPersonaRoleConfig;
48
48
  assistant?: StructuredChatPersonaRoleConfig;
49
49
  }
50
+ export interface DefaultPanelStateConfig {
51
+ sessionsDrawerOpen?: boolean;
52
+ filePanelOpen?: boolean;
53
+ shortcutsExpanded?: boolean;
54
+ claudeHistoryExpanded?: boolean;
55
+ chatMessageExpanded?: boolean;
56
+ structuredThinkingExpanded?: boolean;
57
+ structuredToolGroupExpanded?: boolean;
58
+ structuredInlineToolExpanded?: boolean;
59
+ structuredTerminalExpanded?: boolean;
60
+ structuredToolCardExpanded?: boolean;
61
+ }
62
+ export interface UiPreferencesConfig {
63
+ defaultPanelState?: DefaultPanelStateConfig;
64
+ }
50
65
  export interface WandConfig {
51
66
  host: string;
52
67
  port: number;
@@ -64,11 +79,45 @@ export interface WandConfig {
64
79
  shortcutLogMaxBytes?: number;
65
80
  /** Preferred response language for Claude (e.g. "中文", "English"). Empty string means no override. */
66
81
  language?: string;
82
+ /** UI defaults used for initializing panel open/expanded state. */
83
+ uiPreferences?: UiPreferencesConfig;
67
84
  }
68
85
  interface WorktreeInfo {
69
86
  branch: string;
70
87
  path: string;
71
88
  }
89
+ export interface WorktreeMergeInfo {
90
+ targetBranch?: string;
91
+ mergedAt?: string;
92
+ mergeCommit?: string;
93
+ cleanupDone?: boolean;
94
+ lastError?: string;
95
+ conflict?: boolean;
96
+ }
97
+ export interface WorktreeMergeCheckResult {
98
+ ok: boolean;
99
+ sourceBranch: string;
100
+ targetBranch: string;
101
+ worktreePath: string;
102
+ repoRoot: string;
103
+ hasUncommittedChanges: boolean;
104
+ aheadCount: number;
105
+ hasConflicts: boolean;
106
+ recommendedAction: "merge" | "noop" | "resolve-conflict";
107
+ reason?: string;
108
+ }
109
+ export interface WorktreeMergeResult {
110
+ ok: boolean;
111
+ sourceBranch: string;
112
+ targetBranch: string;
113
+ repoRoot: string;
114
+ mergeCommit?: string;
115
+ mergedAt?: string;
116
+ cleanupDone: boolean;
117
+ conflict: boolean;
118
+ errorCode?: string;
119
+ reason?: string;
120
+ }
72
121
  export interface CommandRequest {
73
122
  command: string;
74
123
  provider?: SessionProvider;
@@ -167,6 +216,8 @@ export interface SessionSnapshot {
167
216
  mode: ExecutionMode;
168
217
  worktreeEnabled?: boolean;
169
218
  worktree?: WorktreeInfo | null;
219
+ worktreeMergeStatus?: "ready" | "checking" | "merging" | "merged" | "failed";
220
+ worktreeMergeInfo?: WorktreeMergeInfo | null;
170
221
  autonomyPolicy?: AutonomyPolicy;
171
222
  approvalPolicy?: ApprovalPolicy;
172
223
  allowedScopes?: EscalationScope[];