claude-code-tools 1.0.6__py3-none-any.whl → 1.4.6__py3-none-any.whl

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 (33) hide show
  1. claude_code_tools/__init__.py +1 -1
  2. claude_code_tools/action_rpc.py +16 -10
  3. claude_code_tools/aichat.py +793 -51
  4. claude_code_tools/claude_continue.py +4 -0
  5. claude_code_tools/codex_continue.py +48 -0
  6. claude_code_tools/export_session.py +94 -11
  7. claude_code_tools/find_claude_session.py +36 -12
  8. claude_code_tools/find_codex_session.py +33 -18
  9. claude_code_tools/find_session.py +30 -16
  10. claude_code_tools/gdoc2md.py +220 -0
  11. claude_code_tools/md2gdoc.py +549 -0
  12. claude_code_tools/search_index.py +119 -15
  13. claude_code_tools/session_menu_cli.py +1 -1
  14. claude_code_tools/session_utils.py +3 -3
  15. claude_code_tools/smart_trim.py +18 -8
  16. claude_code_tools/smart_trim_core.py +4 -2
  17. claude_code_tools/tmux_cli_controller.py +35 -25
  18. claude_code_tools/trim_session.py +28 -2
  19. claude_code_tools-1.4.6.dist-info/METADATA +1112 -0
  20. {claude_code_tools-1.0.6.dist-info → claude_code_tools-1.4.6.dist-info}/RECORD +31 -24
  21. {claude_code_tools-1.0.6.dist-info → claude_code_tools-1.4.6.dist-info}/entry_points.txt +2 -0
  22. docs/linked-in-20260102.md +32 -0
  23. docs/local-llm-setup.md +286 -0
  24. docs/reddit-aichat-resume-v2.md +80 -0
  25. docs/reddit-aichat-resume.md +29 -0
  26. docs/reddit-aichat.md +79 -0
  27. docs/rollover-details.md +67 -0
  28. node_ui/action_config.js +3 -3
  29. node_ui/menu.js +67 -113
  30. claude_code_tools/session_tui.py +0 -516
  31. claude_code_tools-1.0.6.dist-info/METADATA +0 -685
  32. {claude_code_tools-1.0.6.dist-info → claude_code_tools-1.4.6.dist-info}/WHEEL +0 -0
  33. {claude_code_tools-1.0.6.dist-info → claude_code_tools-1.4.6.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,67 @@
1
+ # How Rollover Works
2
+
3
+ ```
4
+ ┌─────────────────────────────────────────────────────────────────────────────┐
5
+ │ ROLLOVER: Continue work in fresh context while preserving full history │
6
+ └─────────────────────────────────────────────────────────────────────────────┘
7
+
8
+ ┌─────────────────────────┐
9
+ │ aichat resume │ ◄── User triggers rollover of session ghi789
10
+ │ or aichat search │
11
+ └───────────┬─────────────┘
12
+
13
+
14
+ ┌──────────────────────────────────────────────────────────────────────┐
15
+ │ 1. TRACE LINEAGE of ghi789 │
16
+ │ Follow continue_metadata.parent_session_file pointers backwards │
17
+ │ │
18
+ │ ghi789.jsonl ──► def456.jsonl ──► abc123.jsonl (original) │
19
+ └──────────────────────────────────────────────────────────────────────┘
20
+
21
+
22
+ ┌──────────────────────────────────────────────────────────────────────┐
23
+ │ 2. BUILD PROMPT │
24
+ │ • Chronological list of all ancestor session files │
25
+ │ • Instructions to extract context │
26
+ │ • Optional: summary of work extracted by another agent │
27
+ └──────────────────────────────────────────────────────────────────────┘
28
+
29
+
30
+ ┌──────────────────────────────────────────────────────────────────────┐
31
+ │ 3. CREATE NEW INTERACTIVE SESSION (jkl012) │
32
+ │ │
33
+ │ • Work summary already present in prompt, OR │
34
+ │ • User can ask agent to recover specific parts of prior work │
35
+ │ (using session-search skill or session-searcher sub-agent) │
36
+ └──────────────────────────────────────────────────────────────────────┘
37
+
38
+
39
+ ┌──────────────────────────────────────────────────────────────────────┐
40
+ │ 4. INJECT METADATA (first line of jkl012.jsonl) │
41
+ │ │
42
+ │ { │
43
+ │ "continue_metadata": { │
44
+ │ "parent_session_file": "/path/to/ghi789.jsonl", │
45
+ │ "parent_session_id": "ghi789-...", │
46
+ │ "continued_at": "2025-12-19T..." │
47
+ │ } │
48
+ │ } │
49
+ └──────────────────────────────────────────────────────────────────────┘
50
+
51
+
52
+ ┌──────────────────────────────────────────────────────────────────────┐
53
+ │ 5. RESUME INTERACTIVELY │
54
+ │ claude --resume jkl012 ◄── Fresh context, full history access │
55
+ └──────────────────────────────────────────────────────────────────────┘
56
+
57
+
58
+ RESULT: Linked chain with on-demand context retrieval
59
+ ═══════════════════════════════════════════════════════
60
+
61
+ abc123.jsonl ◄─── def456.jsonl ◄─── ghi789.jsonl ◄─── jkl012.jsonl
62
+ (original) (trimmed) (rollover) (NEW SESSION)
63
+ │ │ │ │
64
+ └─────────────────┴─────────────────┴──────────────────┘
65
+ Agent can read any ancestor on demand
66
+ (using session-search skill or session-searcher sub-agent)
67
+ ```
node_ui/action_config.js CHANGED
@@ -53,14 +53,14 @@ export const RESUME_SUBMENU = [
53
53
  {value: 'resume', label: 'Resume as-is'},
54
54
  {value: 'clone', label: 'Clone session and resume clone'},
55
55
  {value: 'suppress_resume', label: 'Trim + resume...'},
56
- {value: 'smart_trim_resume', label: 'Smart trim + resume'},
57
- {value: 'continue', label: 'Rollover: handoff work to fresh session'},
56
+ {value: 'smart_trim_resume', label: 'Smart trim + resume...'},
57
+ {value: 'continue', label: 'Rollover: handoff work to fresh session...'},
58
58
  ];
59
59
 
60
60
  // Trim-only submenu (for aichat trim command)
61
61
  export const TRIM_SUBMENU = [
62
62
  {value: 'suppress_resume', label: 'Trim + resume...'},
63
- {value: 'smart_trim_resume', label: 'Smart trim + resume'},
63
+ {value: 'smart_trim_resume', label: 'Smart trim + resume...'},
64
64
  ];
65
65
 
66
66
  export function filteredActions(isSidechain = false) {
node_ui/menu.js CHANGED
@@ -115,8 +115,46 @@ const colorize = {
115
115
  branch: (txt) => chalk.cyan(txt),
116
116
  lines: (txt) => chalk.yellow(txt),
117
117
  date: (txt) => chalk.blue(txt),
118
+ title: (txt) => chalk.cyan.bold(txt),
118
119
  };
119
120
 
121
+ /**
122
+ * Format session header line with optional custom title.
123
+ * Returns array of spans for use in h(Text, null, ...spans).
124
+ *
125
+ * @param {Object} session - Session object with agent_display, session_id, lines, etc.
126
+ * @param {string} [idOverride] - Optional pre-computed id string (8 chars)
127
+ * @param {string} [annoOverride] - Optional pre-computed annotation string
128
+ * @param {string} [dateOverride] - Optional pre-computed date string
129
+ * @returns {Array} Array of chalk-styled strings/spans
130
+ */
131
+ function formatSessionHeader(session, idOverride, annoOverride, dateOverride) {
132
+ const id = idOverride || (session.session_id || '').slice(0, 8);
133
+ const anno = annoOverride !== undefined ? annoOverride : toAnno(session);
134
+ const date = dateOverride || formatDateRange(session.create_time, session.mod_time);
135
+ const customTitle = session.custom_title;
136
+
137
+ const spans = [
138
+ colorize.agent(`[${session.agent_display || 'CLAUDE'}]`), ' ',
139
+ ];
140
+
141
+ // Add custom title if present
142
+ if (customTitle) {
143
+ spans.push(colorize.title(`[${customTitle}]`), ' ');
144
+ }
145
+
146
+ spans.push(
147
+ chalk.white(id),
148
+ anno ? ` ${chalk.dim(anno)}` : '',
149
+ ' | ',
150
+ colorize.lines(formatLines(session.lines)),
151
+ ' | ',
152
+ colorize.date(date)
153
+ );
154
+
155
+ return spans;
156
+ }
157
+
120
158
  // Non-TTY fallback: pick first session and resume
121
159
  if (!process.stdout.isTTY) {
122
160
  if (sessions.length) {
@@ -606,9 +644,6 @@ function ConfirmView({session, actionLabel, onConfirm, onBack}) {
606
644
  if (key.escape) return onBack();
607
645
  if (key.return) return onConfirm();
608
646
  });
609
- const id = (session.session_id || '').slice(0, 8);
610
- const anno = toAnno(session);
611
- const date = formatDateRange(session.create_time, session.mod_time);
612
647
  const branchDisplay = session.branch ? `${BRANCH_ICON} ${session.branch}` : '';
613
648
 
614
649
  return h(
@@ -619,14 +654,7 @@ function ConfirmView({session, actionLabel, onConfirm, onBack}) {
619
654
  colorize.project(session.project || ''), ' ',
620
655
  colorize.branch(branchDisplay)
621
656
  ),
622
- h(
623
- Text,
624
- null,
625
- colorize.agent(`[${session.agent_display || 'CLAUDE'}]`), ' ',
626
- chalk.white(id), anno ? ` ${chalk.dim(anno)}` : '', ' | ',
627
- colorize.lines(formatLines(session.lines)), ' | ',
628
- colorize.date(date)
629
- ),
657
+ h(Text, null, ...formatSessionHeader(session)),
630
658
  h(Box, {marginBottom: 1}, renderPreview(session.preview) || null),
631
659
  h(Text, {dimColor: true}, 'Enter: run action & exit Esc: back')
632
660
  );
@@ -663,9 +691,7 @@ function ActionView({session, onBack, onDone, clearScreen}) {
663
691
  }
664
692
  });
665
693
 
666
- const id = session.session_id || ''; // Show full session ID in actions view
667
- const anno = toAnno(session);
668
- const date = formatDateRange(session.create_time, session.mod_time);
694
+ const fullId = session.session_id || ''; // Show full session ID in actions view
669
695
  const branchDisplay = session.branch ? `${BRANCH_ICON} ${session.branch}` : '';
670
696
 
671
697
  return h(
@@ -680,14 +706,7 @@ function ActionView({session, onBack, onDone, clearScreen}) {
680
706
  colorize.project(session.project || ''), ' ',
681
707
  colorize.branch(branchDisplay)
682
708
  ),
683
- h(
684
- Text,
685
- null,
686
- colorize.agent(`[${session.agent_display || 'CLAUDE'}]`), ' ',
687
- chalk.white(id), anno ? ` ${chalk.dim(anno)}` : '', ' | ',
688
- colorize.lines(formatLines(session.lines)), ' | ',
689
- colorize.date(date)
690
- ),
709
+ h(Text, null, ...formatSessionHeader(session, fullId)),
691
710
  renderPreview(session.preview),
692
711
  // Warning for sub-agent sessions
693
712
  session.is_sidechain ? h(
@@ -743,9 +762,6 @@ function ResumeView({onBack, onDone, session, clearScreen}) {
743
762
  }
744
763
  });
745
764
 
746
- const id = (session.session_id || '').slice(0, 8);
747
- const anno = toAnno(session);
748
- const date = formatDateRange(session.create_time, session.mod_time);
749
765
  const branchDisplay = session.branch ? `${BRANCH_ICON} ${session.branch}` : '';
750
766
 
751
767
  return h(
@@ -759,14 +775,7 @@ function ResumeView({onBack, onDone, session, clearScreen}) {
759
775
  colorize.project(session.project || ''), ' ',
760
776
  colorize.branch(branchDisplay)
761
777
  ),
762
- h(
763
- Text,
764
- null,
765
- colorize.agent(`[${session.agent_display || 'CLAUDE'}]`), ' ',
766
- chalk.white(id), anno ? ` ${chalk.dim(anno)}` : '', ' | ',
767
- colorize.lines(formatLines(session.lines)), ' | ',
768
- colorize.date(date)
769
- ),
778
+ h(Text, null, ...formatSessionHeader(session)),
770
779
  renderPreview(session.preview)
771
780
  ),
772
781
  h(Box, {marginBottom: 1}),
@@ -816,9 +825,6 @@ function TrimView({onBack, onDone, session, clearScreen}) {
816
825
  }
817
826
  });
818
827
 
819
- const id = (session.session_id || '').slice(0, 8);
820
- const anno = toAnno(session);
821
- const date = formatDateRange(session.create_time, session.mod_time);
822
828
  const branchDisplay = session.branch ? `${BRANCH_ICON} ${session.branch}` : '';
823
829
 
824
830
  return h(
@@ -832,14 +838,7 @@ function TrimView({onBack, onDone, session, clearScreen}) {
832
838
  colorize.project(session.project || ''), ' ',
833
839
  colorize.branch(branchDisplay)
834
840
  ),
835
- h(
836
- Text,
837
- null,
838
- colorize.agent(`[${session.agent_display || 'CLAUDE'}]`), ' ',
839
- chalk.white(id), anno ? ` ${chalk.dim(anno)}` : '', ' | ',
840
- colorize.lines(formatLines(session.lines)), ' | ',
841
- colorize.date(date)
842
- ),
841
+ h(Text, null, ...formatSessionHeader(session)),
843
842
  renderPreview(session.preview)
844
843
  ),
845
844
  h(Box, {marginBottom: 1}),
@@ -1094,9 +1093,6 @@ function TrimForm({onSubmit, onBack, clearScreen, session}) {
1094
1093
  });
1095
1094
 
1096
1095
  const arrow = figures.pointer;
1097
- const id = (session.session_id || '').slice(0, 8);
1098
- const anno = toAnno(session);
1099
- const date = formatDateRange(session.create_time, session.mod_time);
1100
1096
  const branchDisplay = session.branch ? `${BRANCH_ICON} ${session.branch}` : '';
1101
1097
 
1102
1098
  return h(
@@ -1110,14 +1106,7 @@ function TrimForm({onSubmit, onBack, clearScreen, session}) {
1110
1106
  colorize.project(session.project || ''), ' ',
1111
1107
  colorize.branch(branchDisplay)
1112
1108
  ),
1113
- h(
1114
- Text,
1115
- null,
1116
- colorize.agent(`[${session.agent_display || 'CLAUDE'}]`), ' ',
1117
- chalk.white(id), anno ? ` ${chalk.dim(anno)}` : '', ' | ',
1118
- colorize.lines(formatLines(session.lines)), ' | ',
1119
- colorize.date(date)
1120
- ),
1109
+ h(Text, null, ...formatSessionHeader(session)),
1121
1110
  renderPreview(session.preview)
1122
1111
  ),
1123
1112
  h(Box, {marginBottom: 1}),
@@ -1240,9 +1229,6 @@ function LineageView({session, rpcPath, onContinue, onBack, clearScreen}) {
1240
1229
  }
1241
1230
  });
1242
1231
 
1243
- const id = (session.session_id || '').slice(0, 8);
1244
- const anno = toAnno(session);
1245
- const date = formatDateRange(session.create_time, session.mod_time);
1246
1232
  const branchDisplay = session.branch ? `${BRANCH_ICON} ${session.branch}` : '';
1247
1233
 
1248
1234
  return h(
@@ -1256,14 +1242,7 @@ function LineageView({session, rpcPath, onContinue, onBack, clearScreen}) {
1256
1242
  colorize.project(session.project || ''), ' ',
1257
1243
  colorize.branch(branchDisplay)
1258
1244
  ),
1259
- h(
1260
- Text,
1261
- null,
1262
- colorize.agent(`[${session.agent_display || 'CLAUDE'}]`), ' ',
1263
- chalk.white(id), anno ? ` ${chalk.dim(anno)}` : '', ' | ',
1264
- colorize.lines(formatLines(session.lines)), ' | ',
1265
- colorize.date(date)
1266
- )
1245
+ h(Text, null, ...formatSessionHeader(session))
1267
1246
  ),
1268
1247
  h(Box, {marginTop: 1}),
1269
1248
  stage === 'loading'
@@ -1372,9 +1351,6 @@ function ContinueForm({onSubmit, onBack, clearScreen, session}) {
1372
1351
  });
1373
1352
 
1374
1353
  const arrow = figures.pointer;
1375
- const id = (session.session_id || '').slice(0, 8);
1376
- const anno = toAnno(session);
1377
- const date = formatDateRange(session.create_time, session.mod_time);
1378
1354
  const branchDisplay = session.branch ? `${BRANCH_ICON} ${session.branch}` : '';
1379
1355
 
1380
1356
  // Build the value display for each field
@@ -1399,14 +1375,7 @@ function ContinueForm({onSubmit, onBack, clearScreen, session}) {
1399
1375
  colorize.project(session.project || ''), ' ',
1400
1376
  colorize.branch(branchDisplay)
1401
1377
  ),
1402
- h(
1403
- Text,
1404
- null,
1405
- colorize.agent(`[${session.agent_display || 'CLAUDE'}]`), ' ',
1406
- chalk.white(id), anno ? ` ${chalk.dim(anno)}` : '', ' | ',
1407
- colorize.lines(formatLines(session.lines)), ' | ',
1408
- colorize.date(date)
1409
- ),
1378
+ h(Text, null, ...formatSessionHeader(session)),
1410
1379
  renderPreview(session.preview)
1411
1380
  ),
1412
1381
  h(Box, {marginBottom: 1}),
@@ -1449,7 +1418,7 @@ function ContinueForm({onSubmit, onBack, clearScreen, session}) {
1449
1418
  null,
1450
1419
  rolloverType === 'context' ? chalk.yellow('[2] Resume with Context Recovery') : chalk.white(' 2 Resume with Context Recovery')
1451
1420
  ),
1452
- h(Text, {dimColor: true}, ' Inject lineage + use sub-agents to extract context.'),
1421
+ h(Text, {dimColor: true}, ' Inject lineage + use headless/non-interactive agents to extract context.'),
1453
1422
  h(Text, {dimColor: true}, ' Slower, but resumes with full understanding of last task and overall work.')
1454
1423
  ),
1455
1424
  // Context instructions field - only shown when on prompt field (after pressing Enter/down)
@@ -1509,9 +1478,6 @@ function SmartTrimForm({onSubmit, onBack, clearScreen, session}) {
1509
1478
  }
1510
1479
  });
1511
1480
 
1512
- const id = (session.session_id || '').slice(0, 8);
1513
- const anno = toAnno(session);
1514
- const date = formatDateRange(session.create_time, session.mod_time);
1515
1481
  const branchDisplay = session.branch ? `${BRANCH_ICON} ${session.branch}` : '';
1516
1482
 
1517
1483
  // Display: show user's input in yellow, or default in dim gray
@@ -1530,14 +1496,7 @@ function SmartTrimForm({onSubmit, onBack, clearScreen, session}) {
1530
1496
  colorize.project(session.project || ''), ' ',
1531
1497
  colorize.branch(branchDisplay)
1532
1498
  ),
1533
- h(
1534
- Text,
1535
- null,
1536
- colorize.agent(`[${session.agent_display || 'CLAUDE'}]`), ' ',
1537
- chalk.white(id), anno ? ` ${chalk.dim(anno)}` : '', ' | ',
1538
- colorize.lines(formatLines(session.lines)), ' | ',
1539
- colorize.date(date)
1540
- ),
1499
+ h(Text, null, ...formatSessionHeader(session)),
1541
1500
  renderPreview(session.preview)
1542
1501
  ),
1543
1502
  h(Box, {marginBottom: 1}),
@@ -1917,14 +1876,7 @@ function QueryView({session, rpcPath, onBack, onExit, clearScreen, exitOnBack =
1917
1876
  colorize.project(session.project || ''), ' ',
1918
1877
  colorize.branch(branchDisplay)
1919
1878
  ),
1920
- h(
1921
- Text,
1922
- null,
1923
- colorize.agent(`[${session.agent_display || 'CLAUDE'}]`), ' ',
1924
- chalk.white(id), anno ? ` ${chalk.dim(anno)}` : '', ' | ',
1925
- colorize.lines(formatLines(session.lines)), ' | ',
1926
- colorize.date(date)
1927
- ),
1879
+ h(Text, null, ...formatSessionHeader(session)),
1928
1880
  stage === 'prompt'
1929
1881
  ? h(
1930
1882
  Box,
@@ -2043,9 +1995,6 @@ function NonLaunchView({session, action, rpcPath, onBack, onExit, clearScreen, e
2043
1995
  }
2044
1996
  });
2045
1997
 
2046
- const id = (session.session_id || '').slice(0, 8);
2047
- const anno = toAnno(session);
2048
- const date = formatDateRange(session.create_time, session.mod_time);
2049
1998
  const branchDisplay = session.branch ? `${BRANCH_ICON} ${session.branch}` : '';
2050
1999
 
2051
2000
  return h(
@@ -2056,14 +2005,7 @@ function NonLaunchView({session, action, rpcPath, onBack, onExit, clearScreen, e
2056
2005
  colorize.project(session.project || ''), ' ',
2057
2006
  colorize.branch(branchDisplay)
2058
2007
  ),
2059
- h(
2060
- Text,
2061
- null,
2062
- colorize.agent(`[${session.agent_display || 'CLAUDE'}]`), ' ',
2063
- chalk.white(id), anno ? ` ${chalk.dim(anno)}` : '', ' | ',
2064
- colorize.lines(formatLines(session.lines)), ' | ',
2065
- colorize.date(date)
2066
- ),
2008
+ h(Text, null, ...formatSessionHeader(session)),
2067
2009
  renderPreview(session.preview),
2068
2010
  stage === 'prompt'
2069
2011
  ? (() => {
@@ -2280,22 +2222,31 @@ function App() {
2280
2222
  if (value === 'suppress_resume') {
2281
2223
  setTrimSource('resume');
2282
2224
  switchScreen('trim');
2225
+ } else if (value === 'smart_trim_resume') {
2226
+ setTrimSource('resume');
2227
+ switchScreen('smart_trim_form');
2283
2228
  } else if (value === 'continue') switchScreen('lineage');
2284
2229
  else finish(value);
2285
2230
  },
2286
2231
  clearScreen,
2287
2232
  });
2288
2233
  } else if (screen === 'lineage') {
2234
+ // If directAction is set, back exits to shell; otherwise go to lineageBackTarget
2235
+ const lineageBack = directAction ? quit : () => switchScreen(lineageBackTarget);
2289
2236
  view = h(LineageView, {
2290
2237
  session,
2291
2238
  rpcPath,
2292
2239
  onContinue: () => switchScreen('continue_form'),
2293
- onBack: () => switchScreen(lineageBackTarget),
2240
+ onBack: lineageBack,
2294
2241
  clearScreen,
2295
2242
  });
2296
2243
  } else if (screen === 'continue_form') {
2297
- // If directAction is set, back exits to Rust search; otherwise go to lineage
2298
- const continueBack = directAction ? quit : () => switchScreen('lineage');
2244
+ // Back behavior depends on how we got here:
2245
+ // - If we started at lineage (rollover cmd), go back to lineage
2246
+ // - If directAction set and we started at continue_form (Rust search), exit to shell
2247
+ const continueBack = (directAction && startScreen !== 'lineage')
2248
+ ? quit
2249
+ : () => switchScreen('lineage');
2299
2250
  view = h(ContinueForm, {
2300
2251
  onBack: continueBack,
2301
2252
  onSubmit: (opts) => finish('continue', opts),
@@ -2303,8 +2254,8 @@ function App() {
2303
2254
  clearScreen,
2304
2255
  });
2305
2256
  } else if (screen === 'smart_trim_form') {
2306
- // If directAction is set, back exits to Rust search; otherwise quit
2307
- const smartTrimBack = directAction ? quit : quit;
2257
+ // If trimSource is 'direct', back exits to Rust search; otherwise go to trimSource screen
2258
+ const smartTrimBack = trimSource === 'direct' ? quit : () => switchScreen(trimSource || 'resume');
2308
2259
  view = h(SmartTrimForm, {
2309
2260
  onBack: smartTrimBack,
2310
2261
  onSubmit: (opts) => finish('smart_trim_resume', opts),
@@ -2332,6 +2283,9 @@ function App() {
2332
2283
  if (value === 'suppress_resume') {
2333
2284
  setTrimSource('trim_menu');
2334
2285
  switchScreen('trim');
2286
+ } else if (value === 'smart_trim_resume') {
2287
+ setTrimSource('trim_menu');
2288
+ switchScreen('smart_trim_form');
2335
2289
  } else finish(value);
2336
2290
  },
2337
2291
  clearScreen,