logwork-cli 0.0.0-stage → 1.1.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 (96) hide show
  1. package/LICENSE +11 -0
  2. package/README.md +330 -2
  3. package/RELEASE.md +89 -0
  4. package/bin/logwork.mjs +104 -0
  5. package/commands/auth.mjs +227 -0
  6. package/commands/commits.mjs +82 -0
  7. package/commands/delete.mjs +69 -0
  8. package/commands/doctor.mjs +24 -0
  9. package/commands/edit.mjs +88 -0
  10. package/commands/history.mjs +62 -0
  11. package/commands/query.mjs +52 -0
  12. package/commands/quick.mjs +163 -0
  13. package/commands/reconcile.mjs +55 -0
  14. package/commands/release.mjs +77 -0
  15. package/commands/reminder.mjs +172 -0
  16. package/commands/setup.mjs +508 -0
  17. package/commands/skill.mjs +91 -0
  18. package/commands/tui.mjs +50 -0
  19. package/commands/update.mjs +161 -0
  20. package/docs/advanced.md +334 -0
  21. package/docs/mcp-setup.md +368 -0
  22. package/docs/operations-guide.md +587 -0
  23. package/docs/recipes.md +259 -0
  24. package/docs/security.md +151 -0
  25. package/examples/mcp/antigravity.mcp_config.json +8 -0
  26. package/examples/mcp/codex.config.toml +5 -0
  27. package/examples/mcp/copilot.mcp.json +9 -0
  28. package/lib/agent-skill.mjs +16 -0
  29. package/lib/api-auth.mjs +1259 -0
  30. package/lib/api.mjs +1422 -0
  31. package/lib/apply-ledger.mjs +227 -0
  32. package/lib/atomic-file.mjs +24 -0
  33. package/lib/auth-diagnostics.mjs +113 -0
  34. package/lib/auth-errors.mjs +22 -0
  35. package/lib/auth-redaction.mjs +78 -0
  36. package/lib/auth-terminal.mjs +97 -0
  37. package/lib/auth.mjs +323 -0
  38. package/lib/batch-parser.mjs +145 -0
  39. package/lib/batch-workflow.mjs +842 -0
  40. package/lib/commit-workflow.mjs +402 -0
  41. package/lib/config.mjs +191 -0
  42. package/lib/credential-store.mjs +130 -0
  43. package/lib/diagnostics.mjs +307 -0
  44. package/lib/entrypoint.mjs +14 -0
  45. package/lib/file-lock.mjs +44 -0
  46. package/lib/http.mjs +125 -0
  47. package/lib/jira-api.mjs +258 -0
  48. package/lib/jira-auth.mjs +203 -0
  49. package/lib/jira-workflow.mjs +523 -0
  50. package/lib/keychain.mjs +105 -0
  51. package/lib/legacy-migration.mjs +66 -0
  52. package/lib/list-navigation.mjs +19 -0
  53. package/lib/logwork-config.mjs +199 -0
  54. package/lib/logwork-f03-enrich.mjs +41 -0
  55. package/lib/macos.mjs +33 -0
  56. package/lib/manual-apply-state.mjs +35 -0
  57. package/lib/manual-commands.mjs +294 -0
  58. package/lib/manual-drafts.mjs +154 -0
  59. package/lib/manual-ink-app.mjs +1654 -0
  60. package/lib/manual-ink-auth.mjs +349 -0
  61. package/lib/manual-ink-ui.mjs +675 -0
  62. package/lib/manual-input.mjs +21 -0
  63. package/lib/manual-logwork-wizard.mjs +310 -0
  64. package/lib/manual-mcp.mjs +187 -0
  65. package/lib/manual-repl.mjs +1037 -0
  66. package/lib/manual-targets.mjs +78 -0
  67. package/lib/mcp-server.mjs +833 -0
  68. package/lib/os-notification.mjs +105 -0
  69. package/lib/package-info.mjs +17 -0
  70. package/lib/paths.mjs +68 -0
  71. package/lib/project-identity.mjs +66 -0
  72. package/lib/project-mapping-workflow.mjs +309 -0
  73. package/lib/project-resolver.mjs +373 -0
  74. package/lib/query-workflow.mjs +553 -0
  75. package/lib/reconciliation-workflow.mjs +446 -0
  76. package/lib/release-doctor.mjs +448 -0
  77. package/lib/reminder-config.mjs +71 -0
  78. package/lib/reminder-scheduler.mjs +301 -0
  79. package/lib/reminder-service.mjs +118 -0
  80. package/lib/reminder-state.mjs +38 -0
  81. package/lib/reminder-workflow.mjs +299 -0
  82. package/lib/ro-delete-args.mjs +38 -0
  83. package/lib/ro-delete-workflow.mjs +115 -0
  84. package/lib/ro-edit-args.mjs +142 -0
  85. package/lib/ro-edit-workflow.mjs +632 -0
  86. package/lib/ro-entry-reader.mjs +56 -0
  87. package/lib/ro-resubmit-workflow.mjs +134 -0
  88. package/lib/terminal-auth-prompts.mjs +120 -0
  89. package/lib/ui.mjs +133 -0
  90. package/lib/update-service.mjs +350 -0
  91. package/lib/util.mjs +66 -0
  92. package/lib/windows-credential-manager.mjs +260 -0
  93. package/lib/worklog-task-resolver.mjs +113 -0
  94. package/package.json +73 -4
  95. package/setup.sh +22 -0
  96. package/skills/logwork/SKILL.md +43 -0
@@ -0,0 +1,368 @@
1
+ # MCP Setup
2
+
3
+ `logwork setup` prints copy-ready configs that use your real path. You can also open the terminal UI with `logwork` and type `/mcp`, or `/mcp <client>` for a single client.
4
+
5
+ For the full RO/Jira/Both processing model, mapping, reconciliation, history, reminders and updates, see the [operations guide](operations-guide.md).
6
+
7
+ Every client runs `node <home>/.logwork-cli/bin/logwork.mjs mcp` under the server name `logwork`.
8
+
9
+ The config uses an absolute `node` entry path instead of the `logwork` command. GUI IDEs often start without the npm global bin directory on `PATH`, so the bare command may not be found.
10
+
11
+ The examples use `/Users/<user>` as a macOS placeholder. On Windows, use the path printed by `logwork setup` or `/mcp`, usually `C:\\Users\\<user>\\.logwork-cli\\bin\\logwork.mjs` inside JSON or TOML strings.
12
+
13
+ ## Cursor / Google Antigravity
14
+
15
+ Cursor MCP settings, or `~/.gemini/antigravity/mcp_config.json`:
16
+
17
+ ```json
18
+ {
19
+ "mcpServers": {
20
+ "logwork": {
21
+ "command": "node",
22
+ "args": ["/Users/<user>/.logwork-cli/bin/logwork.mjs", "mcp"]
23
+ }
24
+ }
25
+ }
26
+ ```
27
+
28
+ ## Codex
29
+
30
+ `~/.codex/config.toml`, or project `.codex/config.toml`:
31
+
32
+ ```toml
33
+ [mcp_servers.logwork]
34
+ command = "node"
35
+ args = ["/Users/<user>/.logwork-cli/bin/logwork.mjs", "mcp"]
36
+ startup_timeout_sec = 20
37
+ tool_timeout_sec = 120
38
+ ```
39
+
40
+ ## GitHub Copilot / VS Code
41
+
42
+ Workspace `.vscode/mcp.json`, or your VS Code user MCP config:
43
+
44
+ ```json
45
+ {
46
+ "servers": {
47
+ "logwork": {
48
+ "type": "stdio",
49
+ "command": "node",
50
+ "args": ["/Users/<user>/.logwork-cli/bin/logwork.mjs", "mcp"]
51
+ }
52
+ }
53
+ }
54
+ ```
55
+
56
+ ## Claude Code
57
+
58
+ ```bash
59
+ claude mcp add --transport stdio logwork -- node "/Users/<user>/.logwork-cli/bin/logwork.mjs" mcp
60
+ ```
61
+
62
+ From Windows PowerShell:
63
+
64
+ ```powershell
65
+ claude mcp add --transport stdio logwork -- node 'C:\Users\<user>\.logwork-cli\bin\logwork.mjs' mcp
66
+ ```
67
+
68
+ Or add the Cursor JSON above to the project `.mcp.json`.
69
+
70
+ Templates are also in [examples/mcp](../examples/mcp/).
71
+
72
+ If you used `logwork-helper` before, delete its old MCP entry. It points at `~/.logwork-helper/mcp-server.mjs`, which is the old runtime.
73
+
74
+ ## Exposed Tools
75
+
76
+ After editing the config, restart or reload the IDE. The server exposes 16 tools and one prompt. Each one has a title, a description, a described input schema, and MCP annotations (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`), so IDEs and agents can tell read-only tools from writes.
77
+
78
+ The server also sends MCP `instructions` that explain the approval rules, the line format and the recommended call order. Most clients pass these to the model automatically.
79
+
80
+ **Writes are two calls to the same tool:**
81
+
82
+ 1. Call the tool **without `confirm`**. It returns a preview: a `batchId` (for `log_work` and `log_jira_work`) or a `previewId` (for the entry tools), plus a `summary`. Show the summary to the user.
83
+ 2. After explicit approval, call the same tool **again with only that id and `confirm: true`**.
84
+
85
+ The apply call writes the cached, approved preview as-is. If the apply call also carries content fields (such as `text`, `hours` or `description`), it is rejected rather than silently ignored. To change values, preview again.
86
+
87
+ Previews expire after 1 hour. They are also lost when the server restarts.
88
+
89
+ | Group | Tool | Kind | Purpose |
90
+ |---|---|---|---|
91
+ | Read | `get_logwork` | read-only | Booked vs logged RO hours by day and project, with entries (`logworkId`, worklog task, description, status). |
92
+ | Read | `draft_from_commits` | read-only | Your git commits (all branches, your author) for a period in the given `repos`, grouped per day into draft lines with a suggested worklog task and hours that fill the RO remaining booked time. Returns `draftText` for `log_work`. |
93
+ | Read | `list_projects` | read-only | RO memberships (`projectMemberId`, `projectId`, `projectName`) and local mappings. |
94
+ | Read | `list_tasks` | read-only | RO worklog task catalog for one `projectId`: id, name, type of work, and when to use each task. |
95
+ | Read | `get_jira_issue` | read-only | Jira issue key, summary and status. |
96
+ | Read | `reconcile` | read-only | Compare RO and Jira hours by day. Never writes. |
97
+ | Read | `get_history` | read-only (local) | Local apply history. Contains no credentials. |
98
+ | RO write | `log_work` | two-call write | Create new RO entries from a weekly block (worklog task + description). |
99
+ | RO write | `edit_entry` | two-call, destructive | Change hours, worklog task, type of work or description of a submitted or approved entry. Apply replaces the entry. |
100
+ | RO write | `resubmit_entry` | two-call write | Fix and resubmit a rejected entry (hours, type, description, note to PM). |
101
+ | RO write | `delete_entry` | two-call, destructive | Delete a submitted or approved entry. |
102
+ | Jira write | `log_jira_work` | two-call write | Create Jira worklogs. The description becomes the worklog comment. |
103
+ | Config | `map_project` | local write, `confirm` | Map ticket prefixes or keywords to an RO project (`.logwork.json`). |
104
+ | Config | `reminder` | `action`: status / enable / disable / test | Native OS reminder. Every action except `status` needs `confirm: true`. |
105
+ | Config | `update` | `action`: check / install | npm self-update. `install` needs `confirm: true`. |
106
+ | Auth | `login` | local | `target: "ro"` (default) opens or explains a terminal login. `target: "jira"` explains how to save a PAT. Never accepts credentials. |
107
+
108
+ ### Tool Name Changes From logwork-helper
109
+
110
+ | logwork-helper (before 0.3.0) | logwork-cli |
111
+ |---|---|
112
+ | `preview_logwork_batch`, `apply_logwork_batch` | `log_work` |
113
+ | `query_logwork` | `get_logwork` |
114
+ | `list_logwork_projects` | `list_projects` |
115
+ | `list_worklog_tasks` | `list_tasks` |
116
+ | `preview_ro_logwork_edit`, `apply_ro_logwork_edit` | `edit_entry` |
117
+ | `preview_ro_logwork_resubmit`, `apply_ro_logwork_resubmit` | `resubmit_entry` |
118
+ | `preview_ro_logwork_delete`, `apply_ro_logwork_delete`, `delete_ro_logwork_entry` | `delete_entry` |
119
+ | `preview_jira_worklog_batch`, `apply_jira_worklog_batch` | `log_jira_work` |
120
+ | `reconcile_logwork` | `reconcile` |
121
+ | `query_apply_history` | `get_history` |
122
+ | `upsert_project_mapping` | `map_project` |
123
+ | `start_auth_login`, `start_jira_auth` | `login` (`target: "ro"` or `"jira"`) |
124
+ | `get_logwork_reminder`, `configure_logwork_reminder`, `test_logwork_reminder` | `reminder` (`action`) |
125
+ | `check_for_updates`, `apply_update` | `update` (`action`) |
126
+
127
+ The structured `batch` / `preview` echo parameters of the old apply tools are gone. Apply calls use only the id.
128
+
129
+ ## Logwork Line Format
130
+
131
+ `log_work` and `log_jira_work` take the same text:
132
+
133
+ ```text
134
+ Monday, 01 Jun 2026
135
+ +2 Feature Development | Implement maintenance mode management and status UI (SCB-213)
136
+ +1 Code Review & Refactoring | Review system page PR (SCB-227)
137
+ Tuesday, 02 Jun 2026
138
+ +2.5 Bug Fix | Password reset validation and UI improvements (SCB-228)
139
+ ```
140
+
141
+ - **Day heading:** `<Weekday>, <DD> <Mon> <YYYY>`.
142
+ - **Entry:** `+<hours> <worklog task> | <description>`.
143
+ - `<worklog task>` is a Resource Optimiser worklog task name from `list_tasks` (case-insensitive exact match), or `#<id>`.
144
+ - `<description>` is free text. Resource Optimiser stores it as the entry `description`, and Jira uses it as the worklog comment. Only the first `|` splits the line; any later `|` stays in the description.
145
+ - **Lines without `|`** are still accepted:
146
+ - If the whole text is a catalog task name, the entry is written without a description and the preview shows a warning.
147
+ - Otherwise, pass `defaultWorklogTask` (for example `"Other Activities"`) to the `log_work` preview call, and the text becomes the description. Without it, the entry is unresolved with reason `worklog_task_required` and the preview lists `availableTaskNames`.
148
+ - **Jira lines** need exactly one issue key, such as `SCB-213`.
149
+
150
+ If a tool says auth is required, the assistant calls `login`, or you can run `logwork login` yourself. For Jira, the assistant calls `login` with `target: "jira"`, or you run `logwork login jira`.
151
+
152
+ The Jira prompt accepts a Personal Access Token in the terminal only. Neither login accepts credentials through MCP.
153
+
154
+ Do not paste passwords, 2FA codes, Bearer tokens, cookies, or raw auth logs into AI chat.
155
+
156
+ ## Verify Setup
157
+
158
+ Use these prompts after your IDE sees the `logwork` server:
159
+
160
+ ```text
161
+ Check my logwork for this week.
162
+ ```
163
+
164
+ ```text
165
+ Which Resource Optimiser project should this repo log work to?
166
+ ```
167
+
168
+ ```text
169
+ Log 2 hours today for SCB-213 "maintenance mode UI" as Feature Development, with a clear description. Preview first and wait for my approval.
170
+ ```
171
+
172
+ ```text
173
+ Set up the SCB ticket mapping to project 2621A-SIT-HTML BUILDER-PRJ.
174
+ ```
175
+
176
+ **Prompt `log_from_commits`** (arguments: `period`, `repos`) returns the full procedure for logging from commits. It is the same text as the agent skill that `logwork skill install` installs for Claude Code and Codex.
177
+
178
+ ## Common MCP Workflows
179
+
180
+ ### Log Work From Your Commits
181
+
182
+ Ask your assistant (or pick the `log_from_commits` prompt in clients that list MCP prompts):
183
+
184
+ ```text
185
+ Log my work for yesterday from my commits in this workspace.
186
+ ```
187
+
188
+ Expected flow:
189
+
190
+ 1. `get_logwork` shows which days and projects still have booked hours to fill.
191
+ 2. `draft_from_commits` (with `repos` set to the workspace folders) groups your commits into draft lines with hours.
192
+ 3. `list_tasks` confirms the worklog task names for each project.
193
+ 4. The assistant rewrites vague descriptions, shows you the block and waits for approval.
194
+ 5. `log_work(text)` previews the entries, then `log_work(batchId, confirm: true)` applies them.
195
+
196
+
197
+
198
+ ### Query Logwork
199
+
200
+ Ask your assistant:
201
+
202
+ ```text
203
+ List the days and projects I logged work for this week, including entry details.
204
+ ```
205
+
206
+ The assistant calls `get_logwork`. Supported periods are `today`, `yesterday`, `this_week`, `last_week`, `this_month` and `last_month`. This is read-only.
207
+
208
+ ### Reconcile Resource Optimiser And Jira
209
+
210
+ ```text
211
+ Compare my Resource Optimiser and Jira worklogs for this week. Show daily differences and do not write anything.
212
+ ```
213
+
214
+ The assistant calls `reconcile` with `period: "this_week"`. Custom dates are intentionally not exposed.
215
+
216
+ The result includes:
217
+ - daily booked, RO and Jira totals;
218
+ - issue-level differences;
219
+ - entries that cannot be verified because they do not contain exactly one Jira key;
220
+ - high-confidence correction suggestions.
221
+
222
+ A suggestion is not an approved write. It still goes through the two-call write of `log_work`, `edit_entry` or `log_jira_work`.
223
+
224
+ ### Log Work To Resource Optimiser
225
+
226
+ 1. Optional: call `get_logwork` to see booked and logged hours.
227
+ 2. Call `list_projects`, then `list_tasks` with the `projectId`, to pick valid worklog task names.
228
+ 3. Call `log_work` with `text`.
229
+ 4. Show the summary and ask for approval. The summary shows each line as `project - worklog task | description`, plus an approval checklist with the total hours, date range and per-project breakdown.
230
+ 5. Call `log_work` with `batchId` and `confirm: true`. Add `allowUnbooked: true` only if the user agreed to log to a project that is not booked on that date.
231
+
232
+ The `batchId` changes whenever a description or `defaultWorklogTask` changes, so an approval always covers the exact text that will be written. If an entry is unresolved, re-preview with `projectOverrides` (preview entry id → `projectMemberId`), or save a mapping with `map_project`.
233
+
234
+ Each preview entry contains:
235
+ - `taskName`: the resolved RO worklog task;
236
+ - `worklogTaskId`;
237
+ - `description`;
238
+ - `descriptionMissing`;
239
+ - `taskResolution.reason`.
240
+
241
+ | `taskResolution.reason` / entry `reason` | Meaning | Fix |
242
+ |---|---|---|
243
+ | `worklog_task_match` | Task resolved from the text before `\|` (or the whole line). | None. |
244
+ | `default_worklog_task` | The line had no `\|`, so `defaultWorklogTask` was used and the line became the description. | None. |
245
+ | `worklog_task_required` | The line had no `\|` and is not a catalog task. | Add `<task> \|`, or pass `defaultWorklogTask`. See `availableTaskNames`. |
246
+ | `task_not_found` | The task before `\|` is not in the catalog. | Use a name from `suggestedTaskNames` or `availableTaskNames`. |
247
+ | `task_lookup_failed` | The catalog request failed. | Retry, or check auth. |
248
+
249
+ Apply sends `POST /logwork/entries` with `worklog_task_id`, `effort_hours`, `logdate`, `type_of_work` and `description`, plus `project_id` when the task is a shared default. New entries are approved immediately. `log_work` never writes Jira.
250
+
251
+ ### Edit, Resubmit, Or Delete An Existing RO Entry
252
+
253
+ Get the `logworkId` from `get_logwork` first.
254
+
255
+ ```text
256
+ RO logwork 304032 should be a Bug Fix with description "Fix date picker timezone (SAP-42)". Keep hours, project, and date. Show me the diff and ask before applying.
257
+ ```
258
+
259
+ 1. Call `edit_entry` with `logworkId: 304032`, `taskName: "Bug Fix"` and `description: "Fix date picker timezone (SAP-42)"`.
260
+ 2. Show the original and updated fields. Project and date are shown as unchanged.
261
+ 3. Ask for explicit approval.
262
+ 4. Call `edit_entry` with the returned `previewId` and `confirm: true`.
263
+
264
+ The optional changes are:
265
+ - `hours`
266
+ - `taskName`: a catalog name from `list_tasks`, or `#<id>`.
267
+ - `description`
268
+ - `typeOfWork`
269
+
270
+ Pass at least one. Omitted fields keep their current value. The exception is `typeOfWork`: if it is omitted and the task changes, it follows the new task's catalog type.
271
+
272
+ RO has no staff edit endpoint. `PATCH /logwork/pm/entries/:id/edit-approve` exists but is PM-only. So apply **replaces** the entry:
273
+
274
+ 1. It re-reads the entry and blocks if the entry changed after the preview.
275
+ 2. It creates the corrected entry with `POST /logwork/entries`.
276
+ 3. It deletes the original with `DELETE /logwork/entries/:id`.
277
+ 4. It verifies the new entry.
278
+
279
+ If the create fails, the original is untouched. If the delete fails, the new entry is rolled back.
280
+
281
+ Deleting needs no approval, and the new entry is approved immediately, so the edit takes effect at once. Because the entry id changes, the result returns both `originalLogworkId` and `newLogworkId`.
282
+
283
+ Which tool to use:
284
+
285
+ | Entry | Tool |
286
+ |---|---|
287
+ | Rejected, needs fixing | `resubmit_entry`: change hours, type of work, description, or add a note to the PM. |
288
+ | No longer needed | `delete_entry`: works only on submitted or approved entries. |
289
+ | On a locked day, created by Jira, or owned by another user | Edit is blocked. |
290
+
291
+ `edit_entry` and `delete_entry` carry `destructiveHint: true`.
292
+
293
+ ### Log Work To Jira
294
+
295
+ Jira worklogs are separate from Resource Optimiser logwork. Log in first with `logwork login jira`.
296
+
297
+ Each line must include exactly one Jira issue key. The RO worklog task before `|` is ignored by Jira. A line without `|` uses the whole text as the comment.
298
+
299
+ 1. Call `log_jira_work` with `text`.
300
+ 2. Show the summary and ask for approval. The summary includes the ready, duplicate, missing-ticket, multi-ticket and lookup-failed counts; the issue summary and status; and the final `started` and comment values.
301
+ 3. Call `log_jira_work` with `batchId` and `confirm: true`.
302
+
303
+ Duplicates block apply, and there is no override option. A worklog counts as a duplicate when it has the same short marker (`#lh:<hash>`), or the same issue, date, duration and normalized comment. Legacy `[logwork-helper:...]` markers in older comments are still recognized.
304
+
305
+ Each worklog is written with these values:
306
+
307
+ ```text
308
+ started: 09:00 Asia/Ho_Chi_Minh on the entry date
309
+ timeSpentSeconds: hours * 3600
310
+ comment: description (text after "|", or the whole line) plus short marker, for example #lh:8f3a91c0
311
+ adjustEstimate: leave
312
+ ```
313
+
314
+ Override `started` with `LOGWORK_JIRA_STARTED_TIME=HH:mm` and the timezone with `LOGWORK_TIMEZONE=<IANA timezone>`.
315
+
316
+ ### Both
317
+
318
+ For the same text, run both previews: `log_work` and `log_jira_work`. Show both summaries, then apply each one with its own approval.
319
+
320
+ The two applies are intentionally not atomic. `log_work` never writes Jira, and `log_jira_work` never writes Resource Optimiser.
321
+
322
+ ### History, Updates, Reminder
323
+
324
+ - **History:** `get_history` reads the sanitized local ledger. The `both` filter returns records created by the combined Both flow.
325
+ - **Updates:**
326
+ 1. Call `update` with `action: "check"` (read-only) and show the target version.
327
+ 2. After approval, call `update` with `action: "install"`, `version` and `confirm: true`.
328
+ 3. Reconnect MCP afterwards.
329
+
330
+ Install re-checks npm, accepts only npm latest, and never accepts a registry, package or shell command.
331
+ - **Reminder:**
332
+ - `reminder` with `action: "status"` is read-only.
333
+ - `enable` (with optional `time` and `target`), `disable` and `test` need `confirm: true` after approval.
334
+ - macOS uses launchd and Windows uses Task Scheduler.
335
+ - Reminder tools never accept credentials, scheduler commands or executable paths.
336
+
337
+ ### Project Mapping
338
+
339
+ You do not need to create the mapping file by hand. When a preview cannot resolve a ticket like `SCB-213`, the assistant calls `list_projects`. After you approve, it calls `map_project` with `tickets`, an optional `keywords`, and `confirm: true`.
340
+
341
+ Mappings are stored in one of two places:
342
+ - **User scope (default):** `~/.logwork-cli/.logwork.json`.
343
+ - **Project scope:** `.logwork.json` in the current repo. A repo that still has `.logwork-helper.json` keeps using that file until a `.logwork.json` exists.
344
+
345
+ ```json
346
+ {
347
+ "projectMappings": [
348
+ {
349
+ "projectName": "2621A-SIT-HTML BUILDER-PRJ",
350
+ "projectMemberId": 5234,
351
+ "tickets": ["SCB"],
352
+ "keywords": ["question bank", "programme", "cluster"]
353
+ }
354
+ ]
355
+ }
356
+ ```
357
+
358
+ This file stores matching hints only. It never stores tokens.
359
+
360
+ ### Allow Unbooked Logging
361
+
362
+ Sometimes a line matches one of your project memberships, but that project is not booked for the date. The preview then marks the entry `UNBOOKED`.
363
+
364
+ Only allow this when the matched project is correct. Apply with:
365
+
366
+ ```json
367
+ { "batchId": "<from preview>", "confirm": true, "allowUnbooked": true }
368
+ ```