logwork-cli 1.2.0 → 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/lib/ui.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as clackPrompts from '@clack/prompts';
2
2
  import { CONFIG } from './config.mjs';
3
+ import { formatSuggestionReason, orderProjectsForPicker } from './repo-project-suggestions.mjs';
3
4
 
4
5
  // Every prompt function takes an optional `{ prompts }` (default: @clack/prompts)
5
6
  // so tests can answer prompts without a terminal.
@@ -28,6 +29,49 @@ export function projectSelectOptions(projects, daySummaries) {
28
29
  });
29
30
  }
30
31
 
32
+ export const SKIP_REPO_PROJECT = '__skip__';
33
+
34
+ // Options for "which RO project does this repo belong to?": suggested projects
35
+ // first (reason as hint), then every other member project, then skip. Values
36
+ // are option keys; resolve them with the returned items.
37
+ export function repoProjectSelectOptions(suggestions = [], candidates = [], { skipLabel = 'Skip for now' } = {}) {
38
+ const items = orderProjectsForPicker(candidates, suggestions);
39
+ const options = items.map((item, index) => ({
40
+ value: `project:${index}`,
41
+ label: item.project.projectName || `project member ${item.project.projectMemberId}`,
42
+ ...(item.suggestion ? { hint: formatSuggestionReason(item.suggestion) } : {})
43
+ }));
44
+ options.push({ value: SKIP_REPO_PROJECT, label: skipLabel });
45
+ return {
46
+ items,
47
+ options,
48
+ initialValue: options[0].value
49
+ };
50
+ }
51
+
52
+ export function repoProjectFromOption(items, value) {
53
+ const match = /^project:(\d+)$/.exec(String(value || ''));
54
+ return match ? items[Number(match[1])]?.project || null : null;
55
+ }
56
+
57
+ // Asks which project a repo belongs to. Returns the chosen project, or null
58
+ // when the user skips (Escape / Ctrl+C also skip).
59
+ export async function chooseRepoProject({ repoName, suggestions = [], candidates = [] }, { prompts = clackPrompts } = {}) {
60
+ const { items, options, initialValue } = repoProjectSelectOptions(suggestions, candidates);
61
+ if (!items.length) {
62
+ return null;
63
+ }
64
+ const value = await prompts.select({
65
+ message: `Which RO project does ${repoName} belong to?`,
66
+ options,
67
+ initialValue
68
+ });
69
+ if (prompts.isCancel(value)) {
70
+ return null;
71
+ }
72
+ return repoProjectFromOption(items, value);
73
+ }
74
+
31
75
  export async function chooseTask(tasks, { prompts = clackPrompts } = {}) {
32
76
  const taskId = handleCancel(await prompts.select({
33
77
  message: 'Chọn loại công việc:',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "logwork-cli",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Log work to Resource Optimiser and Jira from the terminal, an MCP server for AI IDEs, and a terminal UI.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -19,7 +19,12 @@ Goal: every logged line is backed by real work (the user's commits or what the u
19
19
  - Use 0.5h steps.
20
20
  - Do not log weekends or days without a booking unless the user asks.
21
21
  - Work without commits (meetings, reviews, support) only comes from the user; ask, then log it as `Other Activities` or `Code Review & Refactoring`.
22
- 6. **Resolve projects.** The draft's `projectOverrides` already name the project of every resolved line. For `unmappedRepos` (lines with no project), ask the user which RO project the repo belongs to and offer `map_project` with `repos` (and `tickets` if useful), so later drafts resolve it; then run `draft_from_commits` again. A line `log_work` still cannot resolve: ask the user and add its id to `projectOverrides`.
22
+ 6. **Resolve projects.** The draft's `projectOverrides` already name the project of every resolved line. For each repo in `unmappedRepos` (lines with no project):
23
+ - Take the top entry of `repoSuggestions[repo]` (ranked by ticket history, repo/remote name and single booked project; `reasons` say why).
24
+ - Ask one short question per repo, by project name: "Is `<repo name>` part of **<projectName>**? (<first reason>)". With no suggestion, ask which project it belongs to and offer names from `projectCandidates`.
25
+ - On a yes (or the user's own pick), call `map_project` with `projectName` (or that suggestion's `projectMemberId`), `repos: [repo]` and `confirm: true`. Never ask the user for ids.
26
+ - Then run `draft_from_commits` again so those lines get their project and hours.
27
+ A line `log_work` still cannot resolve: ask the user and add its id to `projectOverrides`.
23
28
  7. **Get approval, then write.**
24
29
  - Show the final block to the user.
25
30
  - On approval, call `log_work(text, projectOverrides)` and show its preview summary. Pass `projectOverrides` from the draft unchanged when `draftText` is unchanged. Entry ids are `<date>-<NN>` with NN counting lines through the whole text, so if you add, remove or reorder lines, renumber the overrides to match.