logwork-cli 1.1.1 → 1.2.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 (64) hide show
  1. package/README.md +23 -7
  2. package/RELEASE.md +1 -0
  3. package/commands/commits.mjs +57 -6
  4. package/commands/history.mjs +0 -4
  5. package/commands/quick.mjs +38 -23
  6. package/commands/reminder.mjs +41 -2
  7. package/commands/setup.mjs +50 -16
  8. package/commands/tui.mjs +1 -0
  9. package/docs/advanced.md +12 -2
  10. package/docs/mcp-setup.md +56 -14
  11. package/docs/operations-guide.md +35 -2
  12. package/docs/recipes.md +27 -0
  13. package/docs/security.md +3 -1
  14. package/examples/mcp/antigravity.mcp_config.json +1 -1
  15. package/examples/mcp/codex.config.toml +1 -1
  16. package/examples/mcp/copilot.mcp.json +1 -1
  17. package/lib/api-auth-forms.mjs +393 -0
  18. package/lib/api-auth-http.mjs +184 -0
  19. package/lib/api-auth-support.mjs +101 -0
  20. package/lib/api-auth-tokens.mjs +129 -0
  21. package/lib/api-auth.mjs +39 -813
  22. package/lib/api.mjs +38 -1422
  23. package/lib/apply-ledger.mjs +1 -1
  24. package/lib/batch-workflow.mjs +187 -2
  25. package/lib/commit-workflow.mjs +188 -21
  26. package/lib/config.mjs +7 -0
  27. package/lib/diagnostics.mjs +53 -4
  28. package/lib/jira-api.mjs +25 -3
  29. package/lib/jira-workflow.mjs +36 -6
  30. package/lib/logwork-config.mjs +64 -13
  31. package/lib/manual-apply-state.mjs +15 -0
  32. package/lib/manual-commands.mjs +2 -2
  33. package/lib/manual-ink-app.mjs +108 -1281
  34. package/lib/manual-ink-draft-actions.mjs +227 -0
  35. package/lib/manual-ink-interaction.mjs +306 -0
  36. package/lib/manual-ink-session.mjs +41 -0
  37. package/lib/manual-ink-task-editor.mjs +338 -0
  38. package/lib/manual-ink-wizard.mjs +520 -0
  39. package/lib/manual-mcp.mjs +27 -15
  40. package/lib/manual-repl.mjs +39 -3
  41. package/lib/mcp-server.mjs +51 -20
  42. package/lib/node-runtime.mjs +115 -0
  43. package/lib/paths.mjs +7 -0
  44. package/lib/project-mapping-workflow.mjs +30 -7
  45. package/lib/query-workflow.mjs +0 -1
  46. package/lib/reconciliation-workflow.mjs +1 -1
  47. package/lib/reminder-config.mjs +38 -2
  48. package/lib/reminder-scheduler.mjs +54 -3
  49. package/lib/reminder-service.mjs +28 -3
  50. package/lib/reminder-workflow.mjs +118 -22
  51. package/lib/ro-api-diagnostics.mjs +109 -0
  52. package/lib/ro-api-entries.mjs +359 -0
  53. package/lib/ro-api-fields.mjs +290 -0
  54. package/lib/ro-api-http.mjs +68 -0
  55. package/lib/ro-api-projects.mjs +137 -0
  56. package/lib/ro-api-tasks.mjs +50 -0
  57. package/lib/ro-api-timesheet.mjs +413 -0
  58. package/lib/ro-delete-workflow.mjs +2 -1
  59. package/lib/ro-entry-reader.mjs +11 -6
  60. package/lib/ro-resubmit-workflow.mjs +2 -1
  61. package/lib/terminal-auth-prompts.mjs +7 -4
  62. package/lib/ui.mjs +80 -59
  63. package/package.json +11 -4
  64. package/skills/logwork/SKILL.md +5 -4
@@ -6,65 +6,31 @@ import React, {
6
6
  } from 'react';
7
7
  import { Box, render, useApp } from 'ink';
8
8
  import { isAuthRequiredError } from './auth-errors.mjs';
9
+ import { isDryRun } from './config.mjs';
9
10
  import { isJiraAuthRequiredError } from './jira-auth.mjs';
10
- import {
11
- buildLogworkText,
12
- buildCurrentWeekDateOptions,
13
- buildProjectOverrides,
14
- buildProjectOptions,
15
- buildWeekDateOptions,
16
- canApplyPreview,
17
- formatDraftPreview,
18
- parseTaskLine,
19
- removeDraftTasks,
20
- replaceDraftTask,
21
- toggleTaskSelection
22
- } from './manual-logwork-wizard.mjs';
23
11
  import {
24
12
  getCommandSuggestions,
25
13
  TASK_COMMANDS
26
14
  } from './manual-commands.mjs';
15
+ import { jiraLoginInstruction } from './manual-targets.mjs';
27
16
  import {
28
- AUTH_TARGETS,
29
- BOTH_TARGET,
30
- jiraLoginInstruction,
31
- JIRA_TARGET,
32
- LOGWORK_TARGETS,
33
- previewTargets,
34
- RO_TARGET,
35
- targetLabel
36
- } from './manual-targets.mjs';
37
- import {
38
- deleteManualDraft,
39
- formatManualDraftLabel,
40
- loadManualDrafts,
41
- saveManualDraft
42
- } from './manual-drafts.mjs';
43
- import { enrichRoPreviewWithJiraProjects } from './project-mapping-workflow.mjs';
44
- import {
45
- CommandInput,
46
- ConfirmDialog,
47
17
  completeCommandValue,
48
18
  CurrentPanel,
49
- DatePicker,
50
- DraftPicker,
51
- formatTaskHours,
52
19
  HeaderBar,
53
20
  LoadingLine,
54
21
  loadingMessageForCommand,
55
- McpClientPicker,
56
22
  panelTitleForCommand,
57
- ProjectPicker,
58
- sameProjectIdentity,
59
- StatusBar,
60
- TargetPicker,
61
- TaskEditPicker,
62
- TaskRemovePicker
23
+ StatusBar
63
24
  } from './manual-ink-ui.mjs';
25
+ import { createInkCredentialProvider } from './manual-ink-auth.mjs';
64
26
  import {
65
- AuthPrompt,
66
- createInkCredentialProvider
67
- } from './manual-ink-auth.mjs';
27
+ activeSessionPreview,
28
+ hasSessionPreview
29
+ } from './manual-ink-session.mjs';
30
+ import { useLogworkWizard } from './manual-ink-wizard.mjs';
31
+ import { useDraftActions } from './manual-ink-draft-actions.mjs';
32
+ import { useTaskEditorActions } from './manual-ink-task-editor.mjs';
33
+ import { renderManualInteraction } from './manual-ink-interaction.mjs';
68
34
 
69
35
  export {
70
36
  CommandInput,
@@ -102,50 +68,6 @@ export {
102
68
  } from './manual-ink-auth.mjs';
103
69
 
104
70
  const h = React.createElement;
105
- const RECONCILIATION_PERIOD_OPTIONS = [
106
- { key: 'today', label: 'Today', description: 'Compare today' },
107
- { key: 'yesterday', label: 'Yesterday', description: 'Compare yesterday' },
108
- { key: 'this_week', label: 'This week', description: 'Compare Monday through Sunday' },
109
- { key: 'last_week', label: 'Last week', description: 'Compare the previous week' },
110
- { key: 'this_month', label: 'This month', description: 'Compare the current month' },
111
- { key: 'last_month', label: 'Last month', description: 'Compare the previous month' }
112
- ];
113
-
114
- function setSessionPreview(session, target, preview) {
115
- session.activeTarget = target;
116
- session.previews = {
117
- ...(session.previews || {}),
118
- [target]: preview
119
- };
120
- }
121
-
122
- function clearSessionPreview(session, target) {
123
- if (session.previews) {
124
- session.previews[target] = null;
125
- }
126
- }
127
-
128
- function clearSessionPreviews(session) {
129
- session.previews = {
130
- [RO_TARGET]: null,
131
- [JIRA_TARGET]: null
132
- };
133
- }
134
-
135
- function sessionPreview(session, target) {
136
- return session.previews?.[target] || null;
137
- }
138
-
139
- function hasSessionPreview(session) {
140
- return Boolean(session.previews?.[RO_TARGET] || session.previews?.[JIRA_TARGET]);
141
- }
142
-
143
- function activeSessionPreview(session) {
144
- if (session.activeTarget === JIRA_TARGET) {
145
- return sessionPreview(session, JIRA_TARGET);
146
- }
147
- return sessionPreview(session, RO_TARGET);
148
- }
149
71
 
150
72
  export async function runManualInkApp({
151
73
  input,
@@ -346,952 +268,76 @@ export function ManualApp({
346
268
  }
347
269
  }, [controller, cwd, exit, inkWorkflows, prompts, setCommandPanel]);
348
270
 
349
- const startNewLogworkWizard = useCallback(async (target = RO_TARGET) => {
350
- setInputValue('');
351
- setSelectedIndex(0);
352
- setLoading(target === JIRA_TARGET ? 'Building current week...' : 'Fetching current week...');
353
- setActivePanel({
354
- kind: 'logwork',
355
- title: 'Logwork',
356
- text: target === JIRA_TARGET
357
- ? 'Preparing Jira worklog dates...'
358
- : 'Loading this week booking/logwork data...'
359
- });
360
- try {
361
- const weekly = target === JIRA_TARGET
362
- ? null
363
- : await runWithInlineAuth(() => inkWorkflows.queryLogwork({
364
- period: 'this_week',
365
- cwd,
366
- includeEntries: false
367
- }));
368
- const dateOptions = weekly ? buildWeekDateOptions(weekly) : buildCurrentWeekDateOptions();
369
- if (!dateOptions.length) {
370
- throw new Error('Unable to build week date options.');
371
- }
372
- const todayIndex = dateOptions.findIndex((option) => option.isToday);
373
- setWizard({
374
- step: 'select_date',
375
- target,
376
- weekly,
377
- dateOptions,
378
- dateIndex: todayIndex >= 0 ? todayIndex : 0,
379
- projectsResult: null,
380
- projectOptions: [],
381
- projectIndex: 0,
382
- selectedDate: null,
383
- selectedProject: null,
384
- tasks: [],
385
- preview: null,
386
- activeDraftId: null
387
- });
388
- setActivePanel({
389
- kind: 'logwork',
390
- title: 'Select Date',
391
- text: `Choose a day for this ${targetLabel(target)} logwork session.`
392
- });
393
- } catch (error) {
394
- setActivePanel({
395
- kind: 'error',
396
- title: 'Logwork Error',
397
- text: error.message
398
- });
399
- } finally {
400
- setLoading('');
401
- }
402
- }, [cwd, inkWorkflows, runWithInlineAuth, runWithJiraAuthInstruction]);
403
-
404
- const startLogworkTargetPicker = useCallback(() => {
405
- setInputValue('');
406
- setSelectedIndex(0);
407
- setWizard({
408
- step: 'select_logwork_target',
409
- targetIndex: 0
410
- });
411
- setActivePanel({
412
- kind: 'logwork',
413
- title: 'Select Logwork Target',
414
- text: 'Choose where this logwork session should write.'
415
- });
416
- }, []);
417
-
418
- const startAuthTargetPicker = useCallback(() => {
419
- setInputValue('');
420
- setSelectedIndex(0);
421
- setWizard({
422
- step: 'select_auth_target',
423
- targetIndex: 0
424
- });
425
- setActivePanel({
426
- kind: 'auth',
427
- title: 'Select Auth Target',
428
- text: 'Choose which service to authenticate.'
429
- });
430
- }, []);
431
-
432
- const startReconciliationPeriodPicker = useCallback(() => {
433
- setInputValue('');
434
- setSelectedIndex(0);
435
- setWizard({
436
- step: 'select_reconciliation_period',
437
- periodIndex: 2
438
- });
439
- setActivePanel({
440
- kind: 'output',
441
- title: 'RO / Jira Reconciliation',
442
- text: 'Choose a preset period to compare.'
443
- });
444
- }, []);
445
-
446
- const startLogworkWizard = useCallback(async (target) => {
447
- if (!target) {
448
- startLogworkTargetPicker();
449
- return;
450
- }
451
-
452
- if (target !== RO_TARGET) {
453
- await startNewLogworkWizard(target);
454
- return;
455
- }
456
-
457
- setInputValue('');
458
- setSelectedIndex(0);
459
- setLoading('Loading saved drafts...');
460
- try {
461
- const drafts = await loadManualDrafts({ cwd });
462
- if (drafts.length) {
463
- setWizard({
464
- step: 'select_draft',
465
- target: RO_TARGET,
466
- drafts,
467
- draftIndex: 0,
468
- draftSource: 'start',
469
- weekly: null,
470
- dateOptions: [],
471
- dateIndex: 0,
472
- projectsResult: null,
473
- projectOptions: [],
474
- projectIndex: 0,
475
- selectedDate: null,
476
- selectedProject: null,
477
- tasks: [],
478
- preview: null,
479
- activeDraftId: null
480
- });
481
- setActivePanel({
482
- kind: 'logwork',
483
- title: 'Saved Drafts',
484
- text: 'Resume a saved draft, delete one, or start a new logwork session.'
485
- });
486
- return;
487
- }
488
- await startNewLogworkWizard(RO_TARGET);
489
- } catch (error) {
490
- setActivePanel({
491
- kind: 'error',
492
- title: 'Draft Error',
493
- text: error.message
494
- });
495
- } finally {
496
- setLoading('');
497
- }
498
- }, [cwd, startLogworkTargetPicker, startNewLogworkWizard]);
499
-
500
- const startMcpClientPicker = useCallback(() => {
501
- setInputValue('');
502
- setSelectedIndex(0);
503
- setWizard({
504
- step: 'select_mcp_client',
505
- mcpClientIndex: 0
506
- });
507
- setActivePanel({
508
- kind: 'output',
509
- title: 'MCP Setup',
510
- text: 'Choose an MCP client to print copy-ready setup.'
511
- });
512
- }, []);
513
-
514
- const cancelMcpClientPicker = useCallback(() => {
515
- setWizard(null);
516
- setActivePanel({
517
- kind: 'idle',
518
- title: 'Ready',
519
- text: 'Type / for commands.'
520
- });
521
- }, []);
522
-
523
- const selectWizardDate = useCallback(async (dateOption) => {
524
- const target = wizard?.target || RO_TARGET;
525
- if (target === JIRA_TARGET) {
526
- const draft = {
527
- target,
528
- date: dateOption.date,
529
- project: null,
530
- tasks: [],
531
- preview: null
532
- };
533
- sessionRef.current.logworkDraft = draft;
534
- clearSessionPreviews(sessionRef.current);
535
- sessionRef.current.activeTarget = target;
536
- setWizard((current) => ({
537
- ...current,
538
- step: 'edit_tasks',
539
- selectedDate: dateOption.date,
540
- selectedProject: null,
541
- tasks: [],
542
- preview: null,
543
- previews: {}
544
- }));
545
- setActivePanel({
546
- kind: 'logwork',
547
- title: 'Jira Worklog Draft',
548
- text: [
549
- formatDraftPreview(draft),
550
- '',
551
- 'Enter tasks one per line: +2 Feature Development | check ui/ux (SCB-213)',
552
- 'Empty Enter applies after preview is ready.',
553
- 'Type / for task commands. Esc cancels.'
554
- ].join('\n')
555
- });
556
- return;
557
- }
558
-
559
- setLoading('Fetching project memberships...');
560
- setActivePanel({
561
- kind: 'logwork',
562
- title: 'Select Project',
563
- text: `Loading projects for ${dateOption.date}...`
564
- });
565
- try {
566
- const projectsResult = await runWithInlineAuth(() => inkWorkflows.listLogworkProjects({ cwd }));
567
- const projectOptions = buildProjectOptions({
568
- projectsResult,
569
- weekly: wizard.weekly,
570
- date: dateOption.date
571
- });
572
- if (!projectOptions.length) {
573
- throw new Error('No Resource Optimiser project memberships found.');
574
- }
575
- setWizard((current) => ({
576
- ...current,
577
- step: 'select_project',
578
- target,
579
- selectedDate: dateOption.date,
580
- projectsResult,
581
- projectOptions,
582
- projectIndex: 0,
583
- selectedProject: null,
584
- tasks: [],
585
- preview: null
586
- }));
587
- setActivePanel({
588
- kind: 'logwork',
589
- title: 'Select Project',
590
- text: `Choose a project for ${dateOption.date}.`
591
- });
592
- } catch (error) {
593
- setActivePanel({
594
- kind: 'error',
595
- title: 'Project Error',
596
- text: error.message
597
- });
598
- } finally {
599
- setLoading('');
600
- }
601
- }, [cwd, inkWorkflows, runWithInlineAuth, wizard]);
602
-
603
- const selectWizardProject = useCallback((project) => {
604
- const target = wizard?.target || RO_TARGET;
605
- const draft = {
606
- target,
607
- date: wizard.selectedDate,
608
- project,
609
- tasks: [],
610
- preview: null
611
- };
612
- sessionRef.current.logworkDraft = draft;
613
- clearSessionPreviews(sessionRef.current);
614
- sessionRef.current.activeTarget = target;
615
- setWizard((current) => ({
616
- ...current,
617
- step: 'edit_tasks',
618
- target,
619
- selectedProject: project,
620
- tasks: [],
621
- preview: null,
622
- previews: {}
623
- }));
624
- setActivePanel({
625
- kind: 'logwork',
626
- title: target === BOTH_TARGET ? 'RO + Jira Draft' : 'Logwork Draft',
627
- text: [
628
- formatDraftPreview(draft),
629
- '',
630
- target === BOTH_TARGET ? 'Enter tasks one per line: +2 Feature Development | check ui/ux (SCB-213)' : 'Enter tasks one per line: +2 Feature Development | check ui/ux',
631
- 'Empty Enter applies after preview is ready.',
632
- 'Type / for task commands. Esc cancels.'
633
- ].join('\n')
634
- });
635
- }, [wizard]);
636
-
637
- const previewDraft = useCallback(async (nextWizard) => {
638
- const target = nextWizard.target || RO_TARGET;
639
- const draft = {
640
- target,
641
- date: nextWizard.selectedDate,
642
- project: nextWizard.selectedProject,
643
- tasks: nextWizard.tasks
644
- };
645
-
646
- if (!draft.tasks.length) {
647
- sessionRef.current.logworkDraft = draft;
648
- clearSessionPreviews(sessionRef.current);
649
- sessionRef.current.activeTarget = target;
650
- setWizard(nextWizard);
651
- setActivePanel({
652
- kind: 'logwork',
653
- title: target === JIRA_TARGET ? 'Jira Worklog Draft' : target === BOTH_TARGET ? 'RO + Jira Draft' : 'Logwork Draft',
654
- text: [
655
- formatDraftPreview(draft),
656
- '',
657
- target === RO_TARGET ? 'Enter tasks one per line: +2 Feature Development | check ui/ux' : 'Enter tasks one per line: +2 Feature Development | check ui/ux (SCB-213)',
658
- 'Empty Enter applies after preview is ready.'
659
- ].join('\n')
660
- });
661
- return;
662
- }
663
-
664
- setLoading('Building live preview...');
665
- try {
666
- const text = buildLogworkText({
667
- date: draft.date,
668
- tasks: draft.tasks
669
- });
670
- const previews = {};
671
- if (previewTargets(target).includes(RO_TARGET)) {
672
- const projectOverrides = buildProjectOverrides({
673
- date: draft.date,
674
- tasks: draft.tasks,
675
- projectMemberId: draft.project.projectMemberId
676
- });
677
- previews[RO_TARGET] = await runWithInlineAuth(() => inkWorkflows.previewLogworkBatch({
678
- text,
679
- projectOverrides,
680
- cwd
681
- }));
682
- }
683
- if (previewTargets(target).includes(JIRA_TARGET)) {
684
- previews[JIRA_TARGET] = await runWithJiraAuthInstruction(() => inkWorkflows.previewJiraWorklogBatch({
685
- text
686
- }));
687
- }
688
- if (target === BOTH_TARGET && previews[RO_TARGET] && previews[JIRA_TARGET]) {
689
- previews[RO_TARGET] = enrichRoPreviewWithJiraProjects(previews[RO_TARGET], previews[JIRA_TARGET]);
690
- }
691
- const preview = target === JIRA_TARGET ? previews[JIRA_TARGET] : previews[RO_TARGET];
692
- const withPreview = {
693
- ...draft,
694
- preview,
695
- previews
696
- };
697
- sessionRef.current.logworkDraft = withPreview;
698
- sessionRef.current.activeTarget = target;
699
- for (const previewTarget of previewTargets(target)) {
700
- setSessionPreview(sessionRef.current, previewTarget, previews[previewTarget] || null);
701
- }
702
- setWizard({
703
- ...nextWizard,
704
- target,
705
- preview,
706
- previews
707
- });
708
- const hasBlockedJira = previews[JIRA_TARGET]?.entries?.some((entry) => entry.status !== 'ready');
709
- setActivePanel({
710
- kind: preview?.unresolvedEntries?.length || preview?.errors?.length || hasBlockedJira ? 'error' : 'logwork',
711
- title: target === JIRA_TARGET ? 'Jira Worklog Preview' : target === BOTH_TARGET ? 'RO + Jira Preview' : 'Logwork Preview',
712
- text: [
713
- formatDraftPreview(withPreview),
714
- '',
715
- 'Empty Enter applies this preview.',
716
- 'Type another +hours task to add more.',
717
- '/remove selects tasks to delete.'
718
- ].join('\n')
719
- });
720
- return preview;
721
- } catch (error) {
722
- setActivePanel({
723
- kind: 'error',
724
- title: 'Preview Error',
725
- text: error.message
726
- });
727
- } finally {
728
- setLoading('');
729
- }
730
- }, [cwd, inkWorkflows, runWithInlineAuth, runWithJiraAuthInstruction]);
731
-
732
- const saveCurrentDraft = useCallback(async () => {
733
- if (wizard?.target === JIRA_TARGET) {
734
- setActivePanel({
735
- kind: 'logwork',
736
- title: 'Save Draft',
737
- text: 'Saved drafts are only supported for Resource Optimiser logwork in this version.'
738
- });
739
- return null;
740
- }
741
-
742
- if (!wizard?.selectedDate || !wizard?.selectedProject || !wizard?.tasks?.length) {
743
- setActivePanel({
744
- kind: 'logwork',
745
- title: 'Save Draft',
746
- text: 'Add at least one task before saving a draft.'
747
- });
748
- return null;
749
- }
750
-
751
- try {
752
- const saved = await saveManualDraft({
753
- id: wizard.activeDraftId,
754
- cwd,
755
- date: wizard.selectedDate,
756
- project: wizard.selectedProject,
757
- tasks: wizard.tasks,
758
- latestPreviewStatus: wizard.preview?.status
759
- }, { cwd });
760
- setWizard((current) => ({
761
- ...current,
762
- activeDraftId: saved.id
763
- }));
764
- setActivePanel({
765
- kind: 'success',
766
- title: 'Draft Saved',
767
- text: [
768
- `Saved ${saved.tasks.length} task${saved.tasks.length === 1 ? '' : 's'} locally.`,
769
- formatManualDraftLabel(saved),
770
- '',
771
- 'Continue editing, press empty Enter to apply, or type /drafts.'
772
- ].join('\n')
773
- });
774
- return saved;
775
- } catch (error) {
776
- setActivePanel({
777
- kind: 'error',
778
- title: 'Save Draft Error',
779
- text: error.message
780
- });
781
- return null;
782
- }
783
- }, [cwd, wizard]);
784
-
785
- const openDraftPicker = useCallback(async (source = 'editor') => {
786
- setLoading('Loading saved drafts...');
787
- try {
788
- const drafts = await loadManualDrafts({ cwd });
789
- if (!drafts.length) {
790
- setActivePanel({
791
- kind: 'logwork',
792
- title: 'Saved Drafts',
793
- text: 'No saved drafts found.'
794
- });
795
- return;
796
- }
797
- setWizard((current) => ({
798
- ...current,
799
- step: 'select_draft',
800
- drafts,
801
- draftIndex: 0,
802
- draftSource: source
803
- }));
804
- setActivePanel({
805
- kind: 'logwork',
806
- title: 'Saved Drafts',
807
- text: 'Enter resumes · d deletes · Esc returns.'
808
- });
809
- } catch (error) {
810
- setActivePanel({
811
- kind: 'error',
812
- title: 'Draft Error',
813
- text: error.message
814
- });
815
- } finally {
816
- setLoading('');
817
- }
818
- }, [cwd]);
819
-
820
- const resumeManualDraft = useCallback(async (draft) => {
821
- setLoading('Resuming saved draft...');
822
- try {
823
- const weekly = await runWithInlineAuth(() => inkWorkflows.queryLogwork({
824
- period: 'this_week',
825
- cwd,
826
- includeEntries: false
827
- }));
828
- const dateOptions = buildWeekDateOptions(weekly);
829
- const projectsResult = await runWithInlineAuth(() => inkWorkflows.listLogworkProjects({ cwd }));
830
- const projectOptions = buildProjectOptions({
831
- projectsResult,
832
- weekly,
833
- date: draft.date
834
- });
835
- const selectedProject = projectOptions.find((project) => sameProjectIdentity(project, draft.project)) || draft.project;
836
- const nextWizard = {
837
- step: 'edit_tasks',
838
- target: RO_TARGET,
839
- weekly,
840
- dateOptions,
841
- dateIndex: Math.max(0, dateOptions.findIndex((option) => option.date === draft.date)),
842
- projectsResult,
843
- projectOptions,
844
- projectIndex: Math.max(0, projectOptions.findIndex((project) => sameProjectIdentity(project, selectedProject))),
845
- selectedDate: draft.date,
846
- selectedProject,
847
- tasks: draft.tasks,
848
- preview: null,
849
- activeDraftId: draft.id
850
- };
851
- sessionRef.current.logworkDraft = {
852
- target: RO_TARGET,
853
- date: draft.date,
854
- project: selectedProject,
855
- tasks: draft.tasks,
856
- preview: null
857
- };
858
- clearSessionPreviews(sessionRef.current);
859
- sessionRef.current.activeTarget = RO_TARGET;
860
- await previewDraft(nextWizard);
861
- } catch (error) {
862
- setActivePanel({
863
- kind: 'error',
864
- title: 'Resume Draft Error',
865
- text: error.message
866
- });
867
- } finally {
868
- setLoading('');
869
- }
870
- }, [cwd, inkWorkflows, previewDraft, runWithInlineAuth]);
871
-
872
- const deleteSavedDraft = useCallback(async (draft) => {
873
- const ok = await prompts.confirm(`Delete saved draft "${formatManualDraftLabel(draft)}"?`, false);
874
- if (!ok) {
875
- return;
876
- }
877
- await deleteManualDraft(draft.id);
878
- const drafts = await loadManualDrafts({ cwd });
879
- setWizard((current) => ({
880
- ...current,
881
- drafts,
882
- draftIndex: 0
883
- }));
884
- setActivePanel({
885
- kind: 'success',
886
- title: 'Draft Deleted',
887
- text: drafts.length ? 'Draft deleted. Choose another saved draft or start new.' : 'Draft deleted. No saved drafts remain.'
888
- });
889
- }, [cwd, prompts]);
890
-
891
- const returnFromDraftPicker = useCallback(() => {
892
- if (wizard?.draftSource === 'start') {
893
- startNewLogworkWizard(RO_TARGET);
894
- return;
895
- }
896
- setWizard((current) => ({
897
- ...current,
898
- step: 'edit_tasks',
899
- draftSource: undefined
900
- }));
901
- const draft = {
902
- target: wizard?.target || RO_TARGET,
903
- date: wizard?.selectedDate,
904
- project: wizard?.selectedProject,
905
- tasks: wizard?.tasks || [],
906
- preview: wizard?.preview
907
- };
908
- setActivePanel({
909
- kind: 'logwork',
910
- title: wizard?.preview ? 'Logwork Preview' : 'Logwork Draft',
911
- text: [
912
- formatDraftPreview(draft),
913
- '',
914
- 'Type / for task commands. Empty Enter applies when ready.'
915
- ].join('\n')
916
- });
917
- }, [startNewLogworkWizard, wizard]);
918
-
919
- const cancelCurrentLogwork = useCallback(async () => {
920
- if (wizard?.tasks?.length) {
921
- const ok = await prompts.confirm('Discard this unsaved logwork session?', false);
922
- if (!ok) {
923
- return;
924
- }
925
- }
926
- sessionRef.current.logworkDraft = null;
927
- clearSessionPreviews(sessionRef.current);
928
- setWizard(null);
929
- setActivePanel({
930
- kind: 'idle',
931
- title: 'Ready',
932
- text: 'Logwork session discarded. Type / for commands.'
933
- });
934
- }, [prompts, wizard]);
935
-
936
- const applyWizardPreview = useCallback(async () => {
937
- const target = wizard.target || RO_TARGET;
938
- if (!wizard.tasks.length) {
939
- setActivePanel({
940
- kind: 'logwork',
941
- title: 'Logwork Draft',
942
- text: target === RO_TARGET ? 'Enter a task like +2 Feature Development | check ui/ux.' : 'Enter a task like +2 Feature Development | check ui/ux (SCB-213).'
943
- });
944
- return;
945
- }
946
-
947
- const targets = previewTargets(target);
948
- const missingPreview = targets.some((previewTarget) => !sessionPreview(sessionRef.current, previewTarget));
949
- if (missingPreview) {
950
- await previewDraft(wizard);
951
- return;
952
- }
953
-
954
- for (const previewTarget of targets) {
955
- const applyState = canApplyPreview(sessionPreview(sessionRef.current, previewTarget), { target: previewTarget });
956
- if (!applyState.ok) {
957
- setActivePanel({
958
- kind: 'error',
959
- title: 'Apply Blocked',
960
- text: `${targetLabel(previewTarget)}: ${applyState.reason}`
961
- });
962
- return;
963
- }
964
- }
965
-
966
- await executeCommand({ type: 'apply', target });
967
- if (!hasSessionPreview(sessionRef.current)) {
968
- setWizard(null);
969
- }
970
- }, [executeCommand, previewDraft, wizard]);
971
-
972
- const restoreTaskEditorPanel = useCallback((nextWizard = wizard) => {
973
- const target = nextWizard.target || RO_TARGET;
974
- const draft = {
975
- target,
976
- date: nextWizard.selectedDate,
977
- project: nextWizard.selectedProject,
978
- tasks: nextWizard.tasks,
979
- preview: nextWizard.preview,
980
- previews: nextWizard.previews
981
- };
982
- setActivePanel({
983
- kind: 'logwork',
984
- title: nextWizard.preview ? `${targetLabel(target)} Preview` : `${targetLabel(target)} Draft`,
985
- text: [
986
- formatDraftPreview(draft),
987
- '',
988
- nextWizard.preview ? 'Empty Enter applies this preview.' : target === RO_TARGET ? 'Enter tasks one per line: +2 Feature Development | check ui/ux' : 'Enter tasks one per line: +2 Feature Development | check ui/ux (SCB-213)',
989
- nextWizard.preview ? 'Type another +hours task to add more.' : 'Empty Enter applies after preview is ready.',
990
- 'Type / for task commands. Esc cancels.'
991
- ].join('\n')
992
- });
993
- }, [wizard]);
994
-
995
- const cancelRemoveTasks = useCallback(() => {
996
- setWizard((current) => ({
997
- ...current,
998
- step: 'edit_tasks',
999
- removeIndex: 0,
1000
- removeSelectedIndexes: []
1001
- }));
1002
- restoreTaskEditorPanel();
1003
- }, [restoreTaskEditorPanel]);
1004
-
1005
- const confirmRemoveTasks = useCallback(async (selectedIndexes) => {
1006
- if (!selectedIndexes.length) {
1007
- setActivePanel({
1008
- kind: 'logwork',
1009
- title: 'Remove Tasks',
1010
- text: 'No tasks selected. Use Space to select tasks, Enter to remove, Esc to cancel.'
1011
- });
1012
- return;
1013
- }
1014
-
1015
- const ok = await prompts.confirm(`Remove ${selectedIndexes.length} selected task${selectedIndexes.length === 1 ? '' : 's'}?`);
1016
- if (!ok) {
1017
- cancelRemoveTasks();
1018
- return;
1019
- }
1020
-
1021
- await previewDraft({
1022
- ...wizard,
1023
- step: 'edit_tasks',
1024
- tasks: removeDraftTasks(wizard.tasks, selectedIndexes),
1025
- preview: null,
1026
- removeIndex: 0,
1027
- removeSelectedIndexes: []
1028
- });
1029
- }, [cancelRemoveTasks, previewDraft, prompts, wizard]);
1030
-
1031
- const submitWizardTaskInput = useCallback(async (value) => {
1032
- const text = String(value || '').trim();
1033
- setInputValue('');
1034
-
1035
- if (!text) {
1036
- await applyWizardPreview();
1037
- return;
1038
- }
1039
-
1040
- if (text === '/diagnostics') {
1041
- await executeCommand({ type: 'diagnostics' });
1042
- return;
1043
- }
1044
-
1045
- if (text === '/cancel' || text === '/discard') {
1046
- await cancelCurrentLogwork();
1047
- return;
1048
- }
1049
-
1050
- if (text === '/back') {
1051
- sessionRef.current.logworkDraft = null;
1052
- clearSessionPreviews(sessionRef.current);
1053
- if (wizard.target === JIRA_TARGET) {
1054
- setWizard((current) => ({
1055
- ...current,
1056
- step: 'select_date',
1057
- selectedDate: null,
1058
- tasks: [],
1059
- preview: null,
1060
- previews: {}
1061
- }));
1062
- setActivePanel({
1063
- kind: 'logwork',
1064
- title: 'Select Date',
1065
- text: 'Choose a day for this Jira worklog session.'
1066
- });
1067
- } else {
1068
- setWizard((current) => ({
1069
- ...current,
1070
- step: 'select_project',
1071
- selectedProject: null,
1072
- tasks: [],
1073
- preview: null,
1074
- previews: {}
1075
- }));
1076
- setActivePanel({
1077
- kind: 'logwork',
1078
- title: 'Select Project',
1079
- text: `Choose a project for ${wizard.selectedDate}.`
1080
- });
1081
- }
1082
- return;
1083
- }
1084
-
1085
- if (text === '/clear') {
1086
- const ok = !wizard.tasks.length || await prompts.confirm('Clear all current tasks?', false);
1087
- if (!ok) {
1088
- restoreTaskEditorPanel();
1089
- return;
1090
- }
1091
- await previewDraft({
1092
- ...wizard,
1093
- tasks: [],
1094
- preview: null
1095
- });
1096
- return;
1097
- }
1098
-
1099
- if (text === '/save') {
1100
- await saveCurrentDraft();
1101
- return;
1102
- }
1103
-
1104
- if (text === '/drafts') {
1105
- await openDraftPicker('editor');
1106
- return;
1107
- }
1108
-
1109
- if (text === '/edit') {
1110
- if (!wizard.tasks.length) {
1111
- setActivePanel({
1112
- kind: 'error',
1113
- title: 'Edit Task',
1114
- text: 'No tasks to edit.'
1115
- });
1116
- return;
1117
- }
1118
- setWizard((current) => ({
1119
- ...current,
1120
- step: 'edit_task_select',
1121
- editIndex: 0,
1122
- replacementError: ''
1123
- }));
1124
- setActivePanel({
1125
- kind: 'logwork',
1126
- title: 'Edit Task',
1127
- text: 'Select a task to replace. Esc returns to the editor.'
1128
- });
1129
- return;
1130
- }
1131
-
1132
- if (text === '/remove') {
1133
- if (!wizard.tasks.length) {
1134
- setActivePanel({
1135
- kind: 'error',
1136
- title: 'Remove Task',
1137
- text: 'No tasks to remove.'
1138
- });
1139
- return;
1140
- }
1141
- setWizard((current) => ({
1142
- ...current,
1143
- step: 'remove_tasks',
1144
- removeIndex: 0,
1145
- removeSelectedIndexes: []
1146
- }));
1147
- setActivePanel({
1148
- kind: 'logwork',
1149
- title: 'Remove Tasks',
1150
- text: 'Select tasks to remove. Space toggles, Enter confirms, Esc cancels.'
1151
- });
1152
- return;
1153
- }
1154
-
1155
- if (text.startsWith('/remove ')) {
1156
- const index = Number(text.slice('/remove '.length).trim());
1157
- if (!Number.isInteger(index)) {
1158
- setActivePanel({
1159
- kind: 'error',
1160
- title: 'Remove Task',
1161
- text: 'Usage: /remove <task number>'
1162
- });
1163
- return;
1164
- }
1165
- if (index < 1 || index > wizard.tasks.length) {
1166
- setActivePanel({
1167
- kind: 'error',
1168
- title: 'Remove Task',
1169
- text: `No task found at index ${index}.`
1170
- });
1171
- return;
1172
- }
1173
- await previewDraft({
1174
- ...wizard,
1175
- tasks: wizard.tasks.filter((_, currentIndex) => currentIndex !== index - 1),
1176
- preview: null
1177
- });
1178
- return;
1179
- }
1180
-
1181
- if (text === '/done') {
1182
- if (!wizard.tasks.length) {
1183
- setActivePanel({
1184
- kind: 'error',
1185
- title: 'Logwork Draft',
1186
- text: 'Add at least one task before /done.'
1187
- });
1188
- return;
1189
- }
1190
- await previewDraft(wizard);
1191
- return;
1192
- }
1193
-
1194
- if (text === '/apply') {
1195
- await applyWizardPreview();
1196
- return;
1197
- }
1198
-
1199
- if (text.startsWith('/')) {
1200
- setActivePanel({
1201
- kind: 'error',
1202
- title: 'Logwork Draft',
1203
- text: `Unknown logwork command: ${text}`
1204
- });
1205
- return;
1206
- }
1207
-
1208
- try {
1209
- const task = parseTaskLine(text);
1210
- await previewDraft({
1211
- ...wizard,
1212
- tasks: [...wizard.tasks, task],
1213
- preview: null
1214
- });
1215
- } catch (error) {
1216
- setActivePanel({
1217
- kind: 'error',
1218
- title: 'Task Error',
1219
- text: error.message
1220
- });
1221
- }
1222
- }, [
1223
- applyWizardPreview,
271
+ const {
272
+ startNewLogworkWizard,
273
+ startAuthTargetPicker,
274
+ startReconciliationPeriodPicker,
275
+ startLogworkWizard,
276
+ startMcpClientPicker,
277
+ cancelMcpClientPicker,
278
+ selectWizardDate,
279
+ selectWizardProject,
280
+ previewDraft,
1224
281
  cancelCurrentLogwork,
1225
- executeCommand,
282
+ applyWizardPreview,
283
+ restoreTaskEditorPanel
284
+ } = useLogworkWizard({
285
+ cwd,
286
+ sessionRef,
287
+ wizard,
288
+ setWizard,
289
+ setActivePanel,
290
+ setLoading,
291
+ setInputValue,
292
+ setSelectedIndex,
293
+ inkWorkflows,
294
+ prompts,
295
+ runWithInlineAuth,
296
+ runWithJiraAuthInstruction,
297
+ executeCommand
298
+ });
299
+
300
+ const {
301
+ saveCurrentDraft,
1226
302
  openDraftPicker,
303
+ resumeManualDraft,
304
+ deleteSavedDraft,
305
+ returnFromDraftPicker
306
+ } = useDraftActions({
307
+ cwd,
308
+ sessionRef,
309
+ wizard,
310
+ setWizard,
311
+ setActivePanel,
312
+ setLoading,
313
+ inkWorkflows,
314
+ prompts,
315
+ runWithInlineAuth,
1227
316
  previewDraft,
317
+ startNewLogworkWizard
318
+ });
319
+
320
+ const {
321
+ cancelRemoveTasks,
322
+ confirmRemoveTasks,
323
+ submitWizardTaskInput,
324
+ selectTaskForEdit,
325
+ submitTaskReplacement
326
+ } = useTaskEditorActions({
327
+ sessionRef,
328
+ wizard,
329
+ setWizard,
330
+ setActivePanel,
331
+ setInputValue,
1228
332
  prompts,
333
+ executeCommand,
334
+ previewDraft,
335
+ applyWizardPreview,
336
+ cancelCurrentLogwork,
1229
337
  restoreTaskEditorPanel,
1230
338
  saveCurrentDraft,
1231
- wizard
1232
- ]);
1233
-
1234
- const selectTaskForEdit = useCallback((index) => {
1235
- const task = wizard.tasks[index];
1236
- setInputValue('');
1237
- setWizard((current) => ({
1238
- ...current,
1239
- step: 'edit_task_replace',
1240
- editIndex: index,
1241
- replacementError: ''
1242
- }));
1243
- setActivePanel({
1244
- kind: 'logwork',
1245
- title: 'Replace Task',
1246
- text: [
1247
- `Replacing ${index + 1}. +${formatTaskHours(task.hours)} ${task.taskName}`,
1248
- 'Enter replacement line using format: +2 Feature Development | check ui/ux',
1249
- 'Esc returns without changing the task.'
1250
- ].join('\n')
1251
- });
1252
- }, [wizard]);
1253
-
1254
- const submitTaskReplacement = useCallback(async (value) => {
1255
- const text = String(value || '').trim();
1256
- setInputValue('');
1257
- if (!text) {
1258
- setActivePanel({
1259
- kind: 'error',
1260
- title: 'Replace Task',
1261
- text: 'Replacement task is empty. Use format: +2 Feature Development | check ui/ux'
1262
- });
1263
- return;
1264
- }
1265
- if (text === '/cancel') {
1266
- setWizard((current) => ({
1267
- ...current,
1268
- step: 'edit_tasks',
1269
- replacementError: ''
1270
- }));
1271
- restoreTaskEditorPanel();
1272
- return;
1273
- }
1274
- try {
1275
- const task = parseTaskLine(text);
1276
- await previewDraft({
1277
- ...wizard,
1278
- step: 'edit_tasks',
1279
- tasks: replaceDraftTask(wizard.tasks, wizard.editIndex, task),
1280
- preview: null,
1281
- replacementError: ''
1282
- });
1283
- } catch (error) {
1284
- setWizard((current) => ({
1285
- ...current,
1286
- replacementError: error.message
1287
- }));
1288
- setActivePanel({
1289
- kind: 'error',
1290
- title: 'Replace Task',
1291
- text: error.message
1292
- });
1293
- }
1294
- }, [previewDraft, restoreTaskEditorPanel, wizard]);
339
+ openDraftPicker
340
+ });
1295
341
 
1296
342
  const submitInput = useCallback((value) => {
1297
343
  const text = String(value || '');
@@ -1396,259 +442,40 @@ export function ManualApp({
1396
442
  loading ? h(LoadingLine, { message: loading }) : null,
1397
443
  h(StatusBar, {
1398
444
  preview: activeSessionPreview(sessionRef.current),
1399
- dryRun: process.env.LOGWORK_DRY_RUN === '1',
445
+ dryRun: isDryRun(),
1400
446
  wizard,
1401
447
  authPrompt: authPrompt || (authActive ? { step: 'working' } : null)
1402
448
  }),
1403
- authPrompt
1404
- ? h(AuthPrompt, {
1405
- prompt: authPrompt,
1406
- inputValue,
1407
- onInputChange(value) {
1408
- setInputValue(value);
1409
- },
1410
- onPromptChange(nextPrompt) {
1411
- setAuthPrompt(nextPrompt);
1412
- },
1413
- onResolve: resolveAuthPrompt,
1414
- onReject: rejectAuthPrompt
1415
- })
1416
- : authActive
1417
- ? h(AuthPrompt, {
1418
- prompt: { step: 'working' },
1419
- inputValue,
1420
- onInputChange(value) {
1421
- setInputValue(value);
1422
- },
1423
- onPromptChange(nextPrompt) {
1424
- setAuthPrompt(nextPrompt);
1425
- },
1426
- onResolve: resolveAuthPrompt,
1427
- onReject: rejectAuthPrompt
1428
- })
1429
- : pendingConfirm
1430
- ? h(ConfirmDialog, {
1431
- message: pendingConfirm.message,
1432
- initialValue: pendingConfirm.initialValue,
1433
- onResolve: resolveConfirm
1434
- })
1435
- : wizard?.step === 'select_logwork_target'
1436
- ? h(TargetPicker, {
1437
- title: 'Pick logwork target',
1438
- targets: LOGWORK_TARGETS,
1439
- selectedIndex: wizard.targetIndex || 0,
1440
- onChange(index) {
1441
- setWizard((current) => ({
1442
- ...current,
1443
- targetIndex: index
1444
- }));
1445
- },
1446
- onSelect(target) {
1447
- startLogworkWizard(target.key);
1448
- },
1449
- onCancel() {
1450
- setWizard(null);
1451
- setActivePanel({
1452
- kind: 'idle',
1453
- title: 'Ready',
1454
- text: 'Type / for commands.'
1455
- });
1456
- }
1457
- })
1458
- : wizard?.step === 'select_auth_target'
1459
- ? h(TargetPicker, {
1460
- title: 'Pick auth target',
1461
- targets: AUTH_TARGETS,
1462
- selectedIndex: wizard.targetIndex || 0,
1463
- onChange(index) {
1464
- setWizard((current) => ({
1465
- ...current,
1466
- targetIndex: index
1467
- }));
1468
- },
1469
- onSelect(target) {
1470
- executeCommand({
1471
- type: 'auth',
1472
- target: target.key
1473
- });
1474
- },
1475
- onCancel() {
1476
- setWizard(null);
1477
- setActivePanel({
1478
- kind: 'idle',
1479
- title: 'Ready',
1480
- text: 'Type / for commands.'
1481
- });
1482
- }
1483
- })
1484
- : wizard?.step === 'select_reconciliation_period'
1485
- ? h(TargetPicker, {
1486
- title: 'Pick reconciliation period',
1487
- targets: RECONCILIATION_PERIOD_OPTIONS,
1488
- selectedIndex: wizard.periodIndex || 0,
1489
- onChange(index) {
1490
- setWizard((current) => ({
1491
- ...current,
1492
- periodIndex: index
1493
- }));
1494
- },
1495
- onSelect(period) {
1496
- executeCommand({
1497
- type: 'reconcile',
1498
- period: period.key
1499
- });
1500
- },
1501
- onCancel() {
1502
- setWizard(null);
1503
- setActivePanel({
1504
- kind: 'idle',
1505
- title: 'Ready',
1506
- text: 'Type / for commands.'
1507
- });
1508
- }
1509
- })
1510
- : wizard?.step === 'select_draft'
1511
- ? h(DraftPicker, {
1512
- drafts: wizard.drafts || [],
1513
- selectedIndex: wizard.draftIndex || 0,
1514
- includeStartNew: wizard.draftSource === 'start',
1515
- onChange(index) {
1516
- setWizard((current) => ({
1517
- ...current,
1518
- draftIndex: index
1519
- }));
1520
- },
1521
- onResume(draft) {
1522
- resumeManualDraft(draft);
1523
- },
1524
- onDelete(draft) {
1525
- deleteSavedDraft(draft);
1526
- },
1527
- onStartNew() {
1528
- startNewLogworkWizard(RO_TARGET);
1529
- },
1530
- onCancel() {
1531
- returnFromDraftPicker();
1532
- }
1533
- })
1534
- : wizard?.step === 'select_date'
1535
- ? h(DatePicker, {
1536
- options: wizard.dateOptions,
1537
- selectedIndex: wizard.dateIndex,
1538
- onChange(index) {
1539
- setWizard((current) => ({
1540
- ...current,
1541
- dateIndex: index
1542
- }));
1543
- },
1544
- onSelect(option) {
1545
- selectWizardDate(option);
1546
- }
1547
- })
1548
- : wizard?.step === 'select_project'
1549
- ? h(ProjectPicker, {
1550
- options: wizard.projectOptions,
1551
- selectedIndex: wizard.projectIndex,
1552
- onChange(index) {
1553
- setWizard((current) => ({
1554
- ...current,
1555
- projectIndex: index
1556
- }));
1557
- },
1558
- onSelect(project) {
1559
- selectWizardProject(project);
1560
- }
1561
- })
1562
- : wizard?.step === 'select_mcp_client'
1563
- ? h(McpClientPicker, {
1564
- selectedIndex: wizard.mcpClientIndex || 0,
1565
- onChange(index) {
1566
- setWizard((current) => ({
1567
- ...current,
1568
- mcpClientIndex: index
1569
- }));
1570
- },
1571
- onSelect(client) {
1572
- executeCommand({
1573
- type: 'mcp',
1574
- client: client.key
1575
- });
1576
- },
1577
- onCancel() {
1578
- cancelMcpClientPicker();
1579
- }
1580
- })
1581
- : wizard?.step === 'remove_tasks'
1582
- ? h(TaskRemovePicker, {
1583
- tasks: wizard.tasks,
1584
- selectedIndex: wizard.removeIndex || 0,
1585
- selectedIndexes: wizard.removeSelectedIndexes || [],
1586
- onChange(index) {
1587
- setWizard((current) => ({
1588
- ...current,
1589
- removeIndex: index
1590
- }));
1591
- },
1592
- onToggle(index) {
1593
- setWizard((current) => ({
1594
- ...current,
1595
- removeSelectedIndexes: toggleTaskSelection(current.removeSelectedIndexes || [], index)
1596
- }));
1597
- },
1598
- onSubmit(indexes) {
1599
- confirmRemoveTasks(indexes);
1600
- },
1601
- onCancel() {
1602
- cancelRemoveTasks();
1603
- }
1604
- })
1605
- : wizard?.step === 'edit_task_select'
1606
- ? h(TaskEditPicker, {
1607
- tasks: wizard.tasks,
1608
- selectedIndex: wizard.editIndex || 0,
1609
- onChange(index) {
1610
- setWizard((current) => ({
1611
- ...current,
1612
- editIndex: index
1613
- }));
1614
- },
1615
- onSelect(index) {
1616
- selectTaskForEdit(index);
1617
- },
1618
- onCancel() {
1619
- setWizard((current) => ({
1620
- ...current,
1621
- step: 'edit_tasks'
1622
- }));
1623
- restoreTaskEditorPanel();
1624
- }
1625
- })
1626
- : h(CommandInput, {
1627
- value: inputValue,
1628
- onChange(value) {
1629
- setInputValue(value);
1630
- setSelectedIndex(0);
1631
- },
1632
- onSubmit: submitInput,
1633
- selectedIndex,
1634
- setSelectedIndex,
1635
- mode: wizard?.step === 'edit_tasks' ? 'task' : wizard?.step === 'edit_task_replace' ? 'task-edit' : 'command',
1636
- onEscape() {
1637
- if (!wizard) {
1638
- exit();
1639
- return;
1640
- }
1641
- if (wizard?.step === 'edit_tasks') {
1642
- cancelCurrentLogwork();
1643
- return;
1644
- }
1645
- if (wizard?.step === 'edit_task_replace') {
1646
- setWizard((current) => ({
1647
- ...current,
1648
- step: 'edit_tasks'
1649
- }));
1650
- restoreTaskEditorPanel();
1651
- }
1652
- }
1653
- }));
449
+ renderManualInteraction({
450
+ authPrompt,
451
+ authActive,
452
+ pendingConfirm,
453
+ wizard,
454
+ inputValue,
455
+ selectedIndex,
456
+ setInputValue,
457
+ setSelectedIndex,
458
+ setAuthPrompt,
459
+ setWizard,
460
+ setActivePanel,
461
+ resolveAuthPrompt,
462
+ rejectAuthPrompt,
463
+ resolveConfirm,
464
+ exit,
465
+ executeCommand,
466
+ submitInput,
467
+ startLogworkWizard,
468
+ startNewLogworkWizard,
469
+ selectWizardDate,
470
+ selectWizardProject,
471
+ cancelMcpClientPicker,
472
+ cancelCurrentLogwork,
473
+ restoreTaskEditorPanel,
474
+ resumeManualDraft,
475
+ deleteSavedDraft,
476
+ returnFromDraftPicker,
477
+ confirmRemoveTasks,
478
+ cancelRemoveTasks,
479
+ selectTaskForEdit
480
+ }));
1654
481
  }