logwork-cli 1.1.1 → 1.3.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/README.md +53 -25
- package/RELEASE.md +4 -2
- package/bin/logwork.mjs +2 -2
- package/commands/commits.mjs +128 -13
- package/commands/history.mjs +0 -4
- package/commands/mcp.mjs +266 -0
- package/commands/quick.mjs +38 -23
- package/commands/reminder.mjs +41 -2
- package/commands/setup.mjs +197 -42
- package/commands/tui.mjs +1 -0
- package/docs/advanced.md +18 -7
- package/docs/mcp-setup.md +120 -26
- package/docs/operations-guide.md +50 -2
- package/docs/recipes.md +40 -0
- package/docs/security.md +23 -2
- package/examples/mcp/antigravity.mcp_config.json +2 -2
- package/examples/mcp/codex.config.toml +2 -2
- package/examples/mcp/copilot.mcp.json +2 -2
- package/lib/api-auth-forms.mjs +393 -0
- package/lib/api-auth-http.mjs +184 -0
- package/lib/api-auth-support.mjs +101 -0
- package/lib/api-auth-tokens.mjs +129 -0
- package/lib/api-auth.mjs +39 -813
- package/lib/api.mjs +38 -1422
- package/lib/apply-ledger.mjs +1 -1
- package/lib/batch-workflow.mjs +187 -2
- package/lib/commit-workflow.mjs +345 -23
- package/lib/config.mjs +7 -0
- package/lib/diagnostics.mjs +116 -4
- package/lib/jira-api.mjs +25 -3
- package/lib/jira-workflow.mjs +36 -6
- package/lib/launcher.mjs +303 -0
- package/lib/logwork-config.mjs +64 -13
- package/lib/manual-apply-state.mjs +15 -0
- package/lib/manual-commands.mjs +2 -2
- package/lib/manual-ink-app.mjs +142 -1282
- package/lib/manual-ink-draft-actions.mjs +227 -0
- package/lib/manual-ink-interaction.mjs +318 -0
- package/lib/manual-ink-session.mjs +41 -0
- package/lib/manual-ink-task-editor.mjs +338 -0
- package/lib/manual-ink-ui.mjs +38 -0
- package/lib/manual-ink-wizard.mjs +520 -0
- package/lib/manual-mcp.mjs +78 -20
- package/lib/manual-repl.mjs +171 -22
- package/lib/mcp-install.mjs +930 -0
- package/lib/mcp-server.mjs +51 -20
- package/lib/node-runtime.mjs +155 -0
- package/lib/paths.mjs +7 -0
- package/lib/project-mapping-workflow.mjs +32 -27
- package/lib/query-workflow.mjs +19 -1
- package/lib/reconciliation-workflow.mjs +1 -1
- package/lib/reminder-config.mjs +38 -2
- package/lib/reminder-scheduler.mjs +85 -18
- package/lib/reminder-service.mjs +42 -3
- package/lib/reminder-workflow.mjs +118 -22
- package/lib/repo-project-suggestions.mjs +285 -0
- package/lib/ro-api-diagnostics.mjs +109 -0
- package/lib/ro-api-entries.mjs +359 -0
- package/lib/ro-api-fields.mjs +290 -0
- package/lib/ro-api-http.mjs +68 -0
- package/lib/ro-api-projects.mjs +137 -0
- package/lib/ro-api-tasks.mjs +50 -0
- package/lib/ro-api-timesheet.mjs +413 -0
- package/lib/ro-delete-workflow.mjs +2 -1
- package/lib/ro-entry-reader.mjs +11 -6
- package/lib/ro-resubmit-workflow.mjs +2 -1
- package/lib/terminal-auth-prompts.mjs +7 -4
- package/lib/ui.mjs +121 -56
- package/package.json +11 -4
- package/skills/logwork/SKILL.md +10 -4
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
// Saved-draft actions for the Ink logwork wizard: save, list, resume, delete.
|
|
2
|
+
import { useCallback } from 'react';
|
|
3
|
+
import {
|
|
4
|
+
buildProjectOptions,
|
|
5
|
+
buildWeekDateOptions,
|
|
6
|
+
formatDraftPreview
|
|
7
|
+
} from './manual-logwork-wizard.mjs';
|
|
8
|
+
import { JIRA_TARGET, RO_TARGET } from './manual-targets.mjs';
|
|
9
|
+
import {
|
|
10
|
+
deleteManualDraft,
|
|
11
|
+
formatManualDraftLabel,
|
|
12
|
+
loadManualDrafts,
|
|
13
|
+
saveManualDraft
|
|
14
|
+
} from './manual-drafts.mjs';
|
|
15
|
+
import { sameProjectIdentity } from './manual-ink-ui.mjs';
|
|
16
|
+
import { clearSessionPreviews } from './manual-ink-session.mjs';
|
|
17
|
+
|
|
18
|
+
export function useDraftActions(ctx) {
|
|
19
|
+
const {
|
|
20
|
+
cwd,
|
|
21
|
+
sessionRef,
|
|
22
|
+
wizard,
|
|
23
|
+
setWizard,
|
|
24
|
+
setActivePanel,
|
|
25
|
+
setLoading,
|
|
26
|
+
inkWorkflows,
|
|
27
|
+
prompts,
|
|
28
|
+
runWithInlineAuth,
|
|
29
|
+
previewDraft,
|
|
30
|
+
startNewLogworkWizard
|
|
31
|
+
} = ctx;
|
|
32
|
+
|
|
33
|
+
const saveCurrentDraft = useCallback(async () => {
|
|
34
|
+
if (wizard?.target === JIRA_TARGET) {
|
|
35
|
+
setActivePanel({
|
|
36
|
+
kind: 'logwork',
|
|
37
|
+
title: 'Save Draft',
|
|
38
|
+
text: 'Saved drafts are only supported for Resource Optimiser logwork in this version.'
|
|
39
|
+
});
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!wizard?.selectedDate || !wizard?.selectedProject || !wizard?.tasks?.length) {
|
|
44
|
+
setActivePanel({
|
|
45
|
+
kind: 'logwork',
|
|
46
|
+
title: 'Save Draft',
|
|
47
|
+
text: 'Add at least one task before saving a draft.'
|
|
48
|
+
});
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const saved = await saveManualDraft({
|
|
54
|
+
id: wizard.activeDraftId,
|
|
55
|
+
cwd,
|
|
56
|
+
date: wizard.selectedDate,
|
|
57
|
+
project: wizard.selectedProject,
|
|
58
|
+
tasks: wizard.tasks,
|
|
59
|
+
latestPreviewStatus: wizard.preview?.status
|
|
60
|
+
}, { cwd });
|
|
61
|
+
setWizard((current) => ({
|
|
62
|
+
...current,
|
|
63
|
+
activeDraftId: saved.id
|
|
64
|
+
}));
|
|
65
|
+
setActivePanel({
|
|
66
|
+
kind: 'success',
|
|
67
|
+
title: 'Draft Saved',
|
|
68
|
+
text: [
|
|
69
|
+
`Saved ${saved.tasks.length} task${saved.tasks.length === 1 ? '' : 's'} locally.`,
|
|
70
|
+
formatManualDraftLabel(saved),
|
|
71
|
+
'',
|
|
72
|
+
'Continue editing, press empty Enter to apply, or type /drafts.'
|
|
73
|
+
].join('\n')
|
|
74
|
+
});
|
|
75
|
+
return saved;
|
|
76
|
+
} catch (error) {
|
|
77
|
+
setActivePanel({
|
|
78
|
+
kind: 'error',
|
|
79
|
+
title: 'Save Draft Error',
|
|
80
|
+
text: error.message
|
|
81
|
+
});
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}, [cwd, wizard]);
|
|
85
|
+
|
|
86
|
+
const openDraftPicker = useCallback(async (source = 'editor') => {
|
|
87
|
+
setLoading('Loading saved drafts...');
|
|
88
|
+
try {
|
|
89
|
+
const drafts = await loadManualDrafts({ cwd });
|
|
90
|
+
if (!drafts.length) {
|
|
91
|
+
setActivePanel({
|
|
92
|
+
kind: 'logwork',
|
|
93
|
+
title: 'Saved Drafts',
|
|
94
|
+
text: 'No saved drafts found.'
|
|
95
|
+
});
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
setWizard((current) => ({
|
|
99
|
+
...current,
|
|
100
|
+
step: 'select_draft',
|
|
101
|
+
drafts,
|
|
102
|
+
draftIndex: 0,
|
|
103
|
+
draftSource: source
|
|
104
|
+
}));
|
|
105
|
+
setActivePanel({
|
|
106
|
+
kind: 'logwork',
|
|
107
|
+
title: 'Saved Drafts',
|
|
108
|
+
text: 'Enter resumes · d deletes · Esc returns.'
|
|
109
|
+
});
|
|
110
|
+
} catch (error) {
|
|
111
|
+
setActivePanel({
|
|
112
|
+
kind: 'error',
|
|
113
|
+
title: 'Draft Error',
|
|
114
|
+
text: error.message
|
|
115
|
+
});
|
|
116
|
+
} finally {
|
|
117
|
+
setLoading('');
|
|
118
|
+
}
|
|
119
|
+
}, [cwd]);
|
|
120
|
+
|
|
121
|
+
const resumeManualDraft = useCallback(async (draft) => {
|
|
122
|
+
setLoading('Resuming saved draft...');
|
|
123
|
+
try {
|
|
124
|
+
const weekly = await runWithInlineAuth(() => inkWorkflows.queryLogwork({
|
|
125
|
+
period: 'this_week',
|
|
126
|
+
cwd,
|
|
127
|
+
includeEntries: false
|
|
128
|
+
}));
|
|
129
|
+
const dateOptions = buildWeekDateOptions(weekly);
|
|
130
|
+
const projectsResult = await runWithInlineAuth(() => inkWorkflows.listLogworkProjects({ cwd }));
|
|
131
|
+
const projectOptions = buildProjectOptions({
|
|
132
|
+
projectsResult,
|
|
133
|
+
weekly,
|
|
134
|
+
date: draft.date
|
|
135
|
+
});
|
|
136
|
+
const selectedProject = projectOptions.find((project) => sameProjectIdentity(project, draft.project)) || draft.project;
|
|
137
|
+
const nextWizard = {
|
|
138
|
+
step: 'edit_tasks',
|
|
139
|
+
target: RO_TARGET,
|
|
140
|
+
weekly,
|
|
141
|
+
dateOptions,
|
|
142
|
+
dateIndex: Math.max(0, dateOptions.findIndex((option) => option.date === draft.date)),
|
|
143
|
+
projectsResult,
|
|
144
|
+
projectOptions,
|
|
145
|
+
projectIndex: Math.max(0, projectOptions.findIndex((project) => sameProjectIdentity(project, selectedProject))),
|
|
146
|
+
selectedDate: draft.date,
|
|
147
|
+
selectedProject,
|
|
148
|
+
tasks: draft.tasks,
|
|
149
|
+
preview: null,
|
|
150
|
+
activeDraftId: draft.id
|
|
151
|
+
};
|
|
152
|
+
sessionRef.current.logworkDraft = {
|
|
153
|
+
target: RO_TARGET,
|
|
154
|
+
date: draft.date,
|
|
155
|
+
project: selectedProject,
|
|
156
|
+
tasks: draft.tasks,
|
|
157
|
+
preview: null
|
|
158
|
+
};
|
|
159
|
+
clearSessionPreviews(sessionRef.current);
|
|
160
|
+
sessionRef.current.activeTarget = RO_TARGET;
|
|
161
|
+
await previewDraft(nextWizard);
|
|
162
|
+
} catch (error) {
|
|
163
|
+
setActivePanel({
|
|
164
|
+
kind: 'error',
|
|
165
|
+
title: 'Resume Draft Error',
|
|
166
|
+
text: error.message
|
|
167
|
+
});
|
|
168
|
+
} finally {
|
|
169
|
+
setLoading('');
|
|
170
|
+
}
|
|
171
|
+
}, [cwd, inkWorkflows, previewDraft, runWithInlineAuth]);
|
|
172
|
+
|
|
173
|
+
const deleteSavedDraft = useCallback(async (draft) => {
|
|
174
|
+
const ok = await prompts.confirm(`Delete saved draft "${formatManualDraftLabel(draft)}"?`, false);
|
|
175
|
+
if (!ok) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
await deleteManualDraft(draft.id);
|
|
179
|
+
const drafts = await loadManualDrafts({ cwd });
|
|
180
|
+
setWizard((current) => ({
|
|
181
|
+
...current,
|
|
182
|
+
drafts,
|
|
183
|
+
draftIndex: 0
|
|
184
|
+
}));
|
|
185
|
+
setActivePanel({
|
|
186
|
+
kind: 'success',
|
|
187
|
+
title: 'Draft Deleted',
|
|
188
|
+
text: drafts.length ? 'Draft deleted. Choose another saved draft or start new.' : 'Draft deleted. No saved drafts remain.'
|
|
189
|
+
});
|
|
190
|
+
}, [cwd, prompts]);
|
|
191
|
+
|
|
192
|
+
const returnFromDraftPicker = useCallback(() => {
|
|
193
|
+
if (wizard?.draftSource === 'start') {
|
|
194
|
+
startNewLogworkWizard(RO_TARGET);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
setWizard((current) => ({
|
|
198
|
+
...current,
|
|
199
|
+
step: 'edit_tasks',
|
|
200
|
+
draftSource: undefined
|
|
201
|
+
}));
|
|
202
|
+
const draft = {
|
|
203
|
+
target: wizard?.target || RO_TARGET,
|
|
204
|
+
date: wizard?.selectedDate,
|
|
205
|
+
project: wizard?.selectedProject,
|
|
206
|
+
tasks: wizard?.tasks || [],
|
|
207
|
+
preview: wizard?.preview
|
|
208
|
+
};
|
|
209
|
+
setActivePanel({
|
|
210
|
+
kind: 'logwork',
|
|
211
|
+
title: wizard?.preview ? 'Logwork Preview' : 'Logwork Draft',
|
|
212
|
+
text: [
|
|
213
|
+
formatDraftPreview(draft),
|
|
214
|
+
'',
|
|
215
|
+
'Type / for task commands. Empty Enter applies when ready.'
|
|
216
|
+
].join('\n')
|
|
217
|
+
});
|
|
218
|
+
}, [startNewLogworkWizard, wizard]);
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
saveCurrentDraft,
|
|
222
|
+
openDraftPicker,
|
|
223
|
+
resumeManualDraft,
|
|
224
|
+
deleteSavedDraft,
|
|
225
|
+
returnFromDraftPicker
|
|
226
|
+
};
|
|
227
|
+
}
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
// The bottom interaction area of the Ink app: the auth prompt, a confirm dialog,
|
|
2
|
+
// the active wizard picker, or the command input. A plain render function (not a
|
|
3
|
+
// component) so the element tree matches the single-component layout.
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { toggleTaskSelection } from './manual-logwork-wizard.mjs';
|
|
6
|
+
import { AUTH_TARGETS, LOGWORK_TARGETS, RO_TARGET } from './manual-targets.mjs';
|
|
7
|
+
import {
|
|
8
|
+
CommandInput,
|
|
9
|
+
ConfirmDialog,
|
|
10
|
+
DatePicker,
|
|
11
|
+
DraftPicker,
|
|
12
|
+
McpClientPicker,
|
|
13
|
+
ProjectPicker,
|
|
14
|
+
SelectPrompt,
|
|
15
|
+
TargetPicker,
|
|
16
|
+
TaskEditPicker,
|
|
17
|
+
TaskRemovePicker
|
|
18
|
+
} from './manual-ink-ui.mjs';
|
|
19
|
+
import { AuthPrompt } from './manual-ink-auth.mjs';
|
|
20
|
+
import { RECONCILIATION_PERIOD_OPTIONS } from './manual-ink-session.mjs';
|
|
21
|
+
|
|
22
|
+
const h = React.createElement;
|
|
23
|
+
|
|
24
|
+
export function renderManualInteraction({
|
|
25
|
+
authPrompt,
|
|
26
|
+
authActive,
|
|
27
|
+
pendingConfirm,
|
|
28
|
+
pendingSelect,
|
|
29
|
+
wizard,
|
|
30
|
+
inputValue,
|
|
31
|
+
selectedIndex,
|
|
32
|
+
setInputValue,
|
|
33
|
+
setSelectedIndex,
|
|
34
|
+
setAuthPrompt,
|
|
35
|
+
setWizard,
|
|
36
|
+
setActivePanel,
|
|
37
|
+
resolveAuthPrompt,
|
|
38
|
+
rejectAuthPrompt,
|
|
39
|
+
resolveConfirm,
|
|
40
|
+
changeSelect,
|
|
41
|
+
resolveSelect,
|
|
42
|
+
exit,
|
|
43
|
+
executeCommand,
|
|
44
|
+
submitInput,
|
|
45
|
+
startLogworkWizard,
|
|
46
|
+
startNewLogworkWizard,
|
|
47
|
+
selectWizardDate,
|
|
48
|
+
selectWizardProject,
|
|
49
|
+
cancelMcpClientPicker,
|
|
50
|
+
cancelCurrentLogwork,
|
|
51
|
+
restoreTaskEditorPanel,
|
|
52
|
+
resumeManualDraft,
|
|
53
|
+
deleteSavedDraft,
|
|
54
|
+
returnFromDraftPicker,
|
|
55
|
+
confirmRemoveTasks,
|
|
56
|
+
cancelRemoveTasks,
|
|
57
|
+
selectTaskForEdit
|
|
58
|
+
}) {
|
|
59
|
+
return authPrompt
|
|
60
|
+
? h(AuthPrompt, {
|
|
61
|
+
prompt: authPrompt,
|
|
62
|
+
inputValue,
|
|
63
|
+
onInputChange(value) {
|
|
64
|
+
setInputValue(value);
|
|
65
|
+
},
|
|
66
|
+
onPromptChange(nextPrompt) {
|
|
67
|
+
setAuthPrompt(nextPrompt);
|
|
68
|
+
},
|
|
69
|
+
onResolve: resolveAuthPrompt,
|
|
70
|
+
onReject: rejectAuthPrompt
|
|
71
|
+
})
|
|
72
|
+
: authActive
|
|
73
|
+
? h(AuthPrompt, {
|
|
74
|
+
prompt: { step: 'working' },
|
|
75
|
+
inputValue,
|
|
76
|
+
onInputChange(value) {
|
|
77
|
+
setInputValue(value);
|
|
78
|
+
},
|
|
79
|
+
onPromptChange(nextPrompt) {
|
|
80
|
+
setAuthPrompt(nextPrompt);
|
|
81
|
+
},
|
|
82
|
+
onResolve: resolveAuthPrompt,
|
|
83
|
+
onReject: rejectAuthPrompt
|
|
84
|
+
})
|
|
85
|
+
: pendingConfirm
|
|
86
|
+
? h(ConfirmDialog, {
|
|
87
|
+
message: pendingConfirm.message,
|
|
88
|
+
initialValue: pendingConfirm.initialValue,
|
|
89
|
+
onResolve: resolveConfirm
|
|
90
|
+
})
|
|
91
|
+
: pendingSelect
|
|
92
|
+
? h(SelectPrompt, {
|
|
93
|
+
message: pendingSelect.message,
|
|
94
|
+
options: pendingSelect.options,
|
|
95
|
+
selectedIndex: pendingSelect.selectedIndex || 0,
|
|
96
|
+
onChange: changeSelect,
|
|
97
|
+
onResolve: resolveSelect
|
|
98
|
+
})
|
|
99
|
+
: wizard?.step === 'select_logwork_target'
|
|
100
|
+
? h(TargetPicker, {
|
|
101
|
+
title: 'Pick logwork target',
|
|
102
|
+
targets: LOGWORK_TARGETS,
|
|
103
|
+
selectedIndex: wizard.targetIndex || 0,
|
|
104
|
+
onChange(index) {
|
|
105
|
+
setWizard((current) => ({
|
|
106
|
+
...current,
|
|
107
|
+
targetIndex: index
|
|
108
|
+
}));
|
|
109
|
+
},
|
|
110
|
+
onSelect(target) {
|
|
111
|
+
startLogworkWizard(target.key);
|
|
112
|
+
},
|
|
113
|
+
onCancel() {
|
|
114
|
+
setWizard(null);
|
|
115
|
+
setActivePanel({
|
|
116
|
+
kind: 'idle',
|
|
117
|
+
title: 'Ready',
|
|
118
|
+
text: 'Type / for commands.'
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
: wizard?.step === 'select_auth_target'
|
|
123
|
+
? h(TargetPicker, {
|
|
124
|
+
title: 'Pick auth target',
|
|
125
|
+
targets: AUTH_TARGETS,
|
|
126
|
+
selectedIndex: wizard.targetIndex || 0,
|
|
127
|
+
onChange(index) {
|
|
128
|
+
setWizard((current) => ({
|
|
129
|
+
...current,
|
|
130
|
+
targetIndex: index
|
|
131
|
+
}));
|
|
132
|
+
},
|
|
133
|
+
onSelect(target) {
|
|
134
|
+
executeCommand({
|
|
135
|
+
type: 'auth',
|
|
136
|
+
target: target.key
|
|
137
|
+
});
|
|
138
|
+
},
|
|
139
|
+
onCancel() {
|
|
140
|
+
setWizard(null);
|
|
141
|
+
setActivePanel({
|
|
142
|
+
kind: 'idle',
|
|
143
|
+
title: 'Ready',
|
|
144
|
+
text: 'Type / for commands.'
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
: wizard?.step === 'select_reconciliation_period'
|
|
149
|
+
? h(TargetPicker, {
|
|
150
|
+
title: 'Pick reconciliation period',
|
|
151
|
+
targets: RECONCILIATION_PERIOD_OPTIONS,
|
|
152
|
+
selectedIndex: wizard.periodIndex || 0,
|
|
153
|
+
onChange(index) {
|
|
154
|
+
setWizard((current) => ({
|
|
155
|
+
...current,
|
|
156
|
+
periodIndex: index
|
|
157
|
+
}));
|
|
158
|
+
},
|
|
159
|
+
onSelect(period) {
|
|
160
|
+
executeCommand({
|
|
161
|
+
type: 'reconcile',
|
|
162
|
+
period: period.key
|
|
163
|
+
});
|
|
164
|
+
},
|
|
165
|
+
onCancel() {
|
|
166
|
+
setWizard(null);
|
|
167
|
+
setActivePanel({
|
|
168
|
+
kind: 'idle',
|
|
169
|
+
title: 'Ready',
|
|
170
|
+
text: 'Type / for commands.'
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
})
|
|
174
|
+
: wizard?.step === 'select_draft'
|
|
175
|
+
? h(DraftPicker, {
|
|
176
|
+
drafts: wizard.drafts || [],
|
|
177
|
+
selectedIndex: wizard.draftIndex || 0,
|
|
178
|
+
includeStartNew: wizard.draftSource === 'start',
|
|
179
|
+
onChange(index) {
|
|
180
|
+
setWizard((current) => ({
|
|
181
|
+
...current,
|
|
182
|
+
draftIndex: index
|
|
183
|
+
}));
|
|
184
|
+
},
|
|
185
|
+
onResume(draft) {
|
|
186
|
+
resumeManualDraft(draft);
|
|
187
|
+
},
|
|
188
|
+
onDelete(draft) {
|
|
189
|
+
deleteSavedDraft(draft);
|
|
190
|
+
},
|
|
191
|
+
onStartNew() {
|
|
192
|
+
startNewLogworkWizard(RO_TARGET);
|
|
193
|
+
},
|
|
194
|
+
onCancel() {
|
|
195
|
+
returnFromDraftPicker();
|
|
196
|
+
}
|
|
197
|
+
})
|
|
198
|
+
: wizard?.step === 'select_date'
|
|
199
|
+
? h(DatePicker, {
|
|
200
|
+
options: wizard.dateOptions,
|
|
201
|
+
selectedIndex: wizard.dateIndex,
|
|
202
|
+
onChange(index) {
|
|
203
|
+
setWizard((current) => ({
|
|
204
|
+
...current,
|
|
205
|
+
dateIndex: index
|
|
206
|
+
}));
|
|
207
|
+
},
|
|
208
|
+
onSelect(option) {
|
|
209
|
+
selectWizardDate(option);
|
|
210
|
+
}
|
|
211
|
+
})
|
|
212
|
+
: wizard?.step === 'select_project'
|
|
213
|
+
? h(ProjectPicker, {
|
|
214
|
+
options: wizard.projectOptions,
|
|
215
|
+
selectedIndex: wizard.projectIndex,
|
|
216
|
+
onChange(index) {
|
|
217
|
+
setWizard((current) => ({
|
|
218
|
+
...current,
|
|
219
|
+
projectIndex: index
|
|
220
|
+
}));
|
|
221
|
+
},
|
|
222
|
+
onSelect(project) {
|
|
223
|
+
selectWizardProject(project);
|
|
224
|
+
}
|
|
225
|
+
})
|
|
226
|
+
: wizard?.step === 'select_mcp_client'
|
|
227
|
+
? h(McpClientPicker, {
|
|
228
|
+
selectedIndex: wizard.mcpClientIndex || 0,
|
|
229
|
+
onChange(index) {
|
|
230
|
+
setWizard((current) => ({
|
|
231
|
+
...current,
|
|
232
|
+
mcpClientIndex: index
|
|
233
|
+
}));
|
|
234
|
+
},
|
|
235
|
+
onSelect(client) {
|
|
236
|
+
executeCommand({
|
|
237
|
+
type: 'mcp',
|
|
238
|
+
client: client.key
|
|
239
|
+
});
|
|
240
|
+
},
|
|
241
|
+
onCancel() {
|
|
242
|
+
cancelMcpClientPicker();
|
|
243
|
+
}
|
|
244
|
+
})
|
|
245
|
+
: wizard?.step === 'remove_tasks'
|
|
246
|
+
? h(TaskRemovePicker, {
|
|
247
|
+
tasks: wizard.tasks,
|
|
248
|
+
selectedIndex: wizard.removeIndex || 0,
|
|
249
|
+
selectedIndexes: wizard.removeSelectedIndexes || [],
|
|
250
|
+
onChange(index) {
|
|
251
|
+
setWizard((current) => ({
|
|
252
|
+
...current,
|
|
253
|
+
removeIndex: index
|
|
254
|
+
}));
|
|
255
|
+
},
|
|
256
|
+
onToggle(index) {
|
|
257
|
+
setWizard((current) => ({
|
|
258
|
+
...current,
|
|
259
|
+
removeSelectedIndexes: toggleTaskSelection(current.removeSelectedIndexes || [], index)
|
|
260
|
+
}));
|
|
261
|
+
},
|
|
262
|
+
onSubmit(indexes) {
|
|
263
|
+
confirmRemoveTasks(indexes);
|
|
264
|
+
},
|
|
265
|
+
onCancel() {
|
|
266
|
+
cancelRemoveTasks();
|
|
267
|
+
}
|
|
268
|
+
})
|
|
269
|
+
: wizard?.step === 'edit_task_select'
|
|
270
|
+
? h(TaskEditPicker, {
|
|
271
|
+
tasks: wizard.tasks,
|
|
272
|
+
selectedIndex: wizard.editIndex || 0,
|
|
273
|
+
onChange(index) {
|
|
274
|
+
setWizard((current) => ({
|
|
275
|
+
...current,
|
|
276
|
+
editIndex: index
|
|
277
|
+
}));
|
|
278
|
+
},
|
|
279
|
+
onSelect(index) {
|
|
280
|
+
selectTaskForEdit(index);
|
|
281
|
+
},
|
|
282
|
+
onCancel() {
|
|
283
|
+
setWizard((current) => ({
|
|
284
|
+
...current,
|
|
285
|
+
step: 'edit_tasks'
|
|
286
|
+
}));
|
|
287
|
+
restoreTaskEditorPanel();
|
|
288
|
+
}
|
|
289
|
+
})
|
|
290
|
+
: h(CommandInput, {
|
|
291
|
+
value: inputValue,
|
|
292
|
+
onChange(value) {
|
|
293
|
+
setInputValue(value);
|
|
294
|
+
setSelectedIndex(0);
|
|
295
|
+
},
|
|
296
|
+
onSubmit: submitInput,
|
|
297
|
+
selectedIndex,
|
|
298
|
+
setSelectedIndex,
|
|
299
|
+
mode: wizard?.step === 'edit_tasks' ? 'task' : wizard?.step === 'edit_task_replace' ? 'task-edit' : 'command',
|
|
300
|
+
onEscape() {
|
|
301
|
+
if (!wizard) {
|
|
302
|
+
exit();
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if (wizard?.step === 'edit_tasks') {
|
|
306
|
+
cancelCurrentLogwork();
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if (wizard?.step === 'edit_task_replace') {
|
|
310
|
+
setWizard((current) => ({
|
|
311
|
+
...current,
|
|
312
|
+
step: 'edit_tasks'
|
|
313
|
+
}));
|
|
314
|
+
restoreTaskEditorPanel();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Manual session preview bookkeeping shared by the Ink app and its action hooks.
|
|
2
|
+
import { JIRA_TARGET, RO_TARGET } from './manual-targets.mjs';
|
|
3
|
+
|
|
4
|
+
export const RECONCILIATION_PERIOD_OPTIONS = [
|
|
5
|
+
{ key: 'today', label: 'Today', description: 'Compare today' },
|
|
6
|
+
{ key: 'yesterday', label: 'Yesterday', description: 'Compare yesterday' },
|
|
7
|
+
{ key: 'this_week', label: 'This week', description: 'Compare Monday through Sunday' },
|
|
8
|
+
{ key: 'last_week', label: 'Last week', description: 'Compare the previous week' },
|
|
9
|
+
{ key: 'this_month', label: 'This month', description: 'Compare the current month' },
|
|
10
|
+
{ key: 'last_month', label: 'Last month', description: 'Compare the previous month' }
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
export function setSessionPreview(session, target, preview) {
|
|
14
|
+
session.activeTarget = target;
|
|
15
|
+
session.previews = {
|
|
16
|
+
...(session.previews || {}),
|
|
17
|
+
[target]: preview
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function clearSessionPreviews(session) {
|
|
22
|
+
session.previews = {
|
|
23
|
+
[RO_TARGET]: null,
|
|
24
|
+
[JIRA_TARGET]: null
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function sessionPreview(session, target) {
|
|
29
|
+
return session.previews?.[target] || null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function hasSessionPreview(session) {
|
|
33
|
+
return Boolean(session.previews?.[RO_TARGET] || session.previews?.[JIRA_TARGET]);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function activeSessionPreview(session) {
|
|
37
|
+
if (session.activeTarget === JIRA_TARGET) {
|
|
38
|
+
return sessionPreview(session, JIRA_TARGET);
|
|
39
|
+
}
|
|
40
|
+
return sessionPreview(session, RO_TARGET);
|
|
41
|
+
}
|